11package packaging
22
33import (
4+ "crypto/rand"
5+ "crypto/sha1"
6+ "encoding/hex"
47 "fmt"
8+ "image/png"
59 "io/ioutil"
610 "os"
711 "os/exec"
812 "path/filepath"
13+ "runtime"
914 "strings"
1015
16+ ico "github.com/Kodeworks/golang-image-ico"
17+
1118 "github.com/go-flutter-desktop/hover/internal/log"
1219)
1320
@@ -24,26 +31,101 @@ var WindowsMsiTask = &packagingTask{
2431 flutterBuildOutputDirectory : "build" ,
2532 packagingFunction : func (tmpPath , applicationName , strippedApplicationName , packageName , executableName , version , release string ) (string , error ) {
2633 outputFileName := fmt .Sprintf ("%s %s.msi" , applicationName , version )
27- cmdConvert := exec .Command ("convert" , "-resize" , "x16" , "build/assets/icon.png" , "build/assets/icon.ico" )
28- cmdConvert .Dir = tmpPath
29- cmdConvert .Stdout = os .Stdout
30- cmdConvert .Stderr = os .Stderr
31- err := cmdConvert .Run ()
34+ iconPngFile , err := os .Open (filepath .Join (tmpPath , "build" , "assets" , "icon.png" ))
3235 if err != nil {
3336 return "" , err
3437 }
35- cmdWixl := exec .Command ("wixl" , "-v" , fmt .Sprintf ("%s.wxs" , packageName ), "-o" , outputFileName )
36- cmdWixl .Dir = tmpPath
37- cmdWixl .Stdout = os .Stdout
38- cmdWixl .Stderr = os .Stderr
39- err = cmdWixl .Run ()
38+ pngImage , err := png .Decode (iconPngFile )
4039 if err != nil {
4140 return "" , err
4241 }
42+ // We can't defer it, because windows reports that the file is used by another program
43+ err = iconPngFile .Close ()
44+ if err != nil {
45+ return "" , err
46+ }
47+ iconIcoFile , err := os .Create (filepath .Join (tmpPath , "build" , "assets" , "icon.ico" ))
48+ if err != nil {
49+ return "" , err
50+ }
51+ err = ico .Encode (iconIcoFile , pngImage )
52+ if err != nil {
53+ return "" , err
54+ }
55+ // We can't defer it, because windows reports that the file is used by another program
56+ err = iconIcoFile .Close ()
57+ if err != nil {
58+ return "" , err
59+ }
60+ switch runtime .GOOS {
61+ case "windows" :
62+ cmdCandle := exec .Command ("candle" , fmt .Sprintf ("%s.wxs" , packageName ))
63+ cmdCandle .Dir = tmpPath
64+ cmdCandle .Stdout = os .Stdout
65+ cmdCandle .Stderr = os .Stderr
66+ err = cmdCandle .Run ()
67+ if err != nil {
68+ return "" , err
69+ }
70+ cmdLight := exec .Command ("light" , fmt .Sprintf ("%s.wixobj" , packageName ), "-sval" )
71+ cmdLight .Dir = tmpPath
72+ cmdLight .Stdout = os .Stdout
73+ cmdLight .Stderr = os .Stderr
74+ err = cmdLight .Run ()
75+ if err != nil {
76+ return "" , err
77+ }
78+ err = os .Rename (filepath .Join (tmpPath , fmt .Sprintf ("%s.msi" , packageName )), filepath .Join (tmpPath , outputFileName ))
79+ if err != nil {
80+ return "" , err
81+ }
82+ case "linux" :
83+ cmdWixl := exec .Command ("wixl" , "-v" , fmt .Sprintf ("%s.wxs" , packageName ), "-o" , outputFileName )
84+ cmdWixl .Dir = tmpPath
85+ cmdWixl .Stdout = os .Stdout
86+ cmdWixl .Stderr = os .Stderr
87+ err = cmdWixl .Run ()
88+ if err != nil {
89+ return "" , err
90+ }
91+ default :
92+ panic ("should be unreachable" )
93+ }
4394 return outputFileName , nil
4495 },
4596 requiredTools : map [string ][]string {
46- "linux" : {"convert" , "wixl" },
97+ "windows" : {"candle" , "light" },
98+ "linux" : {"wixl" },
99+ },
100+ generateInitFiles : func (packageName , path string ) {
101+ b := make ([]byte , 16 )
102+ _ , err := rand .Read (b )
103+ if err != nil {
104+ log .Errorf ("Failed to generate GUID: %v" , err )
105+ os .Exit (1 )
106+ }
107+ upgradeCode := strings .ToUpper (fmt .Sprintf ("%x-%x-%x-%x-%x" , b [0 :4 ], b [4 :6 ], b [6 :8 ], b [8 :10 ], b [10 :]))
108+ err = ioutil .WriteFile (filepath .Join (path , "upgrade-code.txt" ), []byte (fmt .Sprintf ("%s\n # This GUID is your upgrade code and ensures that you can properly update your app.\n # Don't change it." , upgradeCode )), 0755 )
109+ if err != nil {
110+ log .Errorf ("Failed to create `upgrade-code.txt` file: %v" , err )
111+ os .Exit (1 )
112+ }
113+ },
114+ extraTemplateData : func (packageName , path string ) map [string ]string {
115+ data , err := ioutil .ReadFile (filepath .Join (path , "upgrade-code.txt" ))
116+ if err != nil {
117+ log .Errorf ("Failed to read `go/packaging/windows-msi/upgrade-code.txt`: %v" , err )
118+ if os .IsNotExist (err ) {
119+ log .Errorf ("Please re-init windows-msi to generate the `go/packaging/windows-msi/upgrade-code.txt`" )
120+ log .Errorf ("or put a GUID from https://www.guidgen.com/ into a new `go/packaging/windows-msi/upgrade-code.txt` file." )
121+ }
122+ os .Exit (1 )
123+ }
124+ guid := strings .Split (string (data ), "\n " )[0 ]
125+ return map [string ]string {
126+ "upgradeCode" : guid ,
127+ "pathSeparator" : string (os .PathSeparator ),
128+ }
47129 },
48130 generateBuildFiles : func (packageName , tmpPath string ) {
49131 directoriesFilePath , err := filepath .Abs (filepath .Join (tmpPath , "directories.wxi" ))
@@ -76,13 +158,13 @@ var WindowsMsiTask = &packagingTask{
76158 log .Errorf ("Failed to create component_refs.wxi file %s: %v" , packageName , err )
77159 os .Exit (1 )
78160 }
79- directoriesFileContent = append (directoriesFileContent , ` <Include>` )
80- directoryRefsFileContent = append (directoryRefsFileContent , ` <Include>` )
81- componentRefsFileContent = append (componentRefsFileContent , ` <Include>` )
161+ directoriesFileContent = append (directoriesFileContent , " <Include>" )
162+ directoryRefsFileContent = append (directoryRefsFileContent , " <Include>" )
163+ componentRefsFileContent = append (componentRefsFileContent , " <Include>" )
82164 windowsMsiProcessFiles (filepath .Join (tmpPath , "build" , "flutter_assets" ))
83- directoriesFileContent = append (directoriesFileContent , ` </Include>` )
84- directoryRefsFileContent = append (directoryRefsFileContent , ` </Include>` )
85- componentRefsFileContent = append (componentRefsFileContent , ` </Include>` )
165+ directoriesFileContent = append (directoriesFileContent , " </Include>" )
166+ directoryRefsFileContent = append (directoryRefsFileContent , " </Include>" )
167+ componentRefsFileContent = append (componentRefsFileContent , " </Include>" )
86168
87169 for _ , line := range directoriesFileContent {
88170 if _ , err := directoriesFile .WriteString (line + "\n " ); err != nil {
@@ -121,6 +203,7 @@ var WindowsMsiTask = &packagingTask{
121203}
122204
123205func windowsMsiProcessFiles (path string ) {
206+ pathSeparator := string (os .PathSeparator )
124207 files , err := ioutil .ReadDir (path )
125208 if err != nil {
126209 log .Errorf ("Failed to read directory %s: %v" , path , err )
@@ -129,34 +212,43 @@ func windowsMsiProcessFiles(path string) {
129212
130213 for _ , f := range files {
131214 p := filepath .Join (path , f .Name ())
132- relativePath := strings .Split (strings .Split (p , "flutter_assets" + string (filepath .Separator ))[1 ], string (filepath .Separator ))
215+ relativePath := strings .Split (strings .Split (p , "flutter_assets" + pathSeparator )[1 ], pathSeparator )
216+ id := hashSha1 (strings .Join (relativePath , "" ))
133217 if f .IsDir () {
134218 directoriesFileContent = append (directoriesFileContent ,
135- `<Directory Id="FLUTTERASSETSDIRECTORY_` + strings . Join ( relativePath , "_" ) + `" Name="` + f .Name ()+ `">` ,
219+ fmt . Sprintf ( `<Directory Id="FLUTTERASSETSDIRECTORY_%s" Name="%s">` , id , f .Name ()) ,
136220 )
137221 windowsMsiProcessFiles (p )
138222 directoriesFileContent = append (directoriesFileContent ,
139- ` </Directory>` ,
223+ " </Directory>" ,
140224 )
141225 } else {
142226 if len (relativePath ) > 1 {
143227 directoryRefsFileContent = append (directoryRefsFileContent ,
144- `<DirectoryRef Id="FLUTTERASSETSDIRECTORY_` + strings .Join (relativePath [:len (relativePath )- 1 ], "_" ) + `">` ,
228+ fmt . Sprintf ( `<DirectoryRef Id="FLUTTERASSETSDIRECTORY_%s">` , hashSha1 ( strings .Join (relativePath [:len (relativePath )- 1 ], "" ))) ,
145229 )
146230 } else {
147231 directoryRefsFileContent = append (directoryRefsFileContent ,
148232 `<DirectoryRef Id="FLUTTERASSETSDIRECTORY">` ,
149233 )
150234 }
235+ fileSource := filepath .Join ("build" , "flutter_assets" , strings .Join (relativePath , pathSeparator ))
151236 directoryRefsFileContent = append (directoryRefsFileContent ,
152- `<Component Id="flutter_assets_` + strings . Join ( relativePath , "_" ) + `" Guid="*">` ,
153- `<File Id="flutter_assets_` + strings . Join ( relativePath , "_" ) + `" Source="build/flutter_assets/` + strings . Join ( relativePath , "/" ) + `" KeyPath="yes"/>` ,
154- ` </Component>` ,
155- ` </DirectoryRef>` ,
237+ fmt . Sprintf ( `<Component Id="flutter_assets_%s" Guid="*">` , id ) ,
238+ fmt . Sprintf ( `<File Id="flutter_assets_%s" Source="%s" KeyPath="yes"/>` , id , fileSource ) ,
239+ " </Component>" ,
240+ " </DirectoryRef>" ,
156241 )
157242 componentRefsFileContent = append (componentRefsFileContent ,
158- `<ComponentRef Id="flutter_assets_` + strings . Join ( relativePath , "_" ) + `" />` ,
243+ fmt . Sprintf ( `<ComponentRef Id="flutter_assets_%s" />` , id ) ,
159244 )
160245 }
161246 }
162247}
248+
249+ func hashSha1 (content string ) string {
250+ h := sha1 .New ()
251+ h .Write ([]byte (content ))
252+ sha := h .Sum (nil )
253+ return hex .EncodeToString (sha )
254+ }
0 commit comments