Update build.go

This commit is contained in:
Alexander Neumann 2018-01-13 10:41:28 +01:00 committed by Zlatko Čalušić
parent f02ee7386a
commit cd62c2bceb

189
build.go
View file

@ -6,12 +6,12 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
) )
@ -23,15 +23,17 @@ var (
) )
var config = struct { var config = struct {
Name string Name string
Namespace string Namespace string
Main string Main string
Tests []string Tests []string
MinVersion GoVersion
}{ }{
Name: "rest-server", // name of the program executable and directory Name: "rest-server", // name of the program executable and directory
Namespace: "github.com/restic/rest-server", // subdir of GOPATH, e.g. "github.com/foo/bar" Namespace: "github.com/restic/rest-server", // subdir of GOPATH, e.g. "github.com/foo/bar"
Main: "github.com/restic/rest-server/cmd/rest-server", // package name for the main package Main: "github.com/restic/rest-server/cmd/rest-server", // package name for the main package
Tests: []string{"github.com/restic/rest-server"}, // tests to run Tests: []string{"github.com/restic/rest-server"}, // tests to run
MinVersion: GoVersion{Major: 1, Minor: 7, Patch: 0}, // minimum Go version supported
} }
// specialDir returns true if the file begins with a special character ('.' or '_'). // specialDir returns true if the file begins with a special character ('.' or '_').
@ -41,7 +43,7 @@ func specialDir(name string) bool {
} }
base := filepath.Base(name) base := filepath.Base(name)
if base[0] == '_' || base[0] == '.' { if base == "vendor" || base[0] == '_' || base[0] == '.' {
return true return true
} }
@ -63,20 +65,27 @@ func excludePath(name string) bool {
return true return true
} }
// updateGopath builds a valid GOPATH at dst, with all Go files in src/ copied to dst/prefix/, so calling // updateGopath builds a valid GOPATH at dst, with all Go files in src/ copied
// to dst/prefix/, so calling
// //
// updateGopath("/tmp/gopath", "/home/u/rest-server", "github.com/restic/rest-server") // updateGopath("/tmp/gopath", "/home/u/restic", "github.com/restic/restic")
// //
// with "/home/u/restic" containing the file "foo.go" yields the following tree at "/tmp/gopath": // with "/home/u/restic" containing the file "foo.go" yields the following tree
// at "/tmp/gopath":
// //
// /tmp/gopath // /tmp/gopath
// └── src // └── src
// └── github.com // └── github.com
// └── restic // └── restic
// └── rest-server // └── restic
// └── foo.go // └── foo.go
func updateGopath(dst, src, prefix string) error { func updateGopath(dst, src, prefix string) error {
verbosePrintf("copy contents of %v to %v\n", src, filepath.Join(dst, prefix))
return filepath.Walk(src, func(name string, fi os.FileInfo, err error) error { return filepath.Walk(src, func(name string, fi os.FileInfo, err error) error {
if name == src {
return err
}
if specialDir(name) { if specialDir(name) {
if fi.IsDir() { if fi.IsDir() {
return filepath.SkipDir return filepath.SkipDir
@ -85,6 +94,10 @@ func updateGopath(dst, src, prefix string) error {
return nil return nil
} }
if err != nil {
return err
}
if fi.IsDir() { if fi.IsDir() {
return nil return nil
} }
@ -125,7 +138,6 @@ func copyFile(dst, src string) error {
if err != nil { if err != nil {
return err return err
} }
defer fsrc.Close()
if err = os.MkdirAll(filepath.Dir(dst), 0755); err != nil { if err = os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
fmt.Printf("MkdirAll(%v)\n", filepath.Dir(dst)) fmt.Printf("MkdirAll(%v)\n", filepath.Dir(dst))
@ -136,17 +148,35 @@ func copyFile(dst, src string) error {
if err != nil { if err != nil {
return err return err
} }
defer fdst.Close()
_, err = io.Copy(fdst, fsrc) if _, err = io.Copy(fdst, fsrc); err != nil {
return err
}
if err == nil {
err = fsrc.Close()
}
if err == nil {
err = fdst.Close()
}
if err == nil { if err == nil {
err = os.Chmod(dst, fi.Mode()) err = os.Chmod(dst, fi.Mode())
} }
if err == nil { if err == nil {
err = os.Chtimes(dst, fi.ModTime(), fi.ModTime()) err = os.Chtimes(dst, fi.ModTime(), fi.ModTime())
} }
return err return nil
}
// die prints the message with fmt.Fprintf() to stderr and exits with an error
// code.
func die(message string, args ...interface{}) {
fmt.Fprintf(os.Stderr, message, args...)
os.Exit(1)
} }
func showUsage(output io.Writer) { func showUsage(output io.Writer) {
@ -171,7 +201,8 @@ func verbosePrintf(message string, args ...interface{}) {
fmt.Printf("build: "+message, args...) fmt.Printf("build: "+message, args...)
} }
// cleanEnv returns a clean environment with GOPATH and GOBIN removed (if present). // cleanEnv returns a clean environment with GOPATH and GOBIN removed (if
// present).
func cleanEnv() (env []string) { func cleanEnv() (env []string) {
for _, v := range os.Environ() { for _, v := range os.Environ() {
if strings.HasPrefix(v, "GOPATH=") || strings.HasPrefix(v, "GOBIN=") { if strings.HasPrefix(v, "GOPATH=") || strings.HasPrefix(v, "GOBIN=") {
@ -217,7 +248,8 @@ func test(cwd, gopath string, args ...string) error {
return cmd.Run() return cmd.Run()
} }
// getVersion returns the version string from the file VERSION in the current directory. // getVersion returns the version string from the file VERSION in the current
// directory.
func getVersionFromFile() string { func getVersionFromFile() string {
buf, err := ioutil.ReadFile("VERSION") buf, err := ioutil.ReadFile("VERSION")
if err != nil { if err != nil {
@ -228,8 +260,9 @@ func getVersionFromFile() string {
return strings.TrimSpace(string(buf)) return strings.TrimSpace(string(buf))
} }
// getVersion returns a version string which is a combination of the contents of the file VERSION in the current // getVersion returns a version string which is a combination of the contents
// directory and the version from git (if available). // of the file VERSION in the current directory and the version from git (if
// available).
func getVersion() string { func getVersion() string {
versionFile := getVersionFromFile() versionFile := getVersionFromFile()
versionGit := getVersionFromGit() versionGit := getVersionFromGit()
@ -247,7 +280,8 @@ func getVersion() string {
return fmt.Sprintf("%s (%s)", versionFile, versionGit) return fmt.Sprintf("%s (%s)", versionFile, versionGit)
} }
// getVersionFromGit returns a version string that identifies the currently checked out git commit. // getVersionFromGit returns a version string that identifies the currently
// checked out git commit.
func getVersionFromGit() string { func getVersionFromGit() string {
cmd := exec.Command("git", "describe", cmd := exec.Command("git", "describe",
"--long", "--tags", "--dirty", "--always") "--long", "--tags", "--dirty", "--always")
@ -262,7 +296,8 @@ func getVersionFromGit() string {
return version return version
} }
// Constants represents a set of constants that are set in the final binary to the given value via compiler flags. // Constants represents a set of constants that are set in the final binary to
// the given value via compiler flags.
type Constants map[string]string type Constants map[string]string
// LDFlags returns the string that can be passed to go build's `-ldflags`. // LDFlags returns the string that can be passed to go build's `-ldflags`.
@ -276,12 +311,81 @@ func (cs Constants) LDFlags() string {
return strings.Join(l, " ") return strings.Join(l, " ")
} }
func main() { // GoVersion is the version of Go used to compile the project.
log.SetFlags(0) type GoVersion struct {
Major int
Minor int
Patch int
}
ver := runtime.Version() // ParseGoVersion parses the Go version s. If s cannot be parsed, the returned GoVersion is null.
if strings.HasPrefix(ver, "go1") && ver < "go1.7" { func ParseGoVersion(s string) (v GoVersion) {
log.Fatalf("Go version %s detected, rest-server requires at least Go 1.7\n", ver) if !strings.HasPrefix(s, "go") {
return
}
s = s[2:]
data := strings.Split(s, ".")
if len(data) != 3 {
return
}
major, err := strconv.Atoi(data[0])
if err != nil {
return
}
minor, err := strconv.Atoi(data[1])
if err != nil {
return
}
patch, err := strconv.Atoi(data[2])
if err != nil {
return
}
v = GoVersion{
Major: major,
Minor: minor,
Patch: patch,
}
return
}
// AtLeast returns true if v is at least as new as other. If v is empty, true is returned.
func (v GoVersion) AtLeast(other GoVersion) bool {
var empty GoVersion
// the empty version satisfies all versions
if v == empty {
return true
}
if v.Major < other.Major {
return false
}
if v.Minor < other.Minor {
return false
}
if v.Patch < other.Patch {
return false
}
return true
}
func (v GoVersion) String() string {
return fmt.Sprintf("Go %d.%d.%d", v.Major, v.Minor, v.Patch)
}
func main() {
ver := ParseGoVersion(runtime.Version())
if !ver.AtLeast(config.MinVersion) {
fmt.Fprintf(os.Stderr, "%s detected, this program requires at least %s\n", ver, config.MinVersion)
os.Exit(1)
} }
buildTags := []string{} buildTags := []string{}
@ -307,7 +411,7 @@ func main() {
keepGopath = true keepGopath = true
case "-t", "-tags", "--tags": case "-t", "-tags", "--tags":
if i+1 >= len(params) { if i+1 >= len(params) {
log.Fatal("-t given but no tag specified") die("-t given but no tag specified")
} }
skipNext = true skipNext = true
buildTags = strings.Split(params[i+1], " ") buildTags = strings.Split(params[i+1], " ")
@ -328,7 +432,7 @@ func main() {
showUsage(os.Stdout) showUsage(os.Stdout)
return return
default: default:
log.Printf("Error: unknown option %q\n\n", arg) fmt.Fprintf(os.Stderr, "Error: unknown option %q\n\n", arg)
showUsage(os.Stderr) showUsage(os.Stderr)
os.Exit(1) os.Exit(1)
} }
@ -347,23 +451,23 @@ func main() {
root, err := os.Getwd() root, err := os.Getwd()
if err != nil { if err != nil {
log.Fatalf("Getwd(): %v\n", err) die("Getwd(): %v\n", err)
} }
gopath, err := ioutil.TempDir("", fmt.Sprintf("%v-build-", config.Name)) gopath, err := ioutil.TempDir("", fmt.Sprintf("%v-build-", config.Name))
if err != nil { if err != nil {
log.Fatalf("TempDir(): %v\n", err) die("TempDir(): %v\n", err)
} }
verbosePrintf("create GOPATH at %v\n", gopath) verbosePrintf("create GOPATH at %v\n", gopath)
if err = updateGopath(gopath, root, config.Namespace); err != nil { if err = updateGopath(gopath, root, config.Namespace); err != nil {
log.Fatalf("copying files from %v/src to %v/src failed: %v\n", root, gopath, err) die("copying files from %v/src to %v/src failed: %v\n", root, gopath, err)
} }
vendor := filepath.Join(root, "vendor") vendor := filepath.Join(root, "vendor")
if directoryExists(vendor) { if directoryExists(vendor) {
if err = updateGopath(gopath, vendor, ""); err != nil { if err = updateGopath(gopath, vendor, filepath.Join(config.Namespace, "vendor")); err != nil {
log.Fatalf("copying files from %v to %v failed: %v\n", root, gopath, err) die("copying files from %v to %v failed: %v\n", root, gopath, err)
} }
} }
@ -371,7 +475,7 @@ func main() {
if !keepGopath { if !keepGopath {
verbosePrintf("remove %v\n", gopath) verbosePrintf("remove %v\n", gopath)
if err = os.RemoveAll(gopath); err != nil { if err = os.RemoveAll(gopath); err != nil {
log.Fatalf("remove GOPATH at %s failed: %v\n", gopath, err) die("remove GOPATH at %s failed: %v\n", err)
} }
} else { } else {
verbosePrintf("leaving temporary GOPATH at %v\n", gopath) verbosePrintf("leaving temporary GOPATH at %v\n", gopath)
@ -387,9 +491,12 @@ func main() {
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
log.Fatalf("Getwd() returned %v\n", err) die("Getwd() returned %v\n", err)
}
output := outputFilename
if !filepath.IsAbs(output) {
output = filepath.Join(cwd, output)
} }
output := filepath.Join(cwd, outputFilename)
version := getVersion() version := getVersion()
constants := Constants{} constants := Constants{}
@ -407,7 +514,7 @@ func main() {
err = build(filepath.Join(gopath, "src"), targetGOOS, targetGOARCH, gopath, args...) err = build(filepath.Join(gopath, "src"), targetGOOS, targetGOARCH, gopath, args...)
if err != nil { if err != nil {
log.Fatalf("build failed: %v\n", err) die("build failed: %v\n", err)
} }
if runTests { if runTests {
@ -415,7 +522,7 @@ func main() {
err = test(cwd, gopath, config.Tests...) err = test(cwd, gopath, config.Tests...)
if err != nil { if err != nil {
log.Fatalf("running tests failed: %v\n", err) die("running tests failed: %v\n", err)
} }
} }
} }