diff --git a/dist/automate.go b/dist/automate.go new file mode 100644 index 000000000..469e5c41d --- /dev/null +++ b/dist/automate.go @@ -0,0 +1,164 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "sync" + + "github.com/mholt/archiver" +) + +var buildScript, pkgDir, distDir, buildDir, releaseDir string + +func init() { + pkgDir = filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "mholt", "caddy") + buildScript = filepath.Join(pkgDir, "build.bash") + distDir = filepath.Join(pkgDir, "dist") + buildDir = filepath.Join(distDir, "builds") + releaseDir = filepath.Join(distDir, "release") +} + +func main() { + // First, clean up + err := os.RemoveAll(buildDir) + if err != nil { + log.Fatal(err) + } + err = os.RemoveAll(releaseDir) + if err != nil { + log.Fatal(err) + } + + // Then set up + err = os.MkdirAll(buildDir, 0755) + if err != nil { + log.Fatal(err) + } + err = os.MkdirAll(releaseDir, 0755) + if err != nil { + log.Fatal(err) + } + + // Perform builds and make archives in parallel; only as many + // goroutines as we have processors. + var wg sync.WaitGroup + var throttle = make(chan struct{}, numProcs()) + for _, p := range platforms { + wg.Add(1) + throttle <- struct{}{} + + if p.os == "" || p.arch == "" || p.archive == "" { + log.Fatalf("Platform OS, architecture, and archive format is required: %+v", p) + } + + go func(p platform) { + defer wg.Done() + defer func() { <-throttle }() + + fmt.Printf("== Building %s\n", p) + + var baseFilename, binFilename string + baseFilename = fmt.Sprintf("caddy_%s_%s", p.os, p.arch) + if p.arch == "arm" { + baseFilename += p.arm + } + binFilename = baseFilename + if p.os == "windows" { + binFilename += ".exe" + } + + binPath := filepath.Join(buildDir, binFilename) + archive := filepath.Join(releaseDir, fmt.Sprintf("%s.%s", baseFilename, p.archive)) + archiveContents := append(distContents, binPath) + + err := build(p, binPath) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("== Compressing %s\n", baseFilename) + + if p.archive == "zip" { + err := archiver.Zip(archive, archiveContents) + if err != nil { + log.Fatal(err) + } + } else if p.archive == "tar.gz" { + err := archiver.TarGz(archive, archiveContents) + if err != nil { + log.Fatal(err) + } + } + }(p) + } + + wg.Wait() +} + +func build(p platform, out string) error { + cmd := exec.Command(buildScript, out) + cmd.Dir = pkgDir + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "CGO_ENABLED=0") + cmd.Env = append(cmd.Env, "GOOS="+p.os) + cmd.Env = append(cmd.Env, "GOARCH="+p.arch) + cmd.Env = append(cmd.Env, "GOARM="+p.arm) + cmd.Stderr = os.Stderr + return cmd.Run() +} + +type platform struct { + os, arch, arm, binExt, archive string +} + +func (p platform) String() string { + outStr := fmt.Sprintf("%s/%s", p.os, p.arch) + if p.arch == "arm" { + outStr += fmt.Sprintf(" (ARM v%s)", p.arm) + } + return outStr +} + +func numProcs() int { + n := runtime.GOMAXPROCS(0) + if n == runtime.NumCPU() && n > 1 { + n -= 1 + } + return n +} + +// See: https://golang.org/doc/install/source#environment +// Not all supported platforms are listed since some are +// problematic and we only build the most common ones. +// These are just the pre-made, readily-available static +// builds, and we can add more upon request if there is +// enough demand. +var platforms = []platform{ + {os: "darwin", arch: "386", archive: "zip"}, + {os: "darwin", arch: "amd64", archive: "zip"}, + {os: "freebsd", arch: "386", archive: "tar.gz"}, + {os: "freebsd", arch: "amd64", archive: "tar.gz"}, + {os: "freebsd", arch: "arm", arm: "7", archive: "tar.gz"}, + {os: "linux", arch: "386", archive: "tar.gz"}, + {os: "linux", arch: "amd64", archive: "tar.gz"}, + {os: "linux", arch: "arm", arm: "7", archive: "tar.gz"}, + {os: "linux", arch: "arm64", archive: "tar.gz"}, + {os: "netbsd", arch: "386", archive: "tar.gz"}, + {os: "netbsd", arch: "amd64", archive: "tar.gz"}, + {os: "openbsd", arch: "386", archive: "tar.gz"}, + {os: "openbsd", arch: "amd64", archive: "tar.gz"}, + {os: "solaris", arch: "amd64", archive: "tar.gz"}, + {os: "windows", arch: "386", archive: "zip"}, + {os: "windows", arch: "amd64", archive: "zip"}, +} + +var distContents = []string{ + filepath.Join(distDir, "init"), + filepath.Join(distDir, "CHANGES.txt"), + filepath.Join(distDir, "LICENSES.txt"), + filepath.Join(distDir, "README.txt"), +}