Skip to content

Commit 18ad6a9

Browse files
committed
Updating build script.
It must now be executed from the build_script directory, but now its dependencies aren't MasterPlan's dependencies, which is good for code separation.
1 parent 183662a commit 18ad6a9

File tree

4 files changed

+624
-588
lines changed

4 files changed

+624
-588
lines changed

build_script/build.go

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
"io/fs"
8+
"os"
9+
"os/exec"
10+
"path/filepath"
11+
"runtime"
12+
"strings"
13+
14+
"github.com/mholt/archives"
15+
"github.com/otiai10/copy"
16+
)
17+
18+
func build(baseDir string, releaseMode string, targetOS, targetArch string) {
19+
20+
fmt.Println(`< Beginning build to "` + baseDir + `" for ` + targetOS + `. >`)
21+
22+
forWin := strings.Contains(targetOS, "windows")
23+
forMac := strings.Contains(targetOS, "darwin")
24+
25+
copyTo := func(src, dest string) {
26+
if err := copy.Copy(src, dest); err != nil {
27+
panic(err)
28+
}
29+
}
30+
31+
// Note that this script is meant to be run from a terminal at the project root.
32+
// It is specifically NOT meant to be built into an executable and run by double-clicking in
33+
// Finder, on Mac OS.
34+
35+
// We always remove any pre-existing platform directory before building to ensure it's fresh.
36+
if err := os.RemoveAll(baseDir); err != nil {
37+
panic(err)
38+
}
39+
40+
copyTo(filepath.Join("..", "changelog.txt"), filepath.Join(baseDir, "changelog.txt"))
41+
42+
if forMac {
43+
baseDir = filepath.Join(baseDir, "MasterPlan.app", "Contents", "MacOS")
44+
}
45+
46+
// Copy the assets folder to the bin directory
47+
48+
copyTo(filepath.Join("..", "assets"), filepath.Join(baseDir, "assets"))
49+
50+
fmt.Println("<Assets copied.>")
51+
52+
outputFilepath := filepath.Join(baseDir, "MasterPlan")
53+
54+
if forWin {
55+
56+
outputFilepath += ".exe"
57+
58+
// Copy the resources.syso so the executable has the generated icon and executable properties compiled in.
59+
// This is done using go generate with goversioninfo downloaded and "// go:generate goversioninfo -64=true" in main.go.
60+
copyTo(filepath.Join("..", "other_sources", "resource.syso"), filepath.Join("..", "resource.syso"))
61+
62+
// Copy in the SDL requirements (.dll files)
63+
filepath.Walk(filepath.Join("other_sources"), func(path string, info fs.FileInfo, err error) error {
64+
_, filename := filepath.Split(path)
65+
if filepath.Ext(path) == ".dll" {
66+
copyTo(path, filepath.Join(baseDir, filename))
67+
}
68+
return nil
69+
})
70+
71+
}
72+
73+
// We should compile statically at some point, but it's broken currently, it seems? See: https://github.com/veandco/go-sdl2/issues/507
74+
// So for the meantime, we'll just build dynamically and bundle the dependencies on Windows and Mac. On Linux, usually users have the dependencies (SDL2, basically) installed already.
75+
76+
// The below string cross-compiles by setting CC to an 64-bit Windows version of MinGW
77+
// CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=` + targetOS + ` GOARCH=amd64 CGO_LDFLAGS="-lSDL2 -lSDL2_gfx" go build -tags ` + releaseMode + ` -ldflags "-s -w -H=windowsgui" -o ` + filename + ` ./`
78+
79+
var c *exec.Cmd
80+
var err error
81+
82+
os.Setenv(`GOOS`, targetOS)
83+
os.Setenv(`GOARCH`, targetArch)
84+
85+
// cross-compile:
86+
if forWin && runtime.GOOS == "linux" {
87+
os.Setenv("CC", "x86_64-w64-mingw32-gcc")
88+
} else {
89+
os.Setenv("CC", "gcc")
90+
}
91+
92+
// Default building for the current OS
93+
if forWin {
94+
// No static flag for Windows because we're redistributing the DLLs for simplicity, and I have yet to figure out where to put the static libraries on Linux to cross-compile successfully.
95+
// When a command is more than a single word and is separated by spaces, it has to be in a single "space" (i.e. "static " + releaseMode)
96+
97+
// Also note that I know it's weird that I'm joining the build script directory here because baseDir is already a folder up; this makes it so
98+
// bin is the running directory
99+
c = exec.Command(`go`, `build`, `-C`, `..`, `-ldflags`, `-s -w -H windowsgui`, `-tags`, releaseMode, `-o`, filepath.Join("build_script", outputFilepath), `.`)
100+
} else {
101+
c = exec.Command(`go`, `build`, `-C`, `..`, `-ldflags`, `-s -w`, `-tags`, releaseMode, `-o`, filepath.Join("build_script", outputFilepath), `.`)
102+
}
103+
104+
fmt.Println("<Building binary with args: ", c.Args, ".>")
105+
106+
text, err := c.CombinedOutput()
107+
108+
if err != nil {
109+
fmt.Println("<ERROR: ", string(err.Error())+">")
110+
fmt.Println(string(text))
111+
}
112+
113+
// Add the stuff for Mac
114+
if forMac {
115+
baseDir = filepath.Clean(filepath.Join(baseDir, ".."))
116+
copyTo(filepath.Join("..", "other_sources", "Info.plist"), filepath.Join(baseDir, "Info.plist"))
117+
copyTo(filepath.Join("..", "other_sources", "macicons.icns"), filepath.Join(baseDir, "Resources", "macicons.icns"))
118+
}
119+
120+
// The final executable should be, well, executable for everybody. 0777 should do it for Mac and Linux.
121+
os.Chmod(outputFilepath, 0777)
122+
123+
if err == nil {
124+
fmt.Println("<Build complete!>")
125+
fmt.Println("")
126+
}
127+
128+
if forWin {
129+
// Remove Resources; we don't need it in the root directory anymore after building.
130+
os.Remove(filepath.Join("..", "resource.syso"))
131+
}
132+
133+
}
134+
135+
// Compress the build output in bin. This is a separate step to ensure that any dependencies that need to be copied in from build
136+
// services (like Appveyor) can be done after building in the build service's configuration.
137+
func compress() {
138+
139+
fmt.Println("<Compressing build...>")
140+
141+
// Archive in .tar.gz because AppVeyor doesn't handle execution bits properly and I don't want to add a ton to the source code
142+
// just to box the output up into a .tar.gz.
143+
144+
os.Chdir("./bin") // Switch to the bin folder and then archive the contents
145+
146+
versions := []string{}
147+
148+
filepath.Walk(filepath.Clean("."), func(path string, info os.FileInfo, err error) error {
149+
150+
dirCount := len(strings.Split(path, string(os.PathSeparator)))
151+
152+
if info.IsDir() && dirCount == 1 && path != "." {
153+
154+
ending := ".tar.gz"
155+
if strings.Contains(path, "windows") {
156+
ending = ".zip"
157+
}
158+
159+
versions = append(versions, path, ending)
160+
}
161+
162+
return nil
163+
164+
})
165+
166+
ctx := context.Background()
167+
for i := 0; i < len(versions); i += 2 {
168+
169+
version := versions[i]
170+
ending := versions[i+1]
171+
172+
// We want to create separate archives for each version (e.g. release and demo)
173+
files, err := archives.FilesFromDisk(ctx, nil, map[string]string{
174+
version: version + ending,
175+
})
176+
177+
if err != nil {
178+
panic(err)
179+
}
180+
181+
format := archives.CompressedArchive{
182+
Compression: archives.Gz{},
183+
Archival: archives.Tar{},
184+
}
185+
186+
out, err := os.Create(version + ending)
187+
if err != nil {
188+
panic(err)
189+
}
190+
191+
defer out.Close()
192+
193+
err = format.Archive(ctx, out, files)
194+
if err != nil {
195+
panic(err)
196+
}
197+
198+
}
199+
200+
fmt.Println("<Build successfully compressed!>")
201+
202+
}
203+
204+
func publishToItch() {
205+
206+
buildNames := []string{}
207+
208+
filepath.Walk(filepath.Join("./", "bin"), func(path string, info os.FileInfo, err error) error {
209+
210+
dirCount := len(strings.Split(path, string(os.PathSeparator)))
211+
212+
if info.IsDir() && dirCount == 2 {
213+
buildNames = append(buildNames, path) // We want to upload the build directories
214+
}
215+
216+
return nil
217+
218+
})
219+
220+
fmt.Println("Builds found:", buildNames)
221+
222+
for _, build := range buildNames {
223+
224+
buildName := strings.Split(build, string(os.PathSeparator))[1]
225+
226+
result, err := exec.Command("butler", "push", build, "solarlune/masterplan:"+buildName).CombinedOutput()
227+
228+
if err == nil {
229+
fmt.Println("Published", build, "to itch!")
230+
} else {
231+
fmt.Println(string(result), string(err.Error()))
232+
}
233+
234+
}
235+
236+
}
237+
238+
func main() {
239+
240+
buildMP := flag.Bool("b", false, "Build MasterPlan into the bin directory.")
241+
osFlag := flag.String("os", "", "What target OS to build MasterPlan for. Omitting this flag will build MasterPlan for the current operating system.")
242+
archFlag := flag.String("arch", "", "What target arch to build MasterPlan for (either 'amd64' or 'arm64'). Omitting this flag will build MasterPlan for the current architecture.")
243+
compressMP := flag.Bool("c", false, "Compress build output.")
244+
itch := flag.Bool("i", false, "Upload build to itch.io.")
245+
246+
flag.Parse()
247+
248+
if *buildMP {
249+
targetName := runtime.GOOS
250+
targetArch := runtime.GOARCH
251+
if *osFlag != "" {
252+
targetName = *osFlag
253+
}
254+
if *archFlag != "" {
255+
targetArch = *archFlag
256+
}
257+
258+
build(filepath.Join("..", "bin", targetName+"-0.9-Release-"+targetArch), "release", targetName, targetArch)
259+
build(filepath.Join("..", "bin", targetName+"-0.9-Demo-"+targetArch), "demo", targetName, targetArch)
260+
}
261+
if *compressMP {
262+
compress() // Compresses all built binary folders in the ./bin folder
263+
}
264+
if *itch {
265+
publishToItch()
266+
}
267+
268+
if !*buildMP && !*compressMP && !*itch {
269+
270+
fmt.Println(
271+
"MASTERPLAN BUILD SCRIPT:\n",
272+
"To use this script, you can use the following arguments:\n",
273+
"\n",
274+
"-b to build MasterPlan for the current OS; cross-platform builds aren't fully supported yet. ",
275+
"\n",
276+
"Example to build for current OS, current architecture: go run . -b\n",
277+
"Example to build for AMD64 Windows: go run . -b -os windows -arch amd64 \n",
278+
"Example to build for ARM64 Mac: go run . -b -os darwin -arch arm64 \n",
279+
"Example to build for Mac, current arch: go run . -b -os darwin\n",
280+
"Example to build for Linux: go run . -b -os linux \n",
281+
"\n",
282+
"-c to compress the build output, as a .tar.gz file for Linux or Mac, or a .zip file for Windows.\n",
283+
"\n",
284+
"-i to publish the bin contents to itch.",
285+
)
286+
287+
}
288+
289+
}

