diff --git a/build.go b/build.go new file mode 100644 index 000000000..c00f03698 --- /dev/null +++ b/build.go @@ -0,0 +1,448 @@ +package main + +import ( + "archive/tar" + "archive/zip" + "bytes" + "compress/gzip" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "os/user" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" +) + +var ( + versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`) + goarch string + goos string + noupgrade bool +) + +func main() { + log.SetOutput(os.Stdout) + log.SetFlags(0) + + if os.Getenv("GOPATH") == "" { + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + gopath := filepath.Clean(filepath.Join(cwd, "../../../../")) + log.Println("GOPATH is", gopath) + os.Setenv("GOPATH", gopath) + } + os.Setenv("PATH", fmt.Sprintf("%s%cbin%c%s", os.Getenv("GOPATH"), os.PathSeparator, os.PathListSeparator, os.Getenv("PATH"))) + + flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH") + flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS") + flag.BoolVar(&noupgrade, "no-upgrade", false, "Disable upgrade functionality") + flag.Parse() + + if check() != nil { + setup() + } + + if flag.NArg() == 0 { + install("./cmd/...") + return + } + + switch flag.Arg(0) { + case "install": + pkg := "./cmd/..." + if flag.NArg() > 2 { + pkg = flag.Arg(1) + } + install(pkg) + + case "build": + pkg := "./cmd/syncthing" + if flag.NArg() > 2 { + pkg = flag.Arg(1) + } + var tags []string + if noupgrade { + tags = []string{"noupgrade"} + } + build(pkg, tags) + + case "test": + pkg := "./..." + if flag.NArg() > 2 { + pkg = flag.Arg(1) + } + test(pkg) + + case "assets": + assets() + + case "xdr": + xdr() + + case "translate": + translate() + + case "transifex": + transifex() + + case "deps": + deps() + + case "tar": + buildTar() + + case "zip": + buildZip() + + case "clean": + clean() + + default: + log.Fatalf("Unknown command %q", flag.Arg(0)) + } +} + +func check() error { + _, err := exec.LookPath("godep") + return err +} + +func setup() { + runPrint("go", "get", "-v", "code.google.com/p/go.tools/cmd/cover") + runPrint("go", "get", "-v", "code.google.com/p/go.tools/cmd/vet") + runPrint("go", "get", "-v", "code.google.com/p/go.net/html") + runPrint("go", "get", "-v", "github.com/mattn/goveralls") + runPrint("go", "get", "-v", "github.com/tools/godep") +} + +func test(pkg string) { + runPrint("godep", "go", "test", pkg) +} + +func install(pkg string) { + os.Setenv("GOBIN", "./bin") + runPrint("godep", "go", "install", "-ldflags", ldflags(), pkg) +} + +func build(pkg string, tags []string) { + rmr("syncthing", "syncthing.exe") + args := []string{"go", "build", "-ldflags", ldflags()} + if len(tags) > 0 { + args = append(args, "-tags", strings.Join(tags, ",")) + } + args = append(args, pkg) + runPrint("godep", args...) +} + +func buildTar() { + name := archiveName() + var tags []string + if noupgrade { + tags = []string{"noupgrade"} + name += "-noupgrade" + } + os.Setenv("GOOS", goos) + os.Setenv("GOARCH", goarch) + + build("./cmd/syncthing", tags) + filename := name + ".tar.gz" + tarGz(filename, []archiveFile{ + {"README.md", name + "/README.txt"}, + {"LICENSE", name + "/LICENSE.txt"}, + {"CONTRIBUTORS", name + "/CONTRIBUTORS.txt"}, + {"syncthing", name + "/syncthing"}, + }) + log.Println(filename) +} + +func buildZip() { + name := archiveName() + var tags []string + if noupgrade { + tags = []string{"noupgrade"} + name += "-noupgrade" + } + os.Setenv("GOOS", goos) + os.Setenv("GOARCH", goarch) + build("./cmd/syncthing", tags) + filename := name + ".zip" + zipFile(filename, []archiveFile{ + {"README.md", name + "/README.txt"}, + {"LICENSE", name + "/LICENSE.txt"}, + {"CONTRIBUTORS", name + "/CONTRIBUTORS.txt"}, + {"syncthing.exe", name + "/syncthing.exe"}, + }) + log.Println(filename) +} + +func assets() { + runPipe("auto/gui.files.go", "godep", "go", "run", "cmd/genassets/main.go", "gui") +} + +func xdr() { + for _, f := range []string{"discover/packets", "files/leveldb", "protocol/message"} { + runPipe(f+"_xdr.go", "go", "run", "./Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go", "--", f+".go") + } +} + +func translate() { + os.Chdir("gui") + runPipe("lang-en-new.json", "go", "run", "../cmd/translate/main.go", "lang-en.json", "index.html") + os.Remove("lang-en.json") + err := os.Rename("lang-en-new.json", "lang-en.json") + if err != nil { + log.Fatal(err) + } + os.Chdir("..") +} + +func transifex() { + os.Chdir("gui") + runPrint("go", "run", "../cmd/transifexdl/main.go") + os.Chdir("..") + assets() +} + +func deps() { + rmr("Godeps") + runPrint("godep", "save", "./cmd/...") +} + +func clean() { + rmr("bin", "Godeps/_workspace/pkg", "Godeps/_workspace/bin") + rmr(filepath.Join(os.Getenv("GOPATH"), fmt.Sprintf("pkg/%s_%s/github.com/syncthing", goos, goarch))) +} + +func ldflags() string { + var b bytes.Buffer + b.WriteString("-w") + b.WriteString(fmt.Sprintf(" -X main.Version %s", version())) + b.WriteString(fmt.Sprintf(" -X main.BuildStamp %d", buildStamp())) + b.WriteString(fmt.Sprintf(" -X main.BuildUser %s", buildUser())) + b.WriteString(fmt.Sprintf(" -X main.BuildHost %s", buildHost())) + b.WriteString(fmt.Sprintf(" -X main.BuildEnv %s", buildEnvironment())) + return b.String() +} + +func rmr(paths ...string) { + for _, path := range paths { + log.Println("rm -r", path) + os.RemoveAll(path) + } +} + +func version() string { + v := run("git", "describe", "--always", "--dirty") + v = versionRe.ReplaceAllFunc(v, func(s []byte) []byte { + s[0] = '+' + return s + }) + return string(v) +} + +func buildStamp() int64 { + bs := run("git", "show", "-s", "--format=%ct") + s, _ := strconv.ParseInt(string(bs), 10, 64) + return s +} + +func buildUser() string { + u, err := user.Current() + if err != nil { + return "unknown-user" + } + return strings.Replace(u.Username, " ", "-", -1) +} + +func buildHost() string { + h, err := os.Hostname() + if err != nil { + return "unknown-host" + } + return h +} + +func buildEnvironment() string { + if v := os.Getenv("ENVIRONMENT"); len(v) > 0 { + return v + } + return "default" +} + +func buildArch() string { + os := goos + if os == "darwin" { + os = "macosx" + } + return fmt.Sprintf("%s-%s", os, goarch) +} + +func archiveName() string { + return fmt.Sprintf("syncthing-%s-%s", buildArch(), version()) +} + +func run(cmd string, args ...string) []byte { + ecmd := exec.Command(cmd, args...) + bs, err := ecmd.CombinedOutput() + if err != nil { + log.Println(cmd, strings.Join(args, " ")) + log.Println(string(bs)) + log.Fatal(err) + } + return bytes.TrimSpace(bs) +} + +func runPrint(cmd string, args ...string) { + log.Println(cmd, strings.Join(args, " ")) + ecmd := exec.Command(cmd, args...) + ecmd.Stdout = os.Stdout + ecmd.Stderr = os.Stderr + err := ecmd.Run() + if err != nil { + log.Fatal(err) + } +} + +func runPipe(file, cmd string, args ...string) { + log.Println(cmd, strings.Join(args, " "), ">", file) + fd, err := os.Create(file) + if err != nil { + log.Fatal(err) + } + ecmd := exec.Command(cmd, args...) + ecmd.Stdout = fd + ecmd.Stderr = os.Stderr + err = ecmd.Run() + if err != nil { + log.Fatal(err) + } +} + +type archiveFile struct { + src string + dst string +} + +func tarGz(out string, files []archiveFile) { + fd, err := os.Create(out) + if err != nil { + log.Fatal(err) + } + + gw := gzip.NewWriter(fd) + tw := tar.NewWriter(gw) + + for _, f := range files { + sf, err := os.Open(f.src) + if err != nil { + log.Fatal(err) + } + + info, err := sf.Stat() + if err != nil { + log.Fatal(err) + } + h := &tar.Header{ + Name: f.dst, + Size: info.Size(), + Mode: int64(info.Mode()), + ModTime: info.ModTime(), + } + + err = tw.WriteHeader(h) + if err != nil { + log.Fatal(err) + } + _, err = io.Copy(tw, sf) + if err != nil { + log.Fatal(err) + } + sf.Close() + } + + err = tw.Close() + if err != nil { + log.Fatal(err) + } + err = gw.Close() + if err != nil { + log.Fatal(err) + } + err = fd.Close() + if err != nil { + log.Fatal(err) + } +} + +func zipFile(out string, files []archiveFile) { + fd, err := os.Create(out) + if err != nil { + log.Fatal(err) + } + + zw := zip.NewWriter(fd) + + for _, f := range files { + sf, err := os.Open(f.src) + if err != nil { + log.Fatal(err) + } + + info, err := sf.Stat() + if err != nil { + log.Fatal(err) + } + + fh, err := zip.FileInfoHeader(info) + if err != nil { + log.Fatal(err) + } + fh.Name = f.dst + fh.Method = zip.Deflate + + if strings.HasSuffix(f.dst, ".txt") { + // Text file. Read it and convert line endings. + bs, err := ioutil.ReadAll(sf) + if err != nil { + log.Fatal(err) + } + bs = bytes.Replace(bs, []byte{'\n'}, []byte{'\n', '\r'}, -1) + fh.UncompressedSize = uint32(len(bs)) + fh.UncompressedSize64 = uint64(len(bs)) + + of, err := zw.CreateHeader(fh) + if err != nil { + log.Fatal(err) + } + of.Write(bs) + } else { + // Binary file. Copy verbatim. + of, err := zw.CreateHeader(fh) + if err != nil { + log.Fatal(err) + } + _, err = io.Copy(of, sf) + if err != nil { + log.Fatal(err) + } + } + } + + err = zw.Close() + if err != nil { + log.Fatal(err) + } + err = fd.Close() + if err != nil { + log.Fatal(err) + } +} diff --git a/build.sh b/build.sh index 10bb932cf..540ee7c98 100755 --- a/build.sh +++ b/build.sh @@ -97,7 +97,7 @@ xdr() { translate() { pushd gui - go run ../cmd/translate/main.go lang-en.json < index.html > lang-en-new.json + go run ../cmd/translate/main.go lang-en.json index.html > lang-en-new.json mv lang-en-new.json lang-en.json popd } diff --git a/cmd/translate/main.go b/cmd/translate/main.go index 35da177e1..33dfa0f46 100644 --- a/cmd/translate/main.go +++ b/cmd/translate/main.go @@ -81,10 +81,16 @@ func main() { } fd.Close() - doc, err := html.Parse(os.Stdin) + fd, err = os.Open(os.Args[2]) if err != nil { log.Fatal(err) } + doc, err := html.Parse(fd) + if err != nil { + log.Fatal(err) + } + fd.Close() + generalNode(doc) bs, err := json.MarshalIndent(trans, "", " ") if err != nil {