| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // The makefuchsia.go script builds a GOROOT in the out/ build directory |
| // using sources from third_party/go. |
| // |
| // When the GOROOT is first built, this script also calls make.bash. |
| // On subsquent modifications to source files in third_party/go, the |
| // source files are copied into the out/ directory but the toolchain |
| // is not rebuilt as the content-based hashing in the Go tool will |
| // rebuild any stale packages. |
| package main |
| |
| import ( |
| "bytes" |
| "crypto/sha256" |
| "flag" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "log" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "strings" |
| "sync" |
| ) |
| |
| var ( |
| flagFuchsiaGoroot = flag.String("fuchsia-goroot", "", "Path to Fuchsia's source GOROOT") |
| flagDepfile = flag.String("depfile", "", "Stdlib depfile") |
| flagRootOutDir = flag.String("root-out-dir", "", "GN variable") |
| flagRootGenDir = flag.String("root-gen-dir", "", "GN variable") |
| flagTargetCPU = flag.String("target-cpu", "", "GN variable") |
| flagOutGoroot = flag.String("out-goroot", "", "Path to the output GOROOT") |
| ) |
| |
| func relGenDir() string { |
| return (*flagRootGenDir)[len(*flagRootOutDir)+1:] |
| } |
| |
| func mkdir(path ...string) { |
| if err := os.MkdirAll(join(path...), 0755); err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| func join(path ...string) string { return filepath.Join(path...) } |
| |
| func rm(path string) { |
| if err := os.RemoveAll(path); err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| func symlink(src, dst string) { |
| if err := os.Symlink(src, dst); err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| func hashfile(path string) string { |
| h := sha256.New() |
| |
| f, err := os.Open(path) |
| if err != nil { |
| if os.IsNotExist(err) { |
| return "does_not_exist(" + path + ")" |
| } |
| log.Fatal(err) |
| } |
| defer f.Close() |
| |
| if _, err := io.Copy(h, f); err != nil { |
| log.Fatalf("hash %s: %v", path, err) |
| } |
| |
| return string(h.Sum(nil)) |
| } |
| |
| var didSomething = false |
| |
| // copyfile copies src to dst if the two files have different contents. |
| func copyfile(src, dst string) { |
| if hashfile(src) == hashfile(dst) { |
| return |
| } |
| didSomething = true |
| |
| rm(dst) |
| srcfile, err := os.Open(src) |
| if err != nil { |
| log.Fatal(err) |
| } |
| defer srcfile.Close() |
| |
| f, err := os.Create(dst) |
| if err != nil { |
| log.Fatal(err) |
| } |
| if _, err := io.Copy(f, srcfile); err != nil { |
| log.Fatalf("cp %s %s: %v", src, dst, err) |
| } |
| if err := f.Close(); err != nil { |
| log.Fatalf("cp %s %s: close: %v", src, dst, err) |
| } |
| |
| fi, err := os.Stat(src) |
| if err != nil { |
| log.Fatal(err) |
| } |
| if err := os.Chmod(dst, fi.Mode()); err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| func collectDeps(w io.Writer) error { |
| fn := func(path string, info os.FileInfo, err error) error { |
| if info.IsDir() || strings.HasSuffix(path, "_test.go") { |
| return err |
| } |
| fmt.Fprintf(w, " %s", path) |
| return err |
| } |
| srcGoroot := join(*flagFuchsiaGoroot, "src") |
| return filepath.Walk(srcGoroot, fn) |
| } |
| |
| func genDepfileStdlib() { |
| // Change anything under src/*, and we copy those changes into out/. |
| path := *flagDepfile |
| if path == "" { |
| return |
| } |
| os.Remove(path) |
| buf := new(bytes.Buffer) |
| buf.WriteString(relGenDir() + "/goroot.done:") |
| if err := collectDeps(buf); err != nil { |
| log.Fatal("gen depfile stdlib: %v", err) |
| } |
| depbytes := buf.Bytes() |
| |
| h := sha256.New() |
| h.Write(depbytes) |
| newhash := string(h.Sum(nil)) |
| |
| if hashfile(path) == newhash { |
| return // don't overwrite an accurate file |
| } |
| |
| if err := ioutil.WriteFile(path, depbytes, 0664); err != nil { |
| log.Fatal("gen depfile stdlib: %v", err) |
| } |
| } |
| |
| func convCPU(cpu string) (arch string) { |
| switch cpu { |
| case "x64": |
| return "amd64" |
| case "arm64": |
| return "arm64" |
| } |
| return "" |
| } |
| |
| func main() { |
| log.SetFlags(0) |
| log.SetPrefix("makefuchsia.go: ") |
| flag.Parse() |
| |
| if *flagRootGenDir == "" { |
| log.Fatal("must specify -root-gen-dir") |
| } |
| |
| if *flagFuchsiaGoroot == "" { |
| log.Fatal("must specify -fuchsia-goroot when building toolchain") |
| } |
| |
| goarchTarget := convCPU(*flagTargetCPU) |
| if goarchTarget == "" { |
| log.Fatalf("unsupported target CPU: %s", *flagTargetCPU) |
| } |
| |
| genDepfileStdlib() |
| |
| srcGoroot := *flagFuchsiaGoroot |
| dstGoroot := *flagOutGoroot |
| |
| src := join(srcGoroot, "src") |
| dst := join(dstGoroot, "src") |
| |
| mkdir(dst) |
| mkdir(join(dstGoroot, "misc")) |
| mkdir(join(dstGoroot, "pkg")) |
| mkdir(join(dstGoroot, "bin")) |
| mkdir(join(dstGoroot, "lib")) |
| |
| mkdir(join(dstGoroot, "misc/fuchsia")) |
| miscFiles := []string{ |
| "clangwrap.sh", |
| "gccwrap.sh", |
| "go_stdlib_tests.json", |
| } |
| for _, file := range miscFiles { |
| copyfile(join(srcGoroot, "misc/fuchsia", file), join(dstGoroot, "misc/fuchsia", file)) |
| } |
| |
| copyfile(join(srcGoroot, "VERSION.cache"), join(dstGoroot, "VERSION.cache")) |
| copyfile(join(srcGoroot, "VERSION.cache"), join(dstGoroot, "VERSION")) |
| |
| srcFiles, err := filepath.Glob(join(src, "*")) |
| if err != nil { |
| log.Fatalf("could not glob source files: %v", err) |
| } |
| |
| var wg sync.WaitGroup |
| for _, file := range srcFiles { |
| file := file |
| deepCopy := func(path string, info os.FileInfo, err error) error { |
| if err != nil { |
| return err |
| } |
| if strings.HasSuffix(path, "_test.go") && !strings.Contains(path, "os/") { |
| return nil |
| } |
| if filepath.Base(path) == "testdata" { |
| return filepath.SkipDir |
| } |
| if info.IsDir() { |
| return nil |
| } |
| dstPath := join(dst, path[len(src)+1:]) |
| mkdir(filepath.Dir(dstPath)) |
| copyfile(path, dstPath) |
| return nil |
| } |
| wg.Add(1) |
| go func() { |
| if err := filepath.Walk(file, deepCopy); err != nil { |
| log.Fatalf("goroot deep copy failed: %v", err) |
| } |
| wg.Done() |
| }() |
| } |
| wg.Wait() |
| |
| if _, err := os.Stat(join(dstGoroot, "bin", "go")); err != nil { |
| env := os.Environ() |
| env = append(env, "CC_FOR_fuchsia_"+goarchTarget+"="+dstGoroot+"/misc/fuchsia/gccwrap.sh") |
| |
| // Fresh copy, we need to build the compiler. |
| // On subsequent copies, we can rely on "go build" to |
| // rebuild any standard library changes. |
| cmd := exec.Command(join(dst, "make.bash")) |
| cmd.Dir = dst |
| cmd.Env = env |
| buf := new(bytes.Buffer) |
| cmd.Stdout = buf |
| cmd.Stderr = buf |
| if err := cmd.Run(); err != nil { |
| log.Fatalf("make.bash failed: %v\n%s", err, buf.String()) |
| } |
| } |
| |
| if didSomething { |
| donePath := join(*flagRootGenDir, "goroot.done") |
| rm(donePath) |
| if err := ioutil.WriteFile(donePath, []byte("done"), 0664); err != nil { |
| log.Fatalf("writing goroot.done failed: %v", err) |
| } |
| } |
| } |