Browse Source

Generate manifest and resources for win32 GUI apps.

woollybah 7 years ago
parent
commit
f6753d0f3c
8 changed files with 255 additions and 67 deletions
  1. 4 0
      CHANGELOG
  2. 79 1
      bmk_config.bmx
  3. 19 1
      bmk_make.bmx
  4. 2 1
      bmk_modutil.bmx
  5. 13 0
      bmk_ng.bmx
  6. 18 0
      bmk_ng_utils.bmx
  7. 1 64
      bmk_util.bmx
  8. 119 0
      make.bmk

+ 4 - 0
CHANGELOG

@@ -1,3 +1,7 @@
+## [3.23] - 2018-03-31
+### Added
+ - Generate manifest and resources for win32 GUI apps.
+ 
 ## [3.22] - 2018-03-21
 ### Changed
  - Further build tree enhancements to significantly improve partial build times.

+ 79 - 1
bmk_config.bmx

@@ -6,10 +6,11 @@ Import BRL.StandardIO
 ?macos
 Import Pub.MacOS
 ?
+Import brl.map
 
 Import "stringbuffer_core.bmx"
 
-Const BMK_VERSION:String = "3.22"
+Const BMK_VERSION:String = "3.23"
 
 Const ALL_SRC_EXTS$="bmx;i;c;m;h;cpp;cxx;mm;hpp;hxx;s;cc;asm;S"
 
@@ -53,6 +54,7 @@ Global opt_musl=False
 Global opt_musl_set=False
 Global opt_static=False
 Global opt_static_set=False
+Global opt_manifest:Int = True
 
 Global opt_dumpbuild
 
@@ -228,6 +230,8 @@ Function ParseConfigArgs$[]( args$[], legacyMax:Int = False )
 		Case "static"
 			opt_static = True
 			opt_static_set = True
+		Case "nomanifest"
+			opt_manifest = False
 		Default
 			CmdError "Invalid option '" + arg[1..] + "'"
 		End Select
@@ -353,6 +357,11 @@ Function Usage:String(fullUsage:Int = False)
 		s:+ "~t-musl~n"
 		s:+ "~t~tEnable musl libc compatibility. (Linux NG only)"
 		s:+ "~n~n"
+		s:+ "~t-nomanifest~n"
+		s:+ "~t~tDon't add an automatically generated manifest and resources to a Win32 application. (Win32 only)~n"
+		s:+ "~t~tName .ico file as <app name>.ico to be included in the the resource.~n"
+		s:+ "~t~tConfigurable application details are placed in the file <app name>.settings"
+		s:+ "~n~n"
 		s:+ "~t-nostrictupgrade~n"
 		s:+ "~t~tDon't upgrade strict method void return types, if required. (NG only)~n"
 		s:+ "~t~tIf a Strict sub type overrides the method of a SuperStrict type and the return type is void,~n"
@@ -590,3 +599,72 @@ Function ValidatePlatform(platform:String)
 			CmdError "Not valid platform : '" + platform + "'"
 	End Select
 End Function
+
+Function ParseApplicationIniFile:TMap()
+	Local appId:String = StripDir(StripExt(opt_outfile))
+	Local buildDir:String = ExtractDir(opt_outfile)
+
+	Local path:String = buildDir + "/" + appId + ".settings"
+
+	Local settings:TMap = New TMap
+	
+	If Not FileType(path) Then
+		Print "Not Found : application settings file '" + appId + ".settings'. Using defaults..."
+		Return DefaultApplicationSettings()
+	End If
+
+	Local file:TStream = ReadFile(path)
+	If Not file
+		Return Null
+	EndIf
+
+	Local line:String
+	Local pos:Int
+	While Not Eof(file)
+		line = ReadLine(file).Trim()
+
+		If line.Find("#") = 0 Then
+			Continue
+		End If
+
+		pos = line.Find("=")
+
+		If pos = -1 Then
+			Continue
+		End If
+
+		settings.Insert(line[..pos], line[pos+1..])
+	Wend
+
+	file.Close()
+
+	Local id:String = StripDir(StripExt(opt_outfile))
+	If opt_debug And opt_outfile.EndsWith(".debug") Then
+		id :+ ".debug"
+	End If
+	settings.Insert("app.id", id)
+	
+	Return settings
+End Function
+
+Function DefaultApplicationSettings:TMap()
+	Local settings:TMap = New TMap
+	settings.Insert("app.package", "com.blitzmax.app")
+	settings.Insert("app.version.code", "1")
+	settings.Insert("app.version.name", "1.0")
+	settings.Insert("app.name", "BlitzMax Application")
+	settings.Insert("app.orientation", "landscape")
+	settings.Insert("app.comments", "landscape")
+	settings.Insert("app.company", "My company")
+	settings.Insert("app.description", "App description")
+	settings.Insert("app.copyright", "Copyright")
+	settings.Insert("app.trademarks", "All rights reserved")
+
+	Local appId:String = StripDir(StripExt(opt_outfile))
+	If opt_debug And opt_outfile.EndsWith(".debug") Then
+		appId :+ ".debug"
+	End If
+	settings.Insert("app.id", appId)
+	Return settings
+End Function
+

+ 19 - 1
bmk_make.bmx

@@ -503,11 +503,29 @@ Type TBuildManager Extends TCallback
 								If Not opt_outfile Then
 									Throw "Build Error: Did not expect to link against " + m.path
 								End If
-							
+
 								' an app!
 								Local max_lnk_time:Int = m.MaxLinkTime()
+								
+								' include settings and icon times in calculation
+								If opt_manifest And processor.Platform() = "win32" And opt_apptype="gui" Then
+									Local settings:String = ExtractDir(opt_outfile) + "/" + StripDir(StripExt(opt_outfile)) + ".settings"
+									max_lnk_time = Max(FileTime(settings), max_lnk_time)
+									max_lnk_time = Max(FileTime(StripDir(StripExt(opt_outfile)) + ".ico"), max_lnk_time)
+								End If
 							
 								If max_lnk_time > FileTime(opt_outfile) Or opt_all Then
+
+									' generate manifest for app
+									If opt_manifest And processor.Platform() = "win32" And opt_apptype="gui" Then
+										processor.RunCommand("make_win32_resource", Null)
+										Local s:TSourceFile = New TSourceFile
+										s.obj_path = BlitzMaxPath() + "/tmp/" + StripDir(StripExt(opt_outfile)) + "." + processor.CPU() + ".res.o"
+										s.stage = STAGE_LINK
+										s.exti = SOURCE_RES
+										m.depslist.AddLast(s)
+									End If
+
 									If Not opt_quiet Then
 										Print ShowPct(m.pct) + "Linking:" + StripDir(opt_outfile)
 									End If

+ 2 - 1
bmk_modutil.bmx

@@ -12,6 +12,7 @@ Const SOURCE_IFACE:Int = $02
 Const SOURCE_C:Int = $04
 Const SOURCE_HEADER:Int = $08
 Const SOURCE_ASM:Int = $10
+Const SOURCE_RES:Int = $20
 'Const SOURCE_PYTHON:Int = $20
 'Const SOURCE_PERL:Int = $40
 'Const SOURCE_RUBY:Int = $80
@@ -238,7 +239,7 @@ Type TSourceFile
 						End If
 					End If
 
-					If s.exti = SOURCE_BMX Or s.exti = SOURCE_IFACE Or s.modid Then
+					If s.exti = SOURCE_BMX Or s.exti = SOURCE_IFACE Or s.modid Or s.exti = SOURCE_RES Then
 						s.GetLinks(list, opts, modsOnly, linksCache, optsCache)
 					End If
 	

+ 13 - 0
bmk_ng.bmx

@@ -49,6 +49,7 @@ Type TBMK
 	Field buildLog:TList
 	
 	Field callback:TCallback
+	Field _appSettings:TMap
 
 	Method New()
 		LuaRegisterObject Self,"bmk"
@@ -780,6 +781,18 @@ Type TBMK
 		End If
 	End Method
 	
+	Method VerboseBuild:Int()
+		Return opt_verbose
+	End Method
+	
+	Method AppSetting:String(key:String)
+		If Not _appSettings Then
+			_appSettings = ParseApplicationIniFile()
+		End If
+		
+		Return String(_appSettings.ValueForKey(key))
+	End Method
+	
 End Type
 
 ?win32

+ 18 - 0
bmk_ng_utils.bmx

@@ -6,6 +6,7 @@ Import BRL.FileSystem
 Import BRL.System
 ?
 Import BRL.MaxLua
+Import BRL.TextStream
 
 ?linux
 Import "bmk_cores_linux.bmx"
@@ -17,6 +18,7 @@ Import "bmk_cores_win32.bmx"
 
 Global utils:TMaxUtils = New TMaxUtils
 Global fsys:TSystem = New TSystem
+Global futils:TFileUtils = New TFileUtils
 
 ' Access to BRL.MaxUtil
 Type TMaxUtils
@@ -170,3 +172,19 @@ Type TSystem
 ?
 End Type
 
+' Access to BRL.MaxUtil
+Type TFileUtils
+
+	Method New()
+		LuaRegisterObject Self,"futils"
+	End Method
+
+	Method SaveText:Int(filename:String, text:String)
+		Try
+			Return BRL.TextStream.SaveText(text, filename)
+		Catch e:TStreamWriteException
+			Return False
+		End Try
+	End Method
+	
+End Type

+ 1 - 64
bmk_util.bmx

@@ -685,7 +685,7 @@ Function DeployAndroidProject()
 		End If
 	End If
 	
-	Local projectSettings:TMap = ParseAndroidIniFile()
+	Local projectSettings:TMap = ParseApplicationIniFile()
 	
 	Local appPackage:String = String(projectSettings.ValueForKey("app.package"))
 	
@@ -799,69 +799,6 @@ Function CopyAndroidResources(buildDir:String, assetsDir:String)
 
 End Function
 
-Function ParseAndroidIniFile:TMap()
-	Local appId:String = StripDir(StripExt(opt_outfile))
-	Local buildDir:String = ExtractDir(opt_outfile)
-
-	Local path:String = ExtractDir(opt_outfile) + "/" + appId + ".android"
-
-	Local settings:TMap = New TMap
-	
-	If Not FileType(path) Then
-		Print "Not Found : application settings file '" + appId + ".android'. Using defaults..."
-		Return DefaultAndroidSettings()
-	End If
-
-	Local file:TStream = ReadFile(path)
-	If Not file
-		Return Null
-	EndIf
-
-	Local line:String
-	Local pos:Int
-	While Not Eof(file)
-		line = ReadLine(file).Trim()
-
-		If line.Find("#") = 0 Then
-			Continue
-		End If
-
-		pos = line.Find("=")
-
-		If pos = -1 Then
-			Continue
-		End If
-
-		settings.Insert(line[..pos], line[pos+1..])
-	Wend
-
-	file.Close()
-
-	Local id:String = StripDir(StripExt(opt_outfile))
-	If opt_debug And opt_outfile.EndsWith(".debug") Then
-		id :+ ".debug"
-	End If
-	settings.Insert("app.id", id)
-	
-	Return settings
-End Function
-
-Function DefaultAndroidSettings:TMap()
-	Local settings:TMap = New TMap
-	settings.Insert("app.package", "com.blitzmax.android")
-	settings.Insert("app.version.code", "1")
-	settings.Insert("app.version.name", "1.0")
-	settings.Insert("app.name", "BlitzMax Application")
-	settings.Insert("app.orientation", "landscape")
-
-	Local appId:String = StripDir(StripExt(opt_outfile))
-	If opt_debug And opt_outfile.EndsWith(".debug") Then
-		appId :+ ".debug"
-	End If
-	settings.Insert("app.id", appId)
-	Return settings
-End Function
-
 Function GetAndroidSDKTarget:String()
 	Local sdkPath:String = processor.Option("android.sdk", "") + "/platforms"
 	Local target:String = processor.Option("android.sdk.target", "")

+ 119 - 0
make.bmk

@@ -491,3 +491,122 @@
 	
 	return 0
 @end
+
+@define make_win32_resource
+
+	local windres = bmk.MinGWBinPath() .. "/windres.exe"
+	
+	if sys.FileType(windres) == 0 then
+		if bmk.VerboseBuild() then
+			print("windres.exe not found in '" .. bmk.MinGWBinPath() .. "'")
+		end
+		return
+	end
+	
+	windres = bmk.Quote(windres)
+	
+	if bmk.VerboseBuild() == 1 then
+		print("Compiling resources...")
+	end
+
+	local arch = "amd64"
+
+	if bmk.CPU() == "x86" then
+		arch = "x86"
+	end
+	
+	local tmp_path = utils.BlitzMaxPath() .. "/tmp/"
+	
+	local manifest_path = tmp_path .. %outfile% .. "." .. bmk.CPU() .. ".manifest"
+	local resource_path = tmp_path .. %outfile% .. "." .. bmk.CPU() .. ".rc"
+	local object_path = bmk.Quote(tmp_path .. %outfile% .. "." .. bmk.CPU() .. ".res.o")
+	local icon_path = %outfile% .. ".ico"
+	
+	local manifest = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" ..
+			"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">" ..
+			"<asmv3:application xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">" ..
+			"<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">" ..
+			"<dpiAware>true</dpiAware>" ..
+			"</asmv3:windowsSettings>" ..
+			"</asmv3:application>" ..
+			"<assemblyIdentity version=\"1.0.0.0\" processorArchitecture=\"" ..
+			arch ..
+			"\" name=\"Application\" type=\"win32\"/>" ..
+			"<description>" .. %outfile% .. "</description>" ..
+			"<dependency>" ..
+			"<dependentAssembly>" ..
+			"<assemblyIdentity type=\"win32\" name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"" ..
+			arch ..
+			"\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" />" ..
+			"</dependentAssembly>" ..
+			"</dependency>" ..
+			"</assembly>"
+
+	local resource = "1 24 \"" .. manifest_path .. "\"\n" ..
+			"\n"
+	
+	if sys.FileType(icon_path) == 1 then
+		resource = resource .. "101 ICON \"" .. icon_path .. "\"\n" ..
+			"\n"
+	end
+	
+	resource = resource .. "1 VERSIONINFO\n" ..
+			"FILEVERSION 1,0,0,0\n" ..
+			"PRODUCTVERSION 1,0,0,0\n" ..
+			"FILEOS 0x40004\n" ..
+			"FILETYPE 0x1\n" ..
+			"{\n" ..
+			"BLOCK \"StringFileInfo\"\n" .. 
+			"{\n" ..
+			"BLOCK \"040904b0\"\n" .. 
+			"{\n"
+	if bmk.AppSetting("app.comments") ~= "" then
+		resource = resource .. "VALUE \"Comments\", \"" .. bmk.AppSetting("app.comments") .. "\"\n"
+	end
+	
+	if bmk.AppSetting("app.company") ~= "" then
+		resource = resource .. "VALUE \"CompanyName\", \"" .. bmk.AppSetting("app.company") .. "\"\n"
+	end
+	
+	if bmk.AppSetting("app.version.name") ~= "" then
+		resource = resource .. "VALUE \"FileVersion\", \"" .. bmk.AppSetting("app.version.name") .. "\"\n" ..
+			"VALUE \"ProductVersion\", \"" .. bmk.AppSetting("app.version.name") .. "\"\n"
+	end
+	
+	if bmk.AppSetting("app.description") ~= "" then
+		resource = resource .. "VALUE \"FileDescription\", \"" .. bmk.AppSetting("app.description") .. "\"\n"
+	end
+	
+	if bmk.AppSetting("app.name") ~= "" then
+		resource = resource .. "VALUE \"InternalName\", \"" .. bmk.AppSetting("app.name") .. "\"\n" ..
+			"VALUE \"ProductName\", \"" .. bmk.AppSetting("app.name") .. "\"\n"
+	end
+	
+	if bmk.AppSetting("app.copyright") ~= "" then
+		resource = resource .. "VALUE \"LegalCopyright\", \"" .. bmk.AppSetting("app.copyright") .. "\"\n"
+	end
+	
+	if bmk.AppSetting("app.trademarks") ~= "" then
+		resource = resource .. "VALUE \"LegalTrademarks\", \"" .. bmk.AppSetting("app.trademarks") .. "\"\n"
+	end
+	
+	resource = resource .. "VALUE \"OriginalFilename\", \"" .. %outfile% .. ".exe\"\n" ..
+			"}\n" ..
+			"}\n" ..
+			"BLOCK \"VarFileInfo\"\n" ..
+			"{\n" ..
+			"VALUE \"Translation\", 0x0409, 0\n" ..
+			"}\n" ..
+			"}"
+
+	if futils.SaveText(manifest_path, manifest) then
+	
+		if futils.SaveText(resource_path, resource) then
+		
+			local cmd = windres .. " -i " .. bmk.Quote(resource_path) .. " -o " .. object_path
+		
+			bmk.Sys(cmd)
+		end
+	end
+	
+@end