Remove vendor from build scripts

This commit is contained in:
Alexander Neumann 2020-03-01 11:30:02 +01:00
parent 266f9dbe16
commit 44a57d66c3
2 changed files with 47 additions and 216 deletions

View file

@ -3,7 +3,7 @@
all: restic all: restic
restic: restic:
go run -mod=vendor build.go || go run build.go go run build.go
clean: clean:
rm -f restic rm -f restic

261
build.go
View file

@ -3,22 +3,15 @@
// This program aims to make building Go programs for end users easier by just // This program aims to make building Go programs for end users easier by just
// calling it with `go run`, without having to setup a GOPATH. // calling it with `go run`, without having to setup a GOPATH.
// //
// For Go < 1.11, it'll create a new GOPATH in a temporary directory, then run // This program needs Go >= 1.11. It'll use Go modules for compilation. It
// `go build` on the package configured as Main in the Config struct. // builds the package configured as Main in the Config struct.
//
// For Go >= 1.11 if the file go.mod is present, it'll use Go modules and not
// setup a GOPATH. It builds the package configured as Main in the Config
// struct with `go build -mod=vendor` to use the vendored dependencies.
// The variable GOPROXY is set to `off` so that no network calls are made. All
// files are copied to a temporary directory before `go build` is called within
// that directory.
// BSD 2-Clause License // BSD 2-Clause License
// //
// Copyright (c) 2016-2018, Alexander Neumann <alexander@bumpern.de> // Copyright (c) 2016-2018, Alexander Neumann <alexander@bumpern.de>
// All rights reserved. // All rights reserved.
// //
// This file has been copied from the repository at: // This file has been derived from the repository at:
// https://github.com/fd0/build-go // https://github.com/fd0/build-go
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -79,125 +72,13 @@ type Config struct {
} }
var ( var (
verbose bool verbose bool
keepGopath bool runTests bool
runTests bool enableCGO bool
enableCGO bool enablePIE bool
enablePIE bool goVersion = ParseGoVersion(runtime.Version())
goVersion = ParseGoVersion(runtime.Version())
) )
// copy all Go files in src to dst, creating directories on the fly, so calling
//
// copy("/tmp/gopath/src/github.com/restic/restic", "/home/u/restic")
//
// with "/home/u/restic" containing the file "foo.go" yields the following tree
// at "/tmp/gopath":
//
// /tmp/gopath
// └── src
// └── github.com
// └── restic
// └── restic
// └── foo.go
func copy(dst, src string) error {
verbosePrintf("copy contents of %v to %v\n", src, dst)
return filepath.Walk(src, func(name string, fi os.FileInfo, err error) error {
if name == src {
return err
}
if name == ".git" {
return filepath.SkipDir
}
if err != nil {
return err
}
if fi.IsDir() {
return nil
}
intermediatePath, err := filepath.Rel(src, name)
if err != nil {
return err
}
fileSrc := filepath.Join(src, intermediatePath)
fileDst := filepath.Join(dst, intermediatePath)
return copyFile(fileDst, fileSrc)
})
}
func directoryExists(dirname string) bool {
stat, err := os.Stat(dirname)
if err != nil && os.IsNotExist(err) {
return false
}
return stat.IsDir()
}
func fileExists(filename string) bool {
stat, err := os.Stat(filename)
if err != nil && os.IsNotExist(err) {
return false
}
return stat.Mode().IsRegular()
}
// copyFile creates dst from src, preserving file attributes and timestamps.
func copyFile(dst, src string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
fsrc, err := os.Open(src)
if err != nil {
return err
}
if err = os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
fmt.Printf("MkdirAll(%v)\n", filepath.Dir(dst))
return err
}
fdst, err := os.Create(dst)
if err != nil {
_ = fsrc.Close()
return err
}
_, err = io.Copy(fdst, fsrc)
if err != nil {
_ = fsrc.Close()
_ = fdst.Close()
return err
}
err = fdst.Close()
if err != nil {
_ = fsrc.Close()
return err
}
err = fsrc.Close()
if err != nil {
return err
}
err = os.Chmod(dst, fi.Mode())
if err != nil {
return err
}
return os.Chtimes(dst, fi.ModTime(), fi.ModTime())
}
// die prints the message with fmt.Fprintf() to stderr and exits with an error // die prints the message with fmt.Fprintf() to stderr and exits with an error
// code. // code.
func die(message string, args ...interface{}) { func die(message string, args ...interface{}) {
@ -211,7 +92,6 @@ func showUsage(output io.Writer) {
fmt.Fprintf(output, "OPTIONS:\n") fmt.Fprintf(output, "OPTIONS:\n")
fmt.Fprintf(output, " -v --verbose output more messages\n") fmt.Fprintf(output, " -v --verbose output more messages\n")
fmt.Fprintf(output, " -t --tags specify additional build tags\n") fmt.Fprintf(output, " -t --tags specify additional build tags\n")
fmt.Fprintf(output, " -k --keep-tempdir do not remove the temporary directory after build\n")
fmt.Fprintf(output, " -T --test run tests\n") fmt.Fprintf(output, " -T --test run tests\n")
fmt.Fprintf(output, " -o --output set output file name\n") fmt.Fprintf(output, " -o --output set output file name\n")
fmt.Fprintf(output, " --enable-cgo use CGO to link against libc\n") fmt.Fprintf(output, " --enable-cgo use CGO to link against libc\n")
@ -219,7 +99,6 @@ func showUsage(output io.Writer) {
fmt.Fprintf(output, " --goos value set GOOS for cross-compilation\n") fmt.Fprintf(output, " --goos value set GOOS for cross-compilation\n")
fmt.Fprintf(output, " --goarch value set GOARCH for cross-compilation\n") fmt.Fprintf(output, " --goarch value set GOARCH for cross-compilation\n")
fmt.Fprintf(output, " --goarm value set GOARM for cross-compilation\n") fmt.Fprintf(output, " --goarm value set GOARM for cross-compilation\n")
fmt.Fprintf(output, " --tempdir dir use a specific directory for compilation\n")
} }
func verbosePrintf(message string, args ...interface{}) { func verbosePrintf(message string, args ...interface{}) {
@ -230,49 +109,39 @@ func verbosePrintf(message string, args ...interface{}) {
fmt.Printf("build: "+message, args...) fmt.Printf("build: "+message, args...)
} }
// cleanEnv returns a clean environment with GOPATH, GOBIN and GO111MODULE // printEnv prints Go-relevant environment variables in a nice way using verbosePrintf.
// removed (if present). func printEnv(env []string) {
func cleanEnv() (env []string) { verbosePrintf("environment (GO*):\n")
removeKeys := map[string]struct{}{ for _, v := range env {
"GOPATH": struct{}{}, // ignore environment variables which do not start with GO*.
"GOBIN": struct{}{}, if !strings.HasPrefix(v, "GO") {
"GO111MODULE": struct{}{},
}
for _, v := range os.Environ() {
data := strings.SplitN(v, "=", 2)
name := data[0]
if _, ok := removeKeys[name]; ok {
continue continue
} }
verbosePrintf(" %s\n", v)
env = append(env, v)
} }
return env
} }
// build runs "go build args..." with GOPATH set to gopath. // build runs "go build args..." with GOPATH set to gopath.
func build(cwd string, env map[string]string, args ...string) error { func build(cwd string, env map[string]string, args ...string) error {
a := []string{"build"} a := []string{"build"}
if goVersion.AtLeast(GoVersion{1, 10, 0}) { // try to remove all absolute paths from resulting binary
verbosePrintf("Go version is at least 1.10, using new syntax for -gcflags\n") if goVersion.AtLeast(GoVersion{1, 13, 0}) {
// use new prefix // use the new flag introduced by Go 1.13
a = append(a, "-trimpath")
} else {
// otherwise try to trim as many paths as possible
a = append(a, "-asmflags", fmt.Sprintf("all=-trimpath=%s", cwd)) a = append(a, "-asmflags", fmt.Sprintf("all=-trimpath=%s", cwd))
a = append(a, "-gcflags", fmt.Sprintf("all=-trimpath=%s", cwd)) a = append(a, "-gcflags", fmt.Sprintf("all=-trimpath=%s", cwd))
} else {
a = append(a, "-asmflags", fmt.Sprintf("-trimpath=%s", cwd))
a = append(a, "-gcflags", fmt.Sprintf("-trimpath=%s", cwd))
} }
if enablePIE { if enablePIE {
a = append(a, "-buildmode=pie") a = append(a, "-buildmode=pie")
} }
a = append(a, args...) a = append(a, args...)
cmd := exec.Command("go", a...) cmd := exec.Command("go", a...)
cmd.Env = append(cleanEnv(), "GOPROXY=off") cmd.Env = os.Environ()
for k, v := range env { for k, v := range env {
cmd.Env = append(cmd.Env, k+"="+v) cmd.Env = append(cmd.Env, k+"="+v)
} }
@ -280,6 +149,8 @@ func build(cwd string, env map[string]string, args ...string) error {
cmd.Env = append(cmd.Env, "CGO_ENABLED=0") cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
} }
printEnv(cmd.Env)
cmd.Dir = cwd cmd.Dir = cwd
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@ -294,7 +165,7 @@ func build(cwd string, env map[string]string, args ...string) error {
func test(cwd string, env map[string]string, args ...string) error { func test(cwd string, env map[string]string, args ...string) error {
args = append([]string{"test", "-count", "1"}, args...) args = append([]string{"test", "-count", "1"}, args...)
cmd := exec.Command("go", args...) cmd := exec.Command("go", args...)
cmd.Env = append(cleanEnv(), "GOPROXY=off") cmd.Env = os.Environ()
for k, v := range env { for k, v := range env {
cmd.Env = append(cmd.Env, k+"="+v) cmd.Env = append(cmd.Env, k+"="+v)
} }
@ -305,6 +176,8 @@ func test(cwd string, env map[string]string, args ...string) error {
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
printEnv(cmd.Env)
verbosePrintf("chdir %q\n", cwd) verbosePrintf("chdir %q\n", cwd)
verbosePrintf("go %q\n", args) verbosePrintf("go %q\n", args)
@ -454,6 +327,10 @@ func (v GoVersion) String() string {
} }
func main() { func main() {
if !goVersion.AtLeast(GoVersion{1, 11, 0}) {
die("Go version (%v) is too old, Go <= 1.11 does not support Go Modules\n", goVersion)
}
if !goVersion.AtLeast(config.MinVersion) { if !goVersion.AtLeast(config.MinVersion) {
fmt.Fprintf(os.Stderr, "%s detected, this program requires at least %s\n", goVersion, config.MinVersion) fmt.Fprintf(os.Stderr, "%s detected, this program requires at least %s\n", goVersion, config.MinVersion)
os.Exit(1) os.Exit(1)
@ -464,15 +341,13 @@ func main() {
skipNext := false skipNext := false
params := os.Args[1:] params := os.Args[1:]
goEnv := map[string]string{} env := map[string]string{
buildEnv := map[string]string{ "GO111MODULE": "on", // make sure we build in Module mode
"GOOS": runtime.GOOS, "GOOS": runtime.GOOS,
"GOARCH": runtime.GOARCH, "GOARCH": runtime.GOARCH,
"GOARM": "", "GOARM": "",
} }
tempdir := ""
var outputFilename string var outputFilename string
for i, arg := range params { for i, arg := range params {
@ -484,8 +359,6 @@ func main() {
switch arg { switch arg {
case "-v", "--verbose": case "-v", "--verbose":
verbose = true verbose = true
case "-k", "--keep-gopath":
keepGopath = true
case "-t", "-tags", "--tags": case "-t", "-tags", "--tags":
if i+1 >= len(params) { if i+1 >= len(params) {
die("-t given but no tag specified") die("-t given but no tag specified")
@ -495,9 +368,6 @@ func main() {
case "-o", "--output": case "-o", "--output":
skipNext = true skipNext = true
outputFilename = params[i+1] outputFilename = params[i+1]
case "--tempdir":
skipNext = true
tempdir = params[i+1]
case "-T", "--test": case "-T", "--test":
runTests = true runTests = true
case "--enable-cgo": case "--enable-cgo":
@ -506,13 +376,13 @@ func main() {
enablePIE = true enablePIE = true
case "--goos": case "--goos":
skipNext = true skipNext = true
buildEnv["GOOS"] = params[i+1] env["GOOS"] = params[i+1]
case "--goarch": case "--goarch":
skipNext = true skipNext = true
buildEnv["GOARCH"] = params[i+1] env["GOARCH"] = params[i+1]
case "--goarm": case "--goarm":
skipNext = true skipNext = true
buildEnv["GOARM"] = params[i+1] env["GOARM"] = params[i+1]
case "-h": case "-h":
showUsage(os.Stdout) showUsage(os.Stdout)
return return
@ -538,7 +408,7 @@ func main() {
if outputFilename == "" { if outputFilename == "" {
outputFilename = config.Name outputFilename = config.Name
if buildEnv["GOOS"] == "windows" { if env["GOOS"] == "windows" {
outputFilename += ".exe" outputFilename += ".exe"
} }
} }
@ -567,57 +437,18 @@ func main() {
} }
buildTarget := filepath.FromSlash(mainPackage) buildTarget := filepath.FromSlash(mainPackage)
buildCWD := "" buildCWD, err := os.Getwd()
if err != nil {
if goVersion.AtLeast(GoVersion{1, 11, 0}) && fileExists("go.mod") { die("unable to determine current working directory: %v\n", err)
verbosePrintf("Go >= 1.11 and 'go.mod' found, building with modules\n")
buildCWD = root
buildArgs = append(buildArgs, "-mod=vendor")
testArgs = append(testArgs, "-mod=vendor")
goEnv["GO111MODULE"] = "on"
buildEnv["GO111MODULE"] = "on"
} else {
if tempdir == "" {
tempdir, err = ioutil.TempDir("", fmt.Sprintf("%v-build-", config.Name))
if err != nil {
die("TempDir(): %v\n", err)
}
}
verbosePrintf("Go < 1.11 or 'go.mod' not found, create GOPATH at %v\n", tempdir)
targetdir := filepath.Join(tempdir, "src", filepath.FromSlash(config.Namespace))
if err = copy(targetdir, root); err != nil {
die("copying files from %v to %v/src failed: %v\n", root, tempdir, err)
}
defer func() {
if !keepGopath {
verbosePrintf("remove %v\n", tempdir)
if err = os.RemoveAll(tempdir); err != nil {
die("remove GOPATH at %s failed: %v\n", tempdir, err)
}
} else {
verbosePrintf("leaving temporary GOPATH at %v\n", tempdir)
}
}()
buildCWD = targetdir
goEnv["GOPATH"] = tempdir
buildEnv["GOPATH"] = tempdir
} }
verbosePrintf("environment:\n go: %v\n build: %v\n", goEnv, buildEnv)
buildArgs = append(buildArgs, buildArgs = append(buildArgs,
"-tags", strings.Join(buildTags, " "), "-tags", strings.Join(buildTags, " "),
"-ldflags", ldflags, "-ldflags", ldflags,
"-o", output, buildTarget, "-o", output, buildTarget,
) )
err = build(buildCWD, buildEnv, buildArgs...) err = build(buildCWD, env, buildArgs...)
if err != nil { if err != nil {
die("build failed: %v\n", err) die("build failed: %v\n", err)
} }
@ -627,7 +458,7 @@ func main() {
testArgs = append(testArgs, config.Tests...) testArgs = append(testArgs, config.Tests...)
err = test(buildCWD, goEnv, testArgs...) err = test(buildCWD, env, testArgs...)
if err != nil { if err != nil {
die("running tests failed: %v\n", err) die("running tests failed: %v\n", err)
} }