build_script/go.mod

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module github.com/solarlune/masterplan/build
2+
3+
go 1.25.1
4+
5+
require (
6+
github.com/mholt/archives v0.1.4
7+
github.com/otiai10/copy v1.14.1
8+
)
9+
10+
require (
11+
github.com/STARRY-S/zip v0.2.3 // indirect
12+
github.com/andybalholm/brotli v1.2.0 // indirect
13+
github.com/bodgit/plumbing v1.3.0 // indirect
14+
github.com/bodgit/sevenzip v1.6.1 // indirect
15+
github.com/bodgit/windows v1.0.1 // indirect
16+
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
17+
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
18+
github.com/klauspost/compress v1.17.11 // indirect
19+
github.com/klauspost/pgzip v1.2.6 // indirect
20+
github.com/mikelolasagasti/xz v1.0.1 // indirect
21+
github.com/minio/minlz v1.0.1 // indirect
22+
github.com/nwaples/rardecode/v2 v2.1.1 // indirect
23+
github.com/otiai10/mint v1.6.3 // indirect
24+
github.com/pierrec/lz4/v4 v4.1.22 // indirect
25+
github.com/sorairolake/lzip-go v0.3.8 // indirect
26+
github.com/spf13/afero v1.11.0 // indirect
27+
github.com/ulikunitz/xz v0.5.15 // indirect
28+
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
29+
golang.org/x/sync v0.16.0 // indirect
30+
golang.org/x/sys v0.24.0 // indirect
31+
golang.org/x/text v0.28.0 // indirect
32+
)

0 commit comments

Comments
 (0)