giffer

Create .gif images from sites like youtube.com
Log | Files | Refs | LICENSE

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 }