| // 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. |
| package main |
| |
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "log" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "strings" |
| ) |
| |
| var ( |
| flagBuildtoolsGo = flag.String("buildtools-go", "", "Path to go tool used for bootstrap") |
| 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...) } |
| |
| // copyfile copies src to dst if the two files have different contents. |
| func copyfile(src, dst string) { |
| srcfile, err := os.Open(src) |
| if err != nil { |
| log.Fatal(err) |
| } |
| defer srcfile.Close() |
| |
| fi, err := srcfile.Stat() |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| mkdir(filepath.Dir(dst)) |
| f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, fi.Mode()) |
| 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) |
| } |
| } |
| |
| func convCPU(cpu string) (arch string) { |
| switch cpu { |
| case "x64": |
| return "amd64" |
| case "arm64": |
| return "arm64" |
| } |
| return "" |
| } |
| |
| // allFilesIn recursively walks a set of files, returning the entire tree in |
| // paths relative to the given root. A map is returned for convenience. If |
| // prefix is given, only files that match the prefix are considered. |
| func allFilesIn(root string, paths map[string]struct{}, prefix ...string) error { |
| return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { |
| if err != nil || info.IsDir() { |
| return err |
| } |
| for _, pfx := range prefix { |
| if strings.HasPrefix(path, join(root, pfx)) { |
| relPath := strings.TrimPrefix(path, root)[1:] |
| paths[relPath] = struct{}{} |
| return err |
| } |
| } |
| return err |
| }) |
| } |
| |
| 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) |
| } |
| |
| srcGoroot := *flagFuchsiaGoroot |
| dstGoroot := *flagOutGoroot |
| |
| srcFiles := map[string]struct{}{} |
| if err := allFilesIn(join(srcGoroot), srcFiles, "src/", "misc/fuchsia/"); err != nil { |
| log.Fatal(err) |
| } |
| |
| dstFiles := map[string]struct{}{} |
| if _, err := os.Stat(join(dstGoroot)); err == nil { |
| if err := allFilesIn(join(dstGoroot), dstFiles, ""); err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| for f := range dstFiles { |
| if _, ok := srcFiles[f]; !ok { |
| // try to preserve the bin/ and pkg/ trees, as they can be used to do faster |
| // rebuilds of the toolchain. |
| if strings.HasPrefix(f, "src/") || strings.HasPrefix(f, "misc/fuchsia/") { |
| os.Remove(filepath.Join(dstGoroot, f)) |
| } |
| } |
| } |
| |
| for f := range srcFiles { |
| src, dst := join(srcGoroot, f), join(dstGoroot, f) |
| copyfile(src, dst) |
| } |
| |
| versionFile := join(dstGoroot, "VERSION") |
| if err := ioutil.WriteFile(versionFile, []byte("devel third_party/go fuchsia"), 0664); err != nil { |
| log.Fatal(err) |
| } |
| |
| cmd := exec.Command(*flagBuildtoolsGo, "env", "GOROOT") |
| out, err := cmd.CombinedOutput() |
| if err != nil { |
| log.Fatalf("cannot find bootstrap GOROOT: %v", err) |
| } |
| |
| env := os.Environ() |
| env = append(env, "GOROOT_BOOTSTRAP="+strings.TrimSpace(string(out))) |
| env = append(env, "CC_FOR_fuchsia_"+goarchTarget+"="+dstGoroot+"/misc/fuchsia/gccwrap.sh") |
| |
| cmd = exec.Command(join(dstGoroot, "src", "make.bash")) |
| cmd.Args = []string{"--no-clean"} |
| cmd.Dir = join(dstGoroot, "src") |
| 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()) |
| } |
| |
| depfile, err := os.Create(*flagDepfile) |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Fprintf(depfile, "%s/goroot.done:", relGenDir()) |
| for f := range srcFiles { |
| fmt.Fprintf(depfile, " %s", join(srcGoroot, f)) |
| } |
| fmt.Fprintf(depfile, " %s", join(srcGoroot, "makefuchsia.go")) |
| |
| donePath := join(*flagRootGenDir, "goroot.done") |
| if err := ioutil.WriteFile(donePath, []byte("done"), 0664); err != nil { |
| log.Fatalf("writing goroot.done failed: %v", err) |
| } |
| } |