github.com/256dpi/max-go@v0.7.0/cmd/maxgo/main.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "runtime" 11 12 "github.com/otiai10/copy" 13 ) 14 15 // On macOS, we need to make sure that we always write external binaries to a 16 // new file. Otherwise, kernel-side code-signing caches become outdated and Max 17 // crashes with a SIGKILL when loading the modified external. 18 // https://developer.apple.com/documentation/security/updating_mac_software 19 20 var name = flag.String("name", "", "the name of the external") 21 var out = flag.String("out", "out", "the output directory") 22 var cross = flag.Bool("cross", false, "cross compile for Windows on macOS") 23 var install = flag.String("install", "", "install into specified package") 24 25 func main() { 26 // parse flags 27 flag.Parse() 28 29 // log 30 fmt.Println("==> checking system...") 31 32 // check go 33 _, err := exec.LookPath("go") 34 if err != nil { 35 panic("missing go command (you may need to install Go)") 36 } 37 38 // check cross compile 39 if *cross { 40 // check OS 41 if runtime.GOOS != "darwin" { 42 panic("cannot cross compile for macOS from Windows") 43 } 44 45 // check zig 46 _, err := exec.LookPath("zig") 47 if err != nil { 48 panic("missing zig command (you may need to install zig)") 49 } 50 } 51 52 // log 53 fmt.Println("==> preparing build...") 54 55 // check name 56 if *name == "" { 57 panic("missing external name") 58 } 59 60 // get out dir 61 var outDir = filepath.Join(".", "out") 62 if *out != "" { 63 outDir, err = filepath.Abs(*out) 64 check(err) 65 } 66 67 // print 68 fmt.Printf("name: %s\n", *name) 69 fmt.Printf("out: %s\n", outDir) 70 71 // clear directory (see top notes) 72 check(os.RemoveAll(outDir)) 73 check(os.MkdirAll(outDir, os.ModePerm)) 74 75 // build 76 switch runtime.GOOS { 77 case "darwin": 78 buildDarwin(outDir) 79 if *cross { 80 crossBuildWindows(outDir) 81 } 82 case "windows": 83 buildWindows(outDir) 84 } 85 86 // install 87 if *install != "" { 88 // log 89 fmt.Println("==> installing external...") 90 91 // get home dir 92 user, err := os.UserHomeDir() 93 check(err) 94 95 // prepare path 96 dir, err := filepath.Abs(filepath.Join(user, "Documents", "Max 8", "Packages", *install, "externals")) 97 check(err) 98 99 // log 100 fmt.Printf("target: %s\n", dir) 101 102 // create path 103 check(os.MkdirAll(dir, os.ModePerm)) 104 105 // copy external (see top notes) 106 switch runtime.GOOS { 107 case "darwin": 108 check(os.RemoveAll(filepath.Join(dir, *name+".mxo"))) 109 check(copy.Copy(filepath.Join(outDir, *name+".mxo"), filepath.Join(dir, *name+".mxo"))) 110 case "windows": 111 check(os.RemoveAll(filepath.Join(dir, *name+".mxe64"))) 112 check(copy.Copy(filepath.Join(outDir, *name+".mxe64"), filepath.Join(dir, *name+".mxe64"))) 113 } 114 } 115 116 // log 117 fmt.Println("==> done!") 118 } 119 120 func buildDarwin(outDir string) { 121 // log 122 fmt.Println("==> building...") 123 124 // prepare bin file 125 bin := filepath.Join("out", *name) 126 127 // build arm64 and amd64 128 run("go", 129 []string{"build", "-v", "-buildmode=c-shared", "-o", bin + "-arm64"}, 130 []string{"CGO_ENABLED=1", "GOARCH=arm64", "CGO_LDFLAGS=-Wl,-no_fixup_chains"}, 131 ) 132 run("go", 133 []string{"build", "-v", "-buildmode=c-shared", "-o", bin + "-amd64"}, 134 []string{"CGO_ENABLED=1", "GOARCH=amd64", "CGO_LDFLAGS=-Wl,-no_fixup_chains"}, 135 ) 136 137 // assemble universal binary 138 run("lipo", 139 []string{"-create", "-output", bin, bin + "-amd64", bin + "-arm64"}, 140 nil, 141 ) 142 143 // ensure directory 144 check(os.MkdirAll(filepath.Join(outDir, *name+".mxo", "Contents", "MacOS"), os.ModePerm)) 145 146 // copy binary 147 check(os.Rename(bin, filepath.Join(outDir, *name+".mxo", "Contents", "MacOS", *name))) 148 149 // write info plist 150 check(ioutil.WriteFile(filepath.Join(outDir, *name+".mxo", "Contents", "Info.plist"), []byte(infoPlist(*name)), os.ModePerm)) 151 152 // write package info 153 check(ioutil.WriteFile(filepath.Join(outDir, *name+".mxo", "Contents", "PkgInfo"), []byte(pkgInfo), os.ModePerm)) 154 } 155 156 func buildWindows(outDir string) { 157 // log 158 fmt.Println("==> building...") 159 160 // build 161 run("go", 162 []string{"build", "-v", "-buildmode=c-shared", "-o", filepath.Join(outDir, *name+".mxe64")}, 163 []string{"CGO_ENABLED=1"}, 164 ) 165 } 166 167 func crossBuildWindows(outDir string) { 168 // log 169 fmt.Println("==> cross building...") 170 171 // build 172 run("go", 173 []string{"build", "-v", "-buildmode=c-shared", "-o", filepath.Join(outDir, *name+".mxe64")}, 174 []string{`CC=zig cc -target x86_64-windows-gnu`, "GOOS=windows", "GOARCH=amd64", "CGO_ENABLED=1"}, 175 ) 176 } 177 178 func run(bin string, args []string, env []string) { 179 // construct 180 cmd := exec.Command(bin, args...) 181 cmd.Stdout = os.Stdout 182 cmd.Stderr = os.Stdout 183 cmd.Env = append(env, os.Environ()...) 184 185 // run 186 err := cmd.Run() 187 if err != nil { 188 panic("command failed") 189 } 190 } 191 192 func check(err error) { 193 if err != nil { 194 panic(err) 195 } 196 }