windows.go (4111B)
1 package main 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "os/user" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/pkg/errors" 16 ) 17 18 // Windows builds Giffer as a .exe for Windows. 19 type Windows struct { 20 // Bin is the name of the built binary. 21 Bin string 22 // Pkg is the go package import string ie, 23 // "github.com/jackmordaunt/giffer/desktop". 24 Pkg string 25 // UI is the root path to the UI files. 26 UI string 27 // FFmpeg is the URL or filepath to a static FFmpeg binary. 28 FFmpeg string 29 // Icon is the path to the icon file (.icns or .png). 30 Icon string 31 // BuildTags to pass to go build. 32 BuildTags string 33 } 34 35 // Build executes the build and writes output to dist. 36 func (exe *Windows) Build(dist string) error { 37 tasks := FanOut([]Task{ 38 { 39 Name: "compile ui", 40 Op: func() error { 41 ui, err := filepath.Abs(exe.UI) 42 if err != nil { 43 return errors.Wrap(err, "resolving absolute path") 44 } 45 exe.UI = filepath.Clean(ui) 46 var ( 47 src = filepath.Join(exe.UI, "src") 48 checksumFile = filepath.Join(exe.UI, "checksum") 49 ) 50 h, err := Hash(src) 51 if err != nil { 52 return errors.Wrap(err, "hashing ui files") 53 } 54 checksum, err := ioutil.ReadFile(checksumFile) 55 if err != nil && !os.IsNotExist(err) { 56 return errors.Wrap(err, "reading checksum file") 57 } 58 if !os.IsNotExist(err) && h == binary.LittleEndian.Uint64(checksum) { 59 return nil 60 } 61 cmd := exec.Command("yarn", "build") 62 cmd.Dir = exe.UI 63 if out, err := cmd.CombinedOutput(); err != nil { 64 return errors.Wrap(err, string(out)) 65 } 66 buf := make([]byte, 64) 67 binary.LittleEndian.PutUint64(buf, h) 68 return ioutil.WriteFile(checksumFile, buf, 0644) 69 }, 70 }, 71 { 72 Name: "compile binary", 73 Op: func() error { 74 ldflags := []string{ 75 // The "X" follows the variable naming convention in webview_windows.go 76 fmt.Sprintf("%s.%sX=%s", exe.Pkg, "ffmpeg", "ffmpeg.exe"), 77 fmt.Sprintf("%s.%sX=%s", exe.Pkg, "logfile", "giffer.log"), 78 fmt.Sprintf("%s.%Xs=%s", exe.Pkg, "tmp", "tmp"), 79 } 80 args := []string{ 81 "go", 82 "build", 83 "-ldflags", 84 fmt.Sprintf("%q", strings.Join(ldflags, "-X ")), 85 } 86 if exe.BuildTags != "" { 87 args = append(args, "-tags", exe.BuildTags) 88 } 89 args = append(args, []string{ 90 "-o", 91 filepath.Join(dist, exe.Bin), 92 exe.Pkg, 93 }...) 94 cmd := exec.Command(args[0], args[1:]...) 95 if out, err := cmd.CombinedOutput(); err != nil { 96 return errors.Wrap(err, string(out)) 97 } 98 return nil 99 }, 100 }, 101 { 102 Name: "embed resources", 103 Requires: []string{"compile binary", "compile ui"}, 104 Op: func() error { 105 time.Sleep(time.Millisecond * 300) 106 cmd := exec.Command( 107 "rice", 108 "append", 109 "-i", exe.Pkg, 110 "--exec", filepath.Join(dist, exe.Bin), 111 ) 112 if out, err := cmd.CombinedOutput(); err != nil { 113 return errors.Wrap(err, string(out)) 114 } 115 return nil 116 }, 117 }, 118 { 119 Name: "bundle ffmpeg", 120 Op: func() error { 121 var ( 122 src io.Reader 123 err error 124 ) 125 if exe.FFmpeg == "" { 126 exe.FFmpeg = "https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20181126-90ac0e5-win64-static.zip" 127 } 128 if strings.Contains(exe.FFmpeg, "https://") { 129 u, err := user.Current() 130 if err != nil { 131 return errors.Wrap(err, "determine current user") 132 } 133 downloads := filepath.Join(u.HomeDir, "Downloads") 134 src, err = openZip(exe.FFmpeg, downloads, "bin/ffmpeg") 135 if err != nil { 136 return errors.Wrap(err, "downloading ffmpeg") 137 } 138 } else { 139 srcf, err := os.Open(exe.FFmpeg) 140 if err != nil { 141 return errors.Wrap(err, "opening ffmpeg file") 142 } 143 defer srcf.Close() 144 src = srcf 145 } 146 destf, err := os.OpenFile( 147 filepath.Join(dist, "ffmpeg.exe"), 148 os.O_CREATE|os.O_RDWR, 149 0777) 150 if err != nil { 151 return errors.Wrap(err, "opening desintaiton file") 152 } 153 defer destf.Close() 154 if _, err := io.Copy(destf, src); err != nil { 155 return errors.Wrap(err, "copying") 156 } 157 return nil 158 }, 159 }, 160 }) 161 return tasks.Run() 162 }