Bläddra i källkod

Initial Import.

woollybah 7 år sedan
förälder
incheckning
eed7defd1a
2 ändrade filer med 497 tillägg och 0 borttagningar
  1. 2 0
      boot_files.txt
  2. 495 0
      bootstrap.bmx

+ 2 - 0
boot_files.txt

@@ -0,0 +1,2 @@
+win32x86	i686-7.2.0-release-posix-sjlj-rt_v5-rev0.7z	https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/7.2.0/threads-posix/sjlj/i686-7.2.0-release-posix-sjlj-rt_v5-rev0.7z	mingw32	MinGW32x86
+win32x64	x86_64-7.2.0-release-posix-seh-rt_v5-rev0.7z	https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/7.2.0/threads-posix/seh/x86_64-7.2.0-release-posix-seh-rt_v5-rev0.7z	mingw64	MinGW32x64

+ 495 - 0
bootstrap.bmx

@@ -0,0 +1,495 @@
+' 
+' Copyright 2018 Bruce A Henderson
+' 
+' Licensed under the Apache License, Version 2.0 (the "License"); you may not
+' use this file except in compliance with the License. You may obtain a copy of
+' the License at
+' 
+'     http://www.apache.org/licenses/LICENSE-2.0
+' 
+' Unless required by applicable law or agreed to in writing, software
+' distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+' WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+' License for the specific language governing permissions and limitations under
+' the License.
+' 
+
+'
+' bootstrap starter
+'
+' 
+'
+SuperStrict
+
+Framework brl.standardio
+Import brl.filesystem
+Import brl.stringbuilder
+Import bah.libarchive
+Import bah.libcurl
+Import bah.format
+
+Const APP_TITLE:String = "BlitzMax bootstrap"
+Const VERSION:String = "1.0.0"
+Const BLOCK_SIZE:Int = 65536
+
+Print APP_TITLE + " v" + VERSION
+Print ""
+
+Local options:TOptions = New TOptions()
+options.ParseArgs(AppArgs[1..])
+
+Local config:TConfig = TConfig.Load(options)
+
+If options.verbose Then
+	Print options.ToString()
+End If
+
+config.ProcessDownloads()
+
+End
+
+Type TOptions
+
+	Field target:String
+	Field config:String
+	Field force:Int
+	Field verbose:Int
+	
+	Field canShowProgress:Int
+
+	Method New()
+		canShowProgress = _isatty(0)
+		config = "boot_files.txt"
+		InitTarget()		
+	End Method
+	
+	Method ParseArgs(args:String[])
+		Local i:Int
+		For i = 0 Until args.length
+			Local arg:String = args[i]
+			If arg[..1] <> "-" Then
+				Exit
+			End If
+			Select arg[1..]
+				Case "t"
+					i:+1
+					CheckArg(i, args.length, "t")
+					target = args[i].ToLower()
+				Case "c"
+					i:+1
+					CheckArg(i, args.length, "c")
+					config = args[i].ToLower()
+				Case "f"
+					force = True
+				Case "v"
+					verbose = True
+				Case "h"
+					ShowUsage()
+					End
+			End Select
+		Next
+	End Method
+	
+	Method ShowUsage()
+		Print "usage: bootstrap [options]"
+		Print " -c <filename> Specific configuration to load"
+		Print " -f            Force install if assets already exist"
+		Print " -h            Help"
+		Print " -t            Set bootstrap target"
+		Print "               Default : " + target
+		Print " -v            Verbose output"
+		Print ""
+	End Method
+
+	Method CheckArg(index:Int, length:Int, option:String)
+		If index = length Then
+			Throw "Missing arg for '-" + option + "'"
+		End If
+	End Method
+	
+	Method ToString:String()
+		Local sb:TStringBuilder = New TStringBuilder()
+		sb.Append("Target = ").Append(target).AppendNewLine()
+		sb.Append("Force  = ").AppendInt(force).AppendNewLine()
+		sb.Append("TTY    = ").AppendInt(canShowProgress).AppendNewLine()
+		Return sb.ToString()
+	End Method
+	
+	Method InitTarget()
+?win32
+		target = "win32"
+?linux
+		target = "linux"
+?macos
+		target = "macos"
+?x86
+		target :+ "x86"
+?x64
+		target :+ "x64"
+?arm
+		target :+ "arm"
+?arm64
+		target :+ "arm64"
+?
+	End Method
+	
+	Method MatchesTarget:Int(otherTarget:String)
+		Return target = otherTarget Or otherTarget = "any" Or target.StartsWith(otherTarget)			
+	End Method
+	
+End Type
+
+Type TConfig
+
+	Field downloads:TList = New TList
+	Field options:TOptions
+	
+	Function Load:TConfig(options:TOptions)
+		Local this:TConfig = New TConfig()
+		this.options = options
+		
+		Local config:String = LoadText(options.config)
+		For Local line:String = EachIn config.Split("~n")
+			line = line.Trim()
+			If line Then
+				this.downloads.AddLast(TDownLoad.Create(line))
+			End If
+		Next
+		
+		Return this
+	End Function
+
+	Method ProcessDownloads()
+		For Local download:TDownload = EachIn downloads
+			If download.Download(options) Then
+				download.Unpack(options)
+			End If
+		Next
+	End Method
+	
+End Type
+
+Type TDownload
+
+	Field target:String
+	Field filename:String
+	Field uri:String
+	Field extractFolder:String
+	Field finalFolder:String
+
+	Function Create:TDownload(line:String)
+		Local download:TDownload = New TDownload
+		Local fields:String[] = line.Split("~t")
+		If fields.length < 3 Then
+			Return Null
+		End If
+		
+		download.target = fields[0]
+		download.filename = fields[1]
+		download.uri = fields[2]
+		
+		If fields.length >= 5 Then
+			download.extractFolder = fields[3]
+			download.finalFolder = fields[4]
+		End If
+		
+		Return download
+	End Function
+
+	Method Download:Int(options:TOptions)
+		Clear()
+		
+		If target And (Not options.MatchesTarget(target)) Then
+			Return False
+		End If
+		
+		If FileType(finalFolder) = FILETYPE_DIR And Not options.force Then
+			Print "Folder '" + finalFolder + "' already exists. Skipping..."
+			Return True
+		End If
+		
+		If FileType(filename) And Not options.force Then
+			Print "File '" + filename + "' already exists. Skipping download..."
+			Return True
+		End If
+		
+		Print "Downloading '" + filename + "'"
+		Local stream:TStream = WriteStream(filename)
+
+		Local curl:TCurlEasy = TCurlEasy.Create()
+		
+		curl.setOptInt(CURLOPT_FOLLOWLOCATION, 1)
+		curl.setOptInt(CURLOPT_SSL_VERIFYPEER, False)
+		
+		Local progress:TProgress
+		
+		If options.canShowProgress Then
+			progress = New TProgress
+			curl.setProgressCallback(ShowProgress, progress)
+		End If
+		
+		curl.setOptString(CURLOPT_URL, uri)
+		
+		' redirect data to stream
+		curl.setWriteStream(stream)
+		
+		Local result:Int = curl.perform()
+
+		If result Then
+			Throw "Error downloading file : " + CurlError(result)
+		End If
+		
+		curl.cleanup()
+		
+		stream.Close()
+		
+		Return True
+	End Method
+	
+	Method Unpack(options:TOptions)
+		Clear()
+
+		If FileType(finalFolder) = FILETYPE_DIR And Not options.force Then
+			Return
+		End If
+		
+		' if the folder exists, then we are here by force. Attempt to delete it:
+		If FileType(finalFolder) = FILETYPE_DIR Then
+			If options.verbose Then
+				Print "Removing folder '" + finalFolder + "'"
+			End If
+			If Not DeleteDir(finalFolder, True)
+				Throw "Cannot delete folder '" + finalFolder + "'"
+			End If
+		End If
+	
+		Local unpacker:TUnPacker = New TUnPacker(Self)
+		unpacker.Unpack(options)
+		
+		If Not FileType(extractFolder) Then
+			Throw "Cannot find unpacked folder '" + extractFolder + "'"
+		End If
+		
+		If FileType(extractFolder) = FILETYPE_DIR Then
+			If options.verbose Then
+				Print "Renaming '" + extractFolder + "'"
+			End If
+			If Not RenameFile(extractFolder, finalFolder) Then
+				Throw "Unable to rename folder '" + extractFolder + "' to '" + finalFolder + "'"
+			End If
+		End If
+		
+	End Method
+
+	Function ShowProgress:Int(data:Object, dltotal:Long, dlnow:Long, ultotal:Long, ulnow:Long)
+		Local progress:TProgress = TProgress(data)
+		progress.ShowProgress(dltotal, dlnow)
+	End Function
+	
+End Type
+
+Type TUnPacker
+
+	Field download:TDownload
+	
+	Field archive:TReadArchive
+	Field entry:TArchiveEntry
+
+	Method New(download:TDownload)
+		Self.download = download
+		
+		entry = New TArchiveEntry.Create()
+	End Method
+
+	Method UnPack(options:TOptions)
+		Clear()
+		
+		Print "Unpacking '" + download.filename + "'"
+
+		Local total:Int = EntryCount()
+		
+		Open()
+		
+		Local progress:TProgress
+		
+		If options.canShowProgress Then
+			progress = New TProgress
+		End If
+		
+		Local count:Int
+		
+		While archive.ReadNextHeader(entry) = ARCHIVE_OK
+			
+			Local path:String = entry.Pathname()
+			Local dir:String = ExtractDir(path)
+			
+			CreateDir(dir, True)
+			
+			If Not path.EndsWith("/") Then			
+				Local stream:TStream = WriteStream(path)
+				
+				CopyStream(archive.DataStream(), stream, BLOCK_SIZE)
+				
+				stream.Close()
+			End If
+			
+			count :+ 1
+
+			If progress Then
+				progress.ShowProgress(total, count)
+			End If
+		Wend
+
+		archive.Free()
+
+		If options.canShowProgress Then
+			Clear()
+		End If
+
+	End Method
+	
+	Method Open()
+		archive = New TReadArchive.Create()
+
+		archive.SupportFilterAll()
+		archive.SupportFormatAll()
+
+		Local result:Int = archive.OpenFilename(download.filename, BLOCK_SIZE)
+		
+		If result <> ARCHIVE_OK Then
+			Throw "Error opening " + download.filename
+		End If
+	End Method
+	
+	Method EntryCount:Int()
+		Local count:Int
+		
+		Open()
+
+		While archive.ReadNextHeader(entry) = ARCHIVE_OK
+			archive.DataSkip()
+			count :+ 1
+		Wend
+
+		archive.Free()
+
+		Return count
+	End Method
+
+End Type
+
+
+Type TProgress
+
+	Const full:String  = "                                                                     "
+	Const space:String = "                                                          "
+	Const bar:String   = "##########################################################"
+	
+	Field length:Int
+	Field progress:Int
+	
+	Field spin:Int
+	Field spinText:String = "\|/-"
+	
+	Field lastUpdate:Int
+	Field startTime:Int
+	
+	Global formatter:TFormatter = TFormatter.Create(" %02d:%02d:%02d")
+	
+	Method New()
+		length = bar.length
+		Clear()
+		Put "~r[" + space + "] --:--:--~r"
+		
+		startTime = MilliSecs()
+	End Method
+
+	Method ShowProgress(dltotal:Long, dlnow:Long)
+		Local sofar:Float = Float(dlnow) / dltotal
+		Local count:Int = length * sofar
+		
+		Local now:Int = MilliSecs()
+		
+		If dltotal <> 0 Then
+
+			Local timeTaken:Int = now - startTime
+			Local timeLeft:Int = 0
+			If dlnow > 0 Then
+				timeLeft = (timeTaken / Double(dlnow)) * (dltotal - dlnow)
+			Else
+				timeLeft = 9999999
+			End If
+
+			If progress <> count Or now > lastUpdate + 1000 Then
+				Put "~r["
+				Local sofar:Float = Float(dlnow) / dltotal
+				
+				Local s:String = bar[..length * sofar]
+				s :+ space[..length - (length * sofar)]
+				s :+ "    "
+				Put s[..length]
+				Put "]  " + FormatTime(timeLeft / 1000) + "~r"
+				
+				progress = count
+				lastUpdate = now
+			End If
+		Else
+			' spinner
+						
+			If now > lastUpdate + 500 Then
+				Put "~r[                          " + spinText[spin..spin+1] + "~r"
+
+				spin :+ 1
+				If spin = spinText.Length Then
+					spin = 0
+				End If
+				
+				lastUpdate = now
+			End If
+
+		End If
+	End Method
+	
+	Method FormatTime:String(time:Int)
+		Local seconds:Int = time Mod 60
+		Local minutes:Int = (time / 60) Mod 60
+		Local hours:Int = time / 3600
+		
+		formatter.Clear()
+		
+		If hours > 99 Then
+			formatter.IntArg(99).IntArg(59).IntArg(59)
+		Else
+			formatter.IntArg(hours).IntArg(minutes).IntArg(seconds)
+		End If
+		
+		Return formatter.format()
+	End Method
+	
+End Type
+
+Function Put( str$="" )
+	StandardIOStream.WriteString str
+	StandardIOStream.Flush
+End Function
+
+Function Clear()
+	Put "~r  " + TProgress.full + "~r"
+End Function
+
+
+Extern
+	Function _isatty:Int(fd:Int)
+End Extern
+
+
+
+
+
+
+
+
+
+
+
+