Go depfile generator
Change-Id: I7fe12a252762b024205699d4d8b752e082c69a11
diff --git a/go/src/godepfile/README.md b/go/src/godepfile/README.md
new file mode 100644
index 0000000..3ea3b88
--- /dev/null
+++ b/go/src/godepfile/README.md
@@ -0,0 +1,3 @@
+# godepfile
+
+Generates a depfile for a Go package that can be consumed by Ninja.
diff --git a/go/src/godepfile/godepfile.go b/go/src/godepfile/godepfile.go
new file mode 100644
index 0000000..6aa84b8
--- /dev/null
+++ b/go/src/godepfile/godepfile.go
@@ -0,0 +1,146 @@
+// Copyright 2016 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.
+
+// Generates a depfile for a Go package that can be consumed by Ninja.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "log"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+)
+
+type stringsFlag []string
+
+func (v *stringsFlag) String() string { return strings.Join(*v, " ") }
+
+func (v *stringsFlag) Set(s string) error {
+ *v = strings.Split(s, " ")
+ if *v == nil {
+ *v = []string{}
+ }
+ return nil
+}
+
+var (
+ ctx = build.Default
+ output string
+)
+
+func init() {
+ flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "build tags")
+ flag.StringVar(&output, "o", "", "name of the resulting executable")
+
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "usage: godepfile [packages]\n")
+ flag.PrintDefaults()
+ }
+}
+
+func main() {
+ flag.Parse()
+
+ if len(flag.Args()) == 0 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ var mu sync.Mutex
+ deps := make(map[string]bool)
+ paths := make(map[string]bool)
+
+ fdlimit := make(chan struct{}, 128)
+ var wg sync.WaitGroup
+ var scan func(path, srcDir string)
+ scan = func(path, srcDir string) {
+ defer wg.Done()
+
+ mu.Lock()
+ _, done := paths[path]
+ if !done {
+ paths[path] = true
+ }
+ mu.Unlock()
+
+ if done {
+ return
+ }
+
+ if path == "C" {
+ return
+ }
+
+ fdlimit <- struct{}{}
+ defer func() { <-fdlimit }()
+
+ pkg, err := ctx.Import(path, srcDir, 0)
+ if err != nil {
+ log.Fatalf("%s: %v", path, err)
+ }
+
+ var files []string
+ srcdir := pkg.Root + "/src/"
+ files = appendAndPrefix(files, srcdir, pkg.GoFiles)
+ files = appendAndPrefix(files, srcdir, pkg.CgoFiles)
+ files = appendAndPrefix(files, srcdir, pkg.CFiles)
+ files = appendAndPrefix(files, srcdir, pkg.CXXFiles)
+ files = appendAndPrefix(files, srcdir, pkg.HFiles)
+ files = appendAndPrefix(files, srcdir, pkg.SFiles)
+ files = appendAndPrefix(files, srcdir, pkg.SwigFiles)
+ files = appendAndPrefix(files, srcdir, pkg.SwigCXXFiles)
+
+ mu.Lock()
+ for _, file := range files {
+ deps[file] = true
+ }
+ mu.Unlock()
+
+ if pkg.Name == "main" && output == "" {
+ bindir := os.Getenv("GOBIN")
+ if bindir == "" {
+ bindir = pkg.BinDir
+ }
+ if ctx.GOOS == runtime.GOOS && ctx.GOARCH == runtime.GOARCH {
+ output = ctx.JoinPath(bindir, path)
+ } else {
+ output = ctx.JoinPath(bindir, ctx.GOOS+"_"+ctx.GOARCH, path)
+ }
+ }
+
+ for _, imp := range pkg.Imports {
+ wg.Add(1)
+ go scan(imp, pkg.Dir)
+ }
+ }
+
+ for _, root := range flag.Args() {
+ wg.Add(1)
+ go scan(root, "")
+ }
+ wg.Wait()
+
+ fmt.Printf("%s:", output)
+ var depnames []string
+ for path := range deps {
+ depnames = append(depnames, path)
+ }
+ sort.Strings(depnames)
+ for path, _ := range deps {
+ fmt.Printf(" %s", path)
+ }
+ fmt.Printf("\n")
+}
+
+func appendAndPrefix(slice []string, prefix string, src []string) []string {
+ for _, s := range src {
+ slice = append(slice, prefix+s)
+ }
+ return slice
+}