xref: /aosp_15_r20/build/blueprint/microfactory/microfactory.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Worker// Microfactory is a tool to incrementally compile a go program. It's similar
16*1fa6dee9SAndroid Build Coastguard Worker// to `go install`, but doesn't require a GOPATH. A package->path mapping can
17*1fa6dee9SAndroid Build Coastguard Worker// be specified as command line options:
18*1fa6dee9SAndroid Build Coastguard Worker//
19*1fa6dee9SAndroid Build Coastguard Worker//	-pkg-path android/soong=build/soong
20*1fa6dee9SAndroid Build Coastguard Worker//	-pkg-path github.com/google/blueprint=build/blueprint
21*1fa6dee9SAndroid Build Coastguard Worker//
22*1fa6dee9SAndroid Build Coastguard Worker// The paths can be relative to the current working directory, or an absolute
23*1fa6dee9SAndroid Build Coastguard Worker// path. Both packages and paths are compared with full directory names, so the
24*1fa6dee9SAndroid Build Coastguard Worker// android/soong-test package wouldn't be mapped in the above case.
25*1fa6dee9SAndroid Build Coastguard Worker//
26*1fa6dee9SAndroid Build Coastguard Worker// Microfactory will ignore *_test.go files, and limits *_darwin.go and
27*1fa6dee9SAndroid Build Coastguard Worker// *_linux.go files to MacOS and Linux respectively. It does not support build
28*1fa6dee9SAndroid Build Coastguard Worker// tags or any other suffixes.
29*1fa6dee9SAndroid Build Coastguard Worker//
30*1fa6dee9SAndroid Build Coastguard Worker// Builds are incremental by package. All input files are hashed, and if the
31*1fa6dee9SAndroid Build Coastguard Worker// hash of an input or dependency changes, the package is rebuilt.
32*1fa6dee9SAndroid Build Coastguard Worker//
33*1fa6dee9SAndroid Build Coastguard Worker// It also exposes the -trimpath option from go's compiler so that embedded
34*1fa6dee9SAndroid Build Coastguard Worker// path names (such as in log.Llongfile) are relative paths instead of absolute
35*1fa6dee9SAndroid Build Coastguard Worker// paths.
36*1fa6dee9SAndroid Build Coastguard Worker//
37*1fa6dee9SAndroid Build Coastguard Worker// If you don't have a previously built version of Microfactory, when used with
38*1fa6dee9SAndroid Build Coastguard Worker// -b <microfactory_bin_file>, Microfactory can rebuild itself as necessary.
39*1fa6dee9SAndroid Build Coastguard Worker// Combined with a shell script like microfactory.bash that uses `go run` to
40*1fa6dee9SAndroid Build Coastguard Worker// run Microfactory for the first time, go programs can be quickly bootstrapped
41*1fa6dee9SAndroid Build Coastguard Worker// entirely from source (and a standard go distribution).
42*1fa6dee9SAndroid Build Coastguard Workerpackage microfactory
43*1fa6dee9SAndroid Build Coastguard Worker
44*1fa6dee9SAndroid Build Coastguard Workerimport (
45*1fa6dee9SAndroid Build Coastguard Worker	"bytes"
46*1fa6dee9SAndroid Build Coastguard Worker	"crypto/sha1"
47*1fa6dee9SAndroid Build Coastguard Worker	"flag"
48*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
49*1fa6dee9SAndroid Build Coastguard Worker	"go/ast"
50*1fa6dee9SAndroid Build Coastguard Worker	"go/build"
51*1fa6dee9SAndroid Build Coastguard Worker	"go/parser"
52*1fa6dee9SAndroid Build Coastguard Worker	"go/token"
53*1fa6dee9SAndroid Build Coastguard Worker	"io"
54*1fa6dee9SAndroid Build Coastguard Worker	"io/ioutil"
55*1fa6dee9SAndroid Build Coastguard Worker	"os"
56*1fa6dee9SAndroid Build Coastguard Worker	"os/exec"
57*1fa6dee9SAndroid Build Coastguard Worker	"path/filepath"
58*1fa6dee9SAndroid Build Coastguard Worker	"runtime"
59*1fa6dee9SAndroid Build Coastguard Worker	"sort"
60*1fa6dee9SAndroid Build Coastguard Worker	"strconv"
61*1fa6dee9SAndroid Build Coastguard Worker	"strings"
62*1fa6dee9SAndroid Build Coastguard Worker	"sync"
63*1fa6dee9SAndroid Build Coastguard Worker	"syscall"
64*1fa6dee9SAndroid Build Coastguard Worker	"time"
65*1fa6dee9SAndroid Build Coastguard Worker)
66*1fa6dee9SAndroid Build Coastguard Worker
67*1fa6dee9SAndroid Build Coastguard Workervar (
68*1fa6dee9SAndroid Build Coastguard Worker	goToolDir = filepath.Join(runtime.GOROOT(), "pkg", "tool", runtime.GOOS+"_"+runtime.GOARCH)
69*1fa6dee9SAndroid Build Coastguard Worker	goVersion = findGoVersion()
70*1fa6dee9SAndroid Build Coastguard Worker	isGo18    = strings.Contains(goVersion, "go1.8")
71*1fa6dee9SAndroid Build Coastguard Worker	relGoRoot = runtime.GOROOT()
72*1fa6dee9SAndroid Build Coastguard Worker)
73*1fa6dee9SAndroid Build Coastguard Worker
74*1fa6dee9SAndroid Build Coastguard Workerfunc init() {
75*1fa6dee9SAndroid Build Coastguard Worker	// make the GoRoot relative
76*1fa6dee9SAndroid Build Coastguard Worker	if filepath.IsAbs(relGoRoot) {
77*1fa6dee9SAndroid Build Coastguard Worker		pwd, err := os.Getwd()
78*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
79*1fa6dee9SAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "failed to get the current directory: %s\n", err)
80*1fa6dee9SAndroid Build Coastguard Worker			return
81*1fa6dee9SAndroid Build Coastguard Worker		}
82*1fa6dee9SAndroid Build Coastguard Worker		relGoRoot, err = filepath.Rel(pwd, relGoRoot)
83*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
84*1fa6dee9SAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "failed to get the GOROOT relative path: %s\n", err)
85*1fa6dee9SAndroid Build Coastguard Worker			return
86*1fa6dee9SAndroid Build Coastguard Worker		}
87*1fa6dee9SAndroid Build Coastguard Worker	}
88*1fa6dee9SAndroid Build Coastguard Worker}
89*1fa6dee9SAndroid Build Coastguard Worker
90*1fa6dee9SAndroid Build Coastguard Workerfunc findGoVersion() string {
91*1fa6dee9SAndroid Build Coastguard Worker	if version, err := ioutil.ReadFile(filepath.Join(runtime.GOROOT(), "VERSION")); err == nil {
92*1fa6dee9SAndroid Build Coastguard Worker		return string(version)
93*1fa6dee9SAndroid Build Coastguard Worker	}
94*1fa6dee9SAndroid Build Coastguard Worker
95*1fa6dee9SAndroid Build Coastguard Worker	cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "version")
96*1fa6dee9SAndroid Build Coastguard Worker	if version, err := cmd.Output(); err == nil {
97*1fa6dee9SAndroid Build Coastguard Worker		return string(version)
98*1fa6dee9SAndroid Build Coastguard Worker	} else {
99*1fa6dee9SAndroid Build Coastguard Worker		panic(fmt.Sprintf("Unable to discover go version: %v", err))
100*1fa6dee9SAndroid Build Coastguard Worker	}
101*1fa6dee9SAndroid Build Coastguard Worker}
102*1fa6dee9SAndroid Build Coastguard Worker
103*1fa6dee9SAndroid Build Coastguard Workertype Config struct {
104*1fa6dee9SAndroid Build Coastguard Worker	Race    bool
105*1fa6dee9SAndroid Build Coastguard Worker	Verbose bool
106*1fa6dee9SAndroid Build Coastguard Worker
107*1fa6dee9SAndroid Build Coastguard Worker	TrimPath string
108*1fa6dee9SAndroid Build Coastguard Worker
109*1fa6dee9SAndroid Build Coastguard Worker	TraceFunc func(name string) func()
110*1fa6dee9SAndroid Build Coastguard Worker
111*1fa6dee9SAndroid Build Coastguard Worker	pkgs  []string
112*1fa6dee9SAndroid Build Coastguard Worker	paths map[string]string
113*1fa6dee9SAndroid Build Coastguard Worker}
114*1fa6dee9SAndroid Build Coastguard Worker
115*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Config) Map(pkgPrefix, pathPrefix string) error {
116*1fa6dee9SAndroid Build Coastguard Worker	if c.paths == nil {
117*1fa6dee9SAndroid Build Coastguard Worker		c.paths = make(map[string]string)
118*1fa6dee9SAndroid Build Coastguard Worker	}
119*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := c.paths[pkgPrefix]; ok {
120*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("Duplicate package prefix: %q", pkgPrefix)
121*1fa6dee9SAndroid Build Coastguard Worker	}
122*1fa6dee9SAndroid Build Coastguard Worker
123*1fa6dee9SAndroid Build Coastguard Worker	c.pkgs = append(c.pkgs, pkgPrefix)
124*1fa6dee9SAndroid Build Coastguard Worker	c.paths[pkgPrefix] = pathPrefix
125*1fa6dee9SAndroid Build Coastguard Worker
126*1fa6dee9SAndroid Build Coastguard Worker	return nil
127*1fa6dee9SAndroid Build Coastguard Worker}
128*1fa6dee9SAndroid Build Coastguard Worker
129*1fa6dee9SAndroid Build Coastguard Worker// Path takes a package name, applies the path mappings and returns the resulting path.
130*1fa6dee9SAndroid Build Coastguard Worker//
131*1fa6dee9SAndroid Build Coastguard Worker// If the package isn't mapped, we'll return false to prevent compilation attempts.
132*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Config) Path(pkg string) (string, bool, error) {
133*1fa6dee9SAndroid Build Coastguard Worker	if c == nil || c.paths == nil {
134*1fa6dee9SAndroid Build Coastguard Worker		return "", false, fmt.Errorf("No package mappings")
135*1fa6dee9SAndroid Build Coastguard Worker	}
136*1fa6dee9SAndroid Build Coastguard Worker
137*1fa6dee9SAndroid Build Coastguard Worker	for _, pkgPrefix := range c.pkgs {
138*1fa6dee9SAndroid Build Coastguard Worker		if pkg == pkgPrefix {
139*1fa6dee9SAndroid Build Coastguard Worker			return c.paths[pkgPrefix], true, nil
140*1fa6dee9SAndroid Build Coastguard Worker		} else if strings.HasPrefix(pkg, pkgPrefix+"/") {
141*1fa6dee9SAndroid Build Coastguard Worker			return filepath.Join(c.paths[pkgPrefix], strings.TrimPrefix(pkg, pkgPrefix+"/")), true, nil
142*1fa6dee9SAndroid Build Coastguard Worker		}
143*1fa6dee9SAndroid Build Coastguard Worker	}
144*1fa6dee9SAndroid Build Coastguard Worker
145*1fa6dee9SAndroid Build Coastguard Worker	return "", false, nil
146*1fa6dee9SAndroid Build Coastguard Worker}
147*1fa6dee9SAndroid Build Coastguard Worker
148*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Config) trace(format string, a ...interface{}) func() {
149*1fa6dee9SAndroid Build Coastguard Worker	if c.TraceFunc == nil {
150*1fa6dee9SAndroid Build Coastguard Worker		return func() {}
151*1fa6dee9SAndroid Build Coastguard Worker	}
152*1fa6dee9SAndroid Build Coastguard Worker	s := strings.TrimSpace(fmt.Sprintf(format, a...))
153*1fa6dee9SAndroid Build Coastguard Worker	return c.TraceFunc(s)
154*1fa6dee9SAndroid Build Coastguard Worker}
155*1fa6dee9SAndroid Build Coastguard Worker
156*1fa6dee9SAndroid Build Coastguard Workerfunc un(f func()) {
157*1fa6dee9SAndroid Build Coastguard Worker	f()
158*1fa6dee9SAndroid Build Coastguard Worker}
159*1fa6dee9SAndroid Build Coastguard Worker
160*1fa6dee9SAndroid Build Coastguard Workertype GoPackage struct {
161*1fa6dee9SAndroid Build Coastguard Worker	Name string
162*1fa6dee9SAndroid Build Coastguard Worker
163*1fa6dee9SAndroid Build Coastguard Worker	// Inputs
164*1fa6dee9SAndroid Build Coastguard Worker	directDeps []*GoPackage // specified directly by the module
165*1fa6dee9SAndroid Build Coastguard Worker	allDeps    []*GoPackage // direct dependencies and transitive dependencies
166*1fa6dee9SAndroid Build Coastguard Worker	files      []string
167*1fa6dee9SAndroid Build Coastguard Worker
168*1fa6dee9SAndroid Build Coastguard Worker	// Outputs
169*1fa6dee9SAndroid Build Coastguard Worker	pkgDir     string
170*1fa6dee9SAndroid Build Coastguard Worker	output     string
171*1fa6dee9SAndroid Build Coastguard Worker	hashResult []byte
172*1fa6dee9SAndroid Build Coastguard Worker
173*1fa6dee9SAndroid Build Coastguard Worker	// Status
174*1fa6dee9SAndroid Build Coastguard Worker	mutex    sync.Mutex
175*1fa6dee9SAndroid Build Coastguard Worker	compiled bool
176*1fa6dee9SAndroid Build Coastguard Worker	failed   error
177*1fa6dee9SAndroid Build Coastguard Worker	rebuilt  bool
178*1fa6dee9SAndroid Build Coastguard Worker}
179*1fa6dee9SAndroid Build Coastguard Worker
180*1fa6dee9SAndroid Build Coastguard Worker// LinkedHashMap<string, GoPackage>
181*1fa6dee9SAndroid Build Coastguard Workertype linkedDepSet struct {
182*1fa6dee9SAndroid Build Coastguard Worker	packageSet  map[string](*GoPackage)
183*1fa6dee9SAndroid Build Coastguard Worker	packageList []*GoPackage
184*1fa6dee9SAndroid Build Coastguard Worker}
185*1fa6dee9SAndroid Build Coastguard Worker
186*1fa6dee9SAndroid Build Coastguard Workerfunc newDepSet() *linkedDepSet {
187*1fa6dee9SAndroid Build Coastguard Worker	return &linkedDepSet{packageSet: make(map[string]*GoPackage)}
188*1fa6dee9SAndroid Build Coastguard Worker}
189*1fa6dee9SAndroid Build Coastguard Workerfunc (s *linkedDepSet) tryGetByName(name string) (*GoPackage, bool) {
190*1fa6dee9SAndroid Build Coastguard Worker	pkg, contained := s.packageSet[name]
191*1fa6dee9SAndroid Build Coastguard Worker	return pkg, contained
192*1fa6dee9SAndroid Build Coastguard Worker}
193*1fa6dee9SAndroid Build Coastguard Workerfunc (s *linkedDepSet) getByName(name string) *GoPackage {
194*1fa6dee9SAndroid Build Coastguard Worker	pkg, _ := s.tryGetByName(name)
195*1fa6dee9SAndroid Build Coastguard Worker	return pkg
196*1fa6dee9SAndroid Build Coastguard Worker}
197*1fa6dee9SAndroid Build Coastguard Workerfunc (s *linkedDepSet) add(name string, goPackage *GoPackage) {
198*1fa6dee9SAndroid Build Coastguard Worker	s.packageSet[name] = goPackage
199*1fa6dee9SAndroid Build Coastguard Worker	s.packageList = append(s.packageList, goPackage)
200*1fa6dee9SAndroid Build Coastguard Worker}
201*1fa6dee9SAndroid Build Coastguard Workerfunc (s *linkedDepSet) ignore(name string) {
202*1fa6dee9SAndroid Build Coastguard Worker	s.packageSet[name] = nil
203*1fa6dee9SAndroid Build Coastguard Worker}
204*1fa6dee9SAndroid Build Coastguard Worker
205*1fa6dee9SAndroid Build Coastguard Worker// FindDeps searches all applicable go files in `path`, parses all of them
206*1fa6dee9SAndroid Build Coastguard Worker// for import dependencies that exist in pkgMap, then recursively does the
207*1fa6dee9SAndroid Build Coastguard Worker// same for all of those dependencies.
208*1fa6dee9SAndroid Build Coastguard Workerfunc (p *GoPackage) FindDeps(config *Config, path string) error {
209*1fa6dee9SAndroid Build Coastguard Worker	defer un(config.trace("findDeps"))
210*1fa6dee9SAndroid Build Coastguard Worker
211*1fa6dee9SAndroid Build Coastguard Worker	depSet := newDepSet()
212*1fa6dee9SAndroid Build Coastguard Worker	err := p.findDeps(config, path, depSet)
213*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
214*1fa6dee9SAndroid Build Coastguard Worker		return err
215*1fa6dee9SAndroid Build Coastguard Worker	}
216*1fa6dee9SAndroid Build Coastguard Worker	p.allDeps = depSet.packageList
217*1fa6dee9SAndroid Build Coastguard Worker	return nil
218*1fa6dee9SAndroid Build Coastguard Worker}
219*1fa6dee9SAndroid Build Coastguard Worker
220*1fa6dee9SAndroid Build Coastguard Worker// Roughly equivalent to go/build.Context.match
221*1fa6dee9SAndroid Build Coastguard Workerfunc matchBuildTag(name string) bool {
222*1fa6dee9SAndroid Build Coastguard Worker	if name == "" {
223*1fa6dee9SAndroid Build Coastguard Worker		return false
224*1fa6dee9SAndroid Build Coastguard Worker	}
225*1fa6dee9SAndroid Build Coastguard Worker	if i := strings.Index(name, ","); i >= 0 {
226*1fa6dee9SAndroid Build Coastguard Worker		ok1 := matchBuildTag(name[:i])
227*1fa6dee9SAndroid Build Coastguard Worker		ok2 := matchBuildTag(name[i+1:])
228*1fa6dee9SAndroid Build Coastguard Worker		return ok1 && ok2
229*1fa6dee9SAndroid Build Coastguard Worker	}
230*1fa6dee9SAndroid Build Coastguard Worker	if strings.HasPrefix(name, "!!") {
231*1fa6dee9SAndroid Build Coastguard Worker		return false
232*1fa6dee9SAndroid Build Coastguard Worker	}
233*1fa6dee9SAndroid Build Coastguard Worker	if strings.HasPrefix(name, "!") {
234*1fa6dee9SAndroid Build Coastguard Worker		return len(name) > 1 && !matchBuildTag(name[1:])
235*1fa6dee9SAndroid Build Coastguard Worker	}
236*1fa6dee9SAndroid Build Coastguard Worker
237*1fa6dee9SAndroid Build Coastguard Worker	if name == runtime.GOOS || name == runtime.GOARCH || name == "gc" {
238*1fa6dee9SAndroid Build Coastguard Worker		return true
239*1fa6dee9SAndroid Build Coastguard Worker	}
240*1fa6dee9SAndroid Build Coastguard Worker	for _, tag := range build.Default.BuildTags {
241*1fa6dee9SAndroid Build Coastguard Worker		if tag == name {
242*1fa6dee9SAndroid Build Coastguard Worker			return true
243*1fa6dee9SAndroid Build Coastguard Worker		}
244*1fa6dee9SAndroid Build Coastguard Worker	}
245*1fa6dee9SAndroid Build Coastguard Worker	for _, tag := range build.Default.ReleaseTags {
246*1fa6dee9SAndroid Build Coastguard Worker		if tag == name {
247*1fa6dee9SAndroid Build Coastguard Worker			return true
248*1fa6dee9SAndroid Build Coastguard Worker		}
249*1fa6dee9SAndroid Build Coastguard Worker	}
250*1fa6dee9SAndroid Build Coastguard Worker
251*1fa6dee9SAndroid Build Coastguard Worker	return false
252*1fa6dee9SAndroid Build Coastguard Worker}
253*1fa6dee9SAndroid Build Coastguard Worker
254*1fa6dee9SAndroid Build Coastguard Workerfunc parseBuildComment(comment string) (matches, ok bool) {
255*1fa6dee9SAndroid Build Coastguard Worker	if !strings.HasPrefix(comment, "//") {
256*1fa6dee9SAndroid Build Coastguard Worker		return false, false
257*1fa6dee9SAndroid Build Coastguard Worker	}
258*1fa6dee9SAndroid Build Coastguard Worker	for i, c := range comment {
259*1fa6dee9SAndroid Build Coastguard Worker		if i < 2 || c == ' ' || c == '\t' {
260*1fa6dee9SAndroid Build Coastguard Worker			continue
261*1fa6dee9SAndroid Build Coastguard Worker		} else if c == '+' {
262*1fa6dee9SAndroid Build Coastguard Worker			f := strings.Fields(comment[i:])
263*1fa6dee9SAndroid Build Coastguard Worker			if f[0] == "+build" {
264*1fa6dee9SAndroid Build Coastguard Worker				matches = false
265*1fa6dee9SAndroid Build Coastguard Worker				for _, tok := range f[1:] {
266*1fa6dee9SAndroid Build Coastguard Worker					matches = matches || matchBuildTag(tok)
267*1fa6dee9SAndroid Build Coastguard Worker				}
268*1fa6dee9SAndroid Build Coastguard Worker				return matches, true
269*1fa6dee9SAndroid Build Coastguard Worker			}
270*1fa6dee9SAndroid Build Coastguard Worker		}
271*1fa6dee9SAndroid Build Coastguard Worker		break
272*1fa6dee9SAndroid Build Coastguard Worker	}
273*1fa6dee9SAndroid Build Coastguard Worker	return false, false
274*1fa6dee9SAndroid Build Coastguard Worker}
275*1fa6dee9SAndroid Build Coastguard Worker
276*1fa6dee9SAndroid Build Coastguard Worker// findDeps is the recursive version of FindDeps. allPackages is the map of
277*1fa6dee9SAndroid Build Coastguard Worker// all locally defined packages so that the same dependency of two different
278*1fa6dee9SAndroid Build Coastguard Worker// packages is only resolved once.
279*1fa6dee9SAndroid Build Coastguard Workerfunc (p *GoPackage) findDeps(config *Config, path string, allPackages *linkedDepSet) error {
280*1fa6dee9SAndroid Build Coastguard Worker	// If this ever becomes too slow, we can look at reading the files once instead of twice
281*1fa6dee9SAndroid Build Coastguard Worker	// But that just complicates things today, and we're already really fast.
282*1fa6dee9SAndroid Build Coastguard Worker	foundPkgs, err := parser.ParseDir(token.NewFileSet(), path, func(fi os.FileInfo) bool {
283*1fa6dee9SAndroid Build Coastguard Worker		name := fi.Name()
284*1fa6dee9SAndroid Build Coastguard Worker		if fi.IsDir() || strings.HasSuffix(name, "_test.go") || name[0] == '.' || name[0] == '_' {
285*1fa6dee9SAndroid Build Coastguard Worker			return false
286*1fa6dee9SAndroid Build Coastguard Worker		}
287*1fa6dee9SAndroid Build Coastguard Worker		if runtime.GOOS != "darwin" && strings.HasSuffix(name, "_darwin.go") {
288*1fa6dee9SAndroid Build Coastguard Worker			return false
289*1fa6dee9SAndroid Build Coastguard Worker		}
290*1fa6dee9SAndroid Build Coastguard Worker		if runtime.GOOS != "linux" && strings.HasSuffix(name, "_linux.go") {
291*1fa6dee9SAndroid Build Coastguard Worker			return false
292*1fa6dee9SAndroid Build Coastguard Worker		}
293*1fa6dee9SAndroid Build Coastguard Worker		return true
294*1fa6dee9SAndroid Build Coastguard Worker	}, parser.ImportsOnly|parser.ParseComments)
295*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
296*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("Error parsing directory %q: %v", path, err)
297*1fa6dee9SAndroid Build Coastguard Worker	}
298*1fa6dee9SAndroid Build Coastguard Worker
299*1fa6dee9SAndroid Build Coastguard Worker	var foundPkg *ast.Package
300*1fa6dee9SAndroid Build Coastguard Worker	// foundPkgs is a map[string]*ast.Package, but we only want one package
301*1fa6dee9SAndroid Build Coastguard Worker	if len(foundPkgs) != 1 {
302*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("Expected one package in %q, got %d", path, len(foundPkgs))
303*1fa6dee9SAndroid Build Coastguard Worker	}
304*1fa6dee9SAndroid Build Coastguard Worker	// Extract the first (and only) entry from the map.
305*1fa6dee9SAndroid Build Coastguard Worker	for _, pkg := range foundPkgs {
306*1fa6dee9SAndroid Build Coastguard Worker		foundPkg = pkg
307*1fa6dee9SAndroid Build Coastguard Worker	}
308*1fa6dee9SAndroid Build Coastguard Worker
309*1fa6dee9SAndroid Build Coastguard Worker	var deps []string
310*1fa6dee9SAndroid Build Coastguard Worker	localDeps := make(map[string]bool)
311*1fa6dee9SAndroid Build Coastguard Worker
312*1fa6dee9SAndroid Build Coastguard Worker	for filename, astFile := range foundPkg.Files {
313*1fa6dee9SAndroid Build Coastguard Worker		ignore := false
314*1fa6dee9SAndroid Build Coastguard Worker		for _, commentGroup := range astFile.Comments {
315*1fa6dee9SAndroid Build Coastguard Worker			for _, comment := range commentGroup.List {
316*1fa6dee9SAndroid Build Coastguard Worker				if matches, ok := parseBuildComment(comment.Text); ok && !matches {
317*1fa6dee9SAndroid Build Coastguard Worker					ignore = true
318*1fa6dee9SAndroid Build Coastguard Worker				}
319*1fa6dee9SAndroid Build Coastguard Worker			}
320*1fa6dee9SAndroid Build Coastguard Worker		}
321*1fa6dee9SAndroid Build Coastguard Worker		if ignore {
322*1fa6dee9SAndroid Build Coastguard Worker			continue
323*1fa6dee9SAndroid Build Coastguard Worker		}
324*1fa6dee9SAndroid Build Coastguard Worker
325*1fa6dee9SAndroid Build Coastguard Worker		p.files = append(p.files, filename)
326*1fa6dee9SAndroid Build Coastguard Worker
327*1fa6dee9SAndroid Build Coastguard Worker		for _, importSpec := range astFile.Imports {
328*1fa6dee9SAndroid Build Coastguard Worker			name, err := strconv.Unquote(importSpec.Path.Value)
329*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
330*1fa6dee9SAndroid Build Coastguard Worker				return fmt.Errorf("%s: invalid quoted string: <%s> %v", filename, importSpec.Path.Value, err)
331*1fa6dee9SAndroid Build Coastguard Worker			}
332*1fa6dee9SAndroid Build Coastguard Worker
333*1fa6dee9SAndroid Build Coastguard Worker			if pkg, ok := allPackages.tryGetByName(name); ok {
334*1fa6dee9SAndroid Build Coastguard Worker				if pkg != nil {
335*1fa6dee9SAndroid Build Coastguard Worker					if _, ok := localDeps[name]; !ok {
336*1fa6dee9SAndroid Build Coastguard Worker						deps = append(deps, name)
337*1fa6dee9SAndroid Build Coastguard Worker						localDeps[name] = true
338*1fa6dee9SAndroid Build Coastguard Worker					}
339*1fa6dee9SAndroid Build Coastguard Worker				}
340*1fa6dee9SAndroid Build Coastguard Worker				continue
341*1fa6dee9SAndroid Build Coastguard Worker			}
342*1fa6dee9SAndroid Build Coastguard Worker
343*1fa6dee9SAndroid Build Coastguard Worker			var pkgPath string
344*1fa6dee9SAndroid Build Coastguard Worker			if path, ok, err := config.Path(name); err != nil {
345*1fa6dee9SAndroid Build Coastguard Worker				return err
346*1fa6dee9SAndroid Build Coastguard Worker			} else if !ok {
347*1fa6dee9SAndroid Build Coastguard Worker				// Probably in the stdlib, but if not, then the compiler will fail with a reasonable error message
348*1fa6dee9SAndroid Build Coastguard Worker				// Mark it as such so that we don't try to decode its path again.
349*1fa6dee9SAndroid Build Coastguard Worker				allPackages.ignore(name)
350*1fa6dee9SAndroid Build Coastguard Worker				continue
351*1fa6dee9SAndroid Build Coastguard Worker			} else {
352*1fa6dee9SAndroid Build Coastguard Worker				pkgPath = path
353*1fa6dee9SAndroid Build Coastguard Worker			}
354*1fa6dee9SAndroid Build Coastguard Worker
355*1fa6dee9SAndroid Build Coastguard Worker			pkg := &GoPackage{
356*1fa6dee9SAndroid Build Coastguard Worker				Name: name,
357*1fa6dee9SAndroid Build Coastguard Worker			}
358*1fa6dee9SAndroid Build Coastguard Worker			deps = append(deps, name)
359*1fa6dee9SAndroid Build Coastguard Worker			allPackages.add(name, pkg)
360*1fa6dee9SAndroid Build Coastguard Worker			localDeps[name] = true
361*1fa6dee9SAndroid Build Coastguard Worker
362*1fa6dee9SAndroid Build Coastguard Worker			if err := pkg.findDeps(config, pkgPath, allPackages); err != nil {
363*1fa6dee9SAndroid Build Coastguard Worker				return err
364*1fa6dee9SAndroid Build Coastguard Worker			}
365*1fa6dee9SAndroid Build Coastguard Worker		}
366*1fa6dee9SAndroid Build Coastguard Worker	}
367*1fa6dee9SAndroid Build Coastguard Worker
368*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(p.files)
369*1fa6dee9SAndroid Build Coastguard Worker
370*1fa6dee9SAndroid Build Coastguard Worker	if config.Verbose {
371*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "Package %q depends on %v\n", p.Name, deps)
372*1fa6dee9SAndroid Build Coastguard Worker	}
373*1fa6dee9SAndroid Build Coastguard Worker
374*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(deps)
375*1fa6dee9SAndroid Build Coastguard Worker	for _, dep := range deps {
376*1fa6dee9SAndroid Build Coastguard Worker		p.directDeps = append(p.directDeps, allPackages.getByName(dep))
377*1fa6dee9SAndroid Build Coastguard Worker	}
378*1fa6dee9SAndroid Build Coastguard Worker
379*1fa6dee9SAndroid Build Coastguard Worker	return nil
380*1fa6dee9SAndroid Build Coastguard Worker}
381*1fa6dee9SAndroid Build Coastguard Worker
382*1fa6dee9SAndroid Build Coastguard Workerfunc (p *GoPackage) Compile(config *Config, outDir string) error {
383*1fa6dee9SAndroid Build Coastguard Worker	p.mutex.Lock()
384*1fa6dee9SAndroid Build Coastguard Worker	defer p.mutex.Unlock()
385*1fa6dee9SAndroid Build Coastguard Worker	if p.compiled {
386*1fa6dee9SAndroid Build Coastguard Worker		return p.failed
387*1fa6dee9SAndroid Build Coastguard Worker	}
388*1fa6dee9SAndroid Build Coastguard Worker	p.compiled = true
389*1fa6dee9SAndroid Build Coastguard Worker
390*1fa6dee9SAndroid Build Coastguard Worker	// Build all dependencies in parallel, then fail if any of them failed.
391*1fa6dee9SAndroid Build Coastguard Worker	var wg sync.WaitGroup
392*1fa6dee9SAndroid Build Coastguard Worker	for _, dep := range p.directDeps {
393*1fa6dee9SAndroid Build Coastguard Worker		wg.Add(1)
394*1fa6dee9SAndroid Build Coastguard Worker		go func(dep *GoPackage) {
395*1fa6dee9SAndroid Build Coastguard Worker			defer wg.Done()
396*1fa6dee9SAndroid Build Coastguard Worker			dep.Compile(config, outDir)
397*1fa6dee9SAndroid Build Coastguard Worker		}(dep)
398*1fa6dee9SAndroid Build Coastguard Worker	}
399*1fa6dee9SAndroid Build Coastguard Worker	wg.Wait()
400*1fa6dee9SAndroid Build Coastguard Worker	for _, dep := range p.directDeps {
401*1fa6dee9SAndroid Build Coastguard Worker		if dep.failed != nil {
402*1fa6dee9SAndroid Build Coastguard Worker			p.failed = dep.failed
403*1fa6dee9SAndroid Build Coastguard Worker			return p.failed
404*1fa6dee9SAndroid Build Coastguard Worker		}
405*1fa6dee9SAndroid Build Coastguard Worker	}
406*1fa6dee9SAndroid Build Coastguard Worker
407*1fa6dee9SAndroid Build Coastguard Worker	endTrace := config.trace("check compile %s", p.Name)
408*1fa6dee9SAndroid Build Coastguard Worker
409*1fa6dee9SAndroid Build Coastguard Worker	p.pkgDir = filepath.Join(outDir, strings.Replace(p.Name, "/", "-", -1))
410*1fa6dee9SAndroid Build Coastguard Worker	p.output = filepath.Join(p.pkgDir, p.Name) + ".a"
411*1fa6dee9SAndroid Build Coastguard Worker	shaFile := p.output + ".hash"
412*1fa6dee9SAndroid Build Coastguard Worker
413*1fa6dee9SAndroid Build Coastguard Worker	hash := sha1.New()
414*1fa6dee9SAndroid Build Coastguard Worker	fmt.Fprintln(hash, runtime.GOOS, runtime.GOARCH, goVersion)
415*1fa6dee9SAndroid Build Coastguard Worker
416*1fa6dee9SAndroid Build Coastguard Worker	cmd := exec.Command(filepath.Join(goToolDir, "compile"),
417*1fa6dee9SAndroid Build Coastguard Worker		"-N", "-l", // Disable optimization and inlining so that debugging works better
418*1fa6dee9SAndroid Build Coastguard Worker		"-o", p.output,
419*1fa6dee9SAndroid Build Coastguard Worker		"-p", p.Name,
420*1fa6dee9SAndroid Build Coastguard Worker		"-complete", "-pack", "-nolocalimports")
421*1fa6dee9SAndroid Build Coastguard Worker	cmd.Env = []string{
422*1fa6dee9SAndroid Build Coastguard Worker		"GOROOT=" + relGoRoot,
423*1fa6dee9SAndroid Build Coastguard Worker	}
424*1fa6dee9SAndroid Build Coastguard Worker	if !isGo18 && !config.Race {
425*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, "-c", fmt.Sprintf("%d", runtime.NumCPU()))
426*1fa6dee9SAndroid Build Coastguard Worker	}
427*1fa6dee9SAndroid Build Coastguard Worker	if config.Race {
428*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, "-race")
429*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(hash, "-race")
430*1fa6dee9SAndroid Build Coastguard Worker	}
431*1fa6dee9SAndroid Build Coastguard Worker	if config.TrimPath != "" {
432*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, "-trimpath", config.TrimPath)
433*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(hash, config.TrimPath)
434*1fa6dee9SAndroid Build Coastguard Worker	}
435*1fa6dee9SAndroid Build Coastguard Worker	for _, dep := range p.directDeps {
436*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, "-I", dep.pkgDir)
437*1fa6dee9SAndroid Build Coastguard Worker		hash.Write(dep.hashResult)
438*1fa6dee9SAndroid Build Coastguard Worker	}
439*1fa6dee9SAndroid Build Coastguard Worker	for _, filename := range p.files {
440*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, filename)
441*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(hash, filename)
442*1fa6dee9SAndroid Build Coastguard Worker
443*1fa6dee9SAndroid Build Coastguard Worker		// Hash the contents of the input files
444*1fa6dee9SAndroid Build Coastguard Worker		f, err := os.Open(filename)
445*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
446*1fa6dee9SAndroid Build Coastguard Worker			f.Close()
447*1fa6dee9SAndroid Build Coastguard Worker			err = fmt.Errorf("%s: %v", filename, err)
448*1fa6dee9SAndroid Build Coastguard Worker			p.failed = err
449*1fa6dee9SAndroid Build Coastguard Worker			return err
450*1fa6dee9SAndroid Build Coastguard Worker		}
451*1fa6dee9SAndroid Build Coastguard Worker		_, err = io.Copy(hash, f)
452*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
453*1fa6dee9SAndroid Build Coastguard Worker			f.Close()
454*1fa6dee9SAndroid Build Coastguard Worker			err = fmt.Errorf("%s: %v", filename, err)
455*1fa6dee9SAndroid Build Coastguard Worker			p.failed = err
456*1fa6dee9SAndroid Build Coastguard Worker			return err
457*1fa6dee9SAndroid Build Coastguard Worker		}
458*1fa6dee9SAndroid Build Coastguard Worker		f.Close()
459*1fa6dee9SAndroid Build Coastguard Worker	}
460*1fa6dee9SAndroid Build Coastguard Worker	p.hashResult = hash.Sum(nil)
461*1fa6dee9SAndroid Build Coastguard Worker
462*1fa6dee9SAndroid Build Coastguard Worker	var rebuild bool
463*1fa6dee9SAndroid Build Coastguard Worker	if _, err := os.Stat(p.output); err != nil {
464*1fa6dee9SAndroid Build Coastguard Worker		rebuild = true
465*1fa6dee9SAndroid Build Coastguard Worker	}
466*1fa6dee9SAndroid Build Coastguard Worker	if !rebuild {
467*1fa6dee9SAndroid Build Coastguard Worker		if oldSha, err := ioutil.ReadFile(shaFile); err == nil {
468*1fa6dee9SAndroid Build Coastguard Worker			rebuild = !bytes.Equal(oldSha, p.hashResult)
469*1fa6dee9SAndroid Build Coastguard Worker		} else {
470*1fa6dee9SAndroid Build Coastguard Worker			rebuild = true
471*1fa6dee9SAndroid Build Coastguard Worker		}
472*1fa6dee9SAndroid Build Coastguard Worker	}
473*1fa6dee9SAndroid Build Coastguard Worker
474*1fa6dee9SAndroid Build Coastguard Worker	endTrace()
475*1fa6dee9SAndroid Build Coastguard Worker	if !rebuild {
476*1fa6dee9SAndroid Build Coastguard Worker		return nil
477*1fa6dee9SAndroid Build Coastguard Worker	}
478*1fa6dee9SAndroid Build Coastguard Worker	defer un(config.trace("compile %s", p.Name))
479*1fa6dee9SAndroid Build Coastguard Worker
480*1fa6dee9SAndroid Build Coastguard Worker	err := os.RemoveAll(p.pkgDir)
481*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
482*1fa6dee9SAndroid Build Coastguard Worker		err = fmt.Errorf("%s: %v", p.Name, err)
483*1fa6dee9SAndroid Build Coastguard Worker		p.failed = err
484*1fa6dee9SAndroid Build Coastguard Worker		return err
485*1fa6dee9SAndroid Build Coastguard Worker	}
486*1fa6dee9SAndroid Build Coastguard Worker
487*1fa6dee9SAndroid Build Coastguard Worker	err = os.MkdirAll(filepath.Dir(p.output), 0777)
488*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
489*1fa6dee9SAndroid Build Coastguard Worker		err = fmt.Errorf("%s: %v", p.Name, err)
490*1fa6dee9SAndroid Build Coastguard Worker		p.failed = err
491*1fa6dee9SAndroid Build Coastguard Worker		return err
492*1fa6dee9SAndroid Build Coastguard Worker	}
493*1fa6dee9SAndroid Build Coastguard Worker
494*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stdin = nil
495*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stdout = os.Stdout
496*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stderr = os.Stderr
497*1fa6dee9SAndroid Build Coastguard Worker	if config.Verbose {
498*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, cmd.Args)
499*1fa6dee9SAndroid Build Coastguard Worker	}
500*1fa6dee9SAndroid Build Coastguard Worker	err = cmd.Run()
501*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
502*1fa6dee9SAndroid Build Coastguard Worker		commandText := strings.Join(cmd.Args, " ")
503*1fa6dee9SAndroid Build Coastguard Worker		err = fmt.Errorf("%q: %v", commandText, err)
504*1fa6dee9SAndroid Build Coastguard Worker		p.failed = err
505*1fa6dee9SAndroid Build Coastguard Worker		return err
506*1fa6dee9SAndroid Build Coastguard Worker	}
507*1fa6dee9SAndroid Build Coastguard Worker
508*1fa6dee9SAndroid Build Coastguard Worker	err = ioutil.WriteFile(shaFile, p.hashResult, 0666)
509*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
510*1fa6dee9SAndroid Build Coastguard Worker		err = fmt.Errorf("%s: %v", p.Name, err)
511*1fa6dee9SAndroid Build Coastguard Worker		p.failed = err
512*1fa6dee9SAndroid Build Coastguard Worker		return err
513*1fa6dee9SAndroid Build Coastguard Worker	}
514*1fa6dee9SAndroid Build Coastguard Worker
515*1fa6dee9SAndroid Build Coastguard Worker	p.rebuilt = true
516*1fa6dee9SAndroid Build Coastguard Worker
517*1fa6dee9SAndroid Build Coastguard Worker	return nil
518*1fa6dee9SAndroid Build Coastguard Worker}
519*1fa6dee9SAndroid Build Coastguard Worker
520*1fa6dee9SAndroid Build Coastguard Workerfunc (p *GoPackage) Link(config *Config, out string) error {
521*1fa6dee9SAndroid Build Coastguard Worker	if p.Name != "main" {
522*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("Can only link main package")
523*1fa6dee9SAndroid Build Coastguard Worker	}
524*1fa6dee9SAndroid Build Coastguard Worker	endTrace := config.trace("check link %s", p.Name)
525*1fa6dee9SAndroid Build Coastguard Worker
526*1fa6dee9SAndroid Build Coastguard Worker	shaFile := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+"_hash")
527*1fa6dee9SAndroid Build Coastguard Worker
528*1fa6dee9SAndroid Build Coastguard Worker	if !p.rebuilt {
529*1fa6dee9SAndroid Build Coastguard Worker		if _, err := os.Stat(out); err != nil {
530*1fa6dee9SAndroid Build Coastguard Worker			p.rebuilt = true
531*1fa6dee9SAndroid Build Coastguard Worker		} else if oldSha, err := ioutil.ReadFile(shaFile); err != nil {
532*1fa6dee9SAndroid Build Coastguard Worker			p.rebuilt = true
533*1fa6dee9SAndroid Build Coastguard Worker		} else {
534*1fa6dee9SAndroid Build Coastguard Worker			p.rebuilt = !bytes.Equal(oldSha, p.hashResult)
535*1fa6dee9SAndroid Build Coastguard Worker		}
536*1fa6dee9SAndroid Build Coastguard Worker	}
537*1fa6dee9SAndroid Build Coastguard Worker	endTrace()
538*1fa6dee9SAndroid Build Coastguard Worker	if !p.rebuilt {
539*1fa6dee9SAndroid Build Coastguard Worker		return nil
540*1fa6dee9SAndroid Build Coastguard Worker	}
541*1fa6dee9SAndroid Build Coastguard Worker	defer un(config.trace("link %s", p.Name))
542*1fa6dee9SAndroid Build Coastguard Worker
543*1fa6dee9SAndroid Build Coastguard Worker	err := os.Remove(shaFile)
544*1fa6dee9SAndroid Build Coastguard Worker	if err != nil && !os.IsNotExist(err) {
545*1fa6dee9SAndroid Build Coastguard Worker		return err
546*1fa6dee9SAndroid Build Coastguard Worker	}
547*1fa6dee9SAndroid Build Coastguard Worker	err = os.Remove(out)
548*1fa6dee9SAndroid Build Coastguard Worker	if err != nil && !os.IsNotExist(err) {
549*1fa6dee9SAndroid Build Coastguard Worker		return err
550*1fa6dee9SAndroid Build Coastguard Worker	}
551*1fa6dee9SAndroid Build Coastguard Worker
552*1fa6dee9SAndroid Build Coastguard Worker	cmd := exec.Command(filepath.Join(goToolDir, "link"), "-o", out)
553*1fa6dee9SAndroid Build Coastguard Worker	if config.Race {
554*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, "-race")
555*1fa6dee9SAndroid Build Coastguard Worker	}
556*1fa6dee9SAndroid Build Coastguard Worker	for _, dep := range p.allDeps {
557*1fa6dee9SAndroid Build Coastguard Worker		cmd.Args = append(cmd.Args, "-L", dep.pkgDir)
558*1fa6dee9SAndroid Build Coastguard Worker	}
559*1fa6dee9SAndroid Build Coastguard Worker	cmd.Args = append(cmd.Args, p.output)
560*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stdin = nil
561*1fa6dee9SAndroid Build Coastguard Worker	cmd.Env = []string{
562*1fa6dee9SAndroid Build Coastguard Worker		"GOROOT=" + relGoRoot,
563*1fa6dee9SAndroid Build Coastguard Worker	}
564*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stdout = os.Stdout
565*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stderr = os.Stderr
566*1fa6dee9SAndroid Build Coastguard Worker	if config.Verbose {
567*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, cmd.Args)
568*1fa6dee9SAndroid Build Coastguard Worker	}
569*1fa6dee9SAndroid Build Coastguard Worker	err = cmd.Run()
570*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
571*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("command %s failed with error %v", cmd.Args, err)
572*1fa6dee9SAndroid Build Coastguard Worker	}
573*1fa6dee9SAndroid Build Coastguard Worker
574*1fa6dee9SAndroid Build Coastguard Worker	return ioutil.WriteFile(shaFile, p.hashResult, 0666)
575*1fa6dee9SAndroid Build Coastguard Worker}
576*1fa6dee9SAndroid Build Coastguard Worker
577*1fa6dee9SAndroid Build Coastguard Workerfunc Build(config *Config, out, pkg string) (*GoPackage, error) {
578*1fa6dee9SAndroid Build Coastguard Worker	p := &GoPackage{
579*1fa6dee9SAndroid Build Coastguard Worker		Name: "main",
580*1fa6dee9SAndroid Build Coastguard Worker	}
581*1fa6dee9SAndroid Build Coastguard Worker
582*1fa6dee9SAndroid Build Coastguard Worker	lockFileName := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+".lock")
583*1fa6dee9SAndroid Build Coastguard Worker	lockFile, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0666)
584*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
585*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Error creating lock file (%q): %v", lockFileName, err)
586*1fa6dee9SAndroid Build Coastguard Worker	}
587*1fa6dee9SAndroid Build Coastguard Worker	defer lockFile.Close()
588*1fa6dee9SAndroid Build Coastguard Worker
589*1fa6dee9SAndroid Build Coastguard Worker	err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX)
590*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
591*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Error locking file (%q): %v", lockFileName, err)
592*1fa6dee9SAndroid Build Coastguard Worker	}
593*1fa6dee9SAndroid Build Coastguard Worker
594*1fa6dee9SAndroid Build Coastguard Worker	path, ok, err := config.Path(pkg)
595*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
596*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Error finding package %q for main: %v", pkg, err)
597*1fa6dee9SAndroid Build Coastguard Worker	}
598*1fa6dee9SAndroid Build Coastguard Worker	if !ok {
599*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Could not find package %q", pkg)
600*1fa6dee9SAndroid Build Coastguard Worker	}
601*1fa6dee9SAndroid Build Coastguard Worker
602*1fa6dee9SAndroid Build Coastguard Worker	intermediates := filepath.Join(filepath.Dir(out), "."+filepath.Base(out)+"_intermediates")
603*1fa6dee9SAndroid Build Coastguard Worker	if err := os.MkdirAll(intermediates, 0777); err != nil {
604*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Failed to create intermediates directory: %v", err)
605*1fa6dee9SAndroid Build Coastguard Worker	}
606*1fa6dee9SAndroid Build Coastguard Worker
607*1fa6dee9SAndroid Build Coastguard Worker	if err := p.FindDeps(config, path); err != nil {
608*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Failed to find deps of %v: %v", pkg, err)
609*1fa6dee9SAndroid Build Coastguard Worker	}
610*1fa6dee9SAndroid Build Coastguard Worker	if err := p.Compile(config, intermediates); err != nil {
611*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Failed to compile %v: %v", pkg, err)
612*1fa6dee9SAndroid Build Coastguard Worker	}
613*1fa6dee9SAndroid Build Coastguard Worker	if err := p.Link(config, out); err != nil {
614*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("Failed to link %v: %v", pkg, err)
615*1fa6dee9SAndroid Build Coastguard Worker	}
616*1fa6dee9SAndroid Build Coastguard Worker	return p, nil
617*1fa6dee9SAndroid Build Coastguard Worker}
618*1fa6dee9SAndroid Build Coastguard Worker
619*1fa6dee9SAndroid Build Coastguard Worker// rebuildMicrofactory checks to see if microfactory itself needs to be rebuilt,
620*1fa6dee9SAndroid Build Coastguard Worker// and if does, it will launch a new copy and return true. Otherwise it will return
621*1fa6dee9SAndroid Build Coastguard Worker// false to continue executing.
622*1fa6dee9SAndroid Build Coastguard Workerfunc rebuildMicrofactory(config *Config, mybin string) bool {
623*1fa6dee9SAndroid Build Coastguard Worker	if pkg, err := Build(config, mybin, "github.com/google/blueprint/microfactory/main"); err != nil {
624*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, err)
625*1fa6dee9SAndroid Build Coastguard Worker		os.Exit(1)
626*1fa6dee9SAndroid Build Coastguard Worker	} else if !pkg.rebuilt {
627*1fa6dee9SAndroid Build Coastguard Worker		return false
628*1fa6dee9SAndroid Build Coastguard Worker	}
629*1fa6dee9SAndroid Build Coastguard Worker
630*1fa6dee9SAndroid Build Coastguard Worker	cmd := exec.Command(mybin, os.Args[1:]...)
631*1fa6dee9SAndroid Build Coastguard Worker	cmd.Env = []string{
632*1fa6dee9SAndroid Build Coastguard Worker		"GOROOT=" + relGoRoot,
633*1fa6dee9SAndroid Build Coastguard Worker	}
634*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stdin = os.Stdin
635*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stdout = os.Stdout
636*1fa6dee9SAndroid Build Coastguard Worker	cmd.Stderr = os.Stderr
637*1fa6dee9SAndroid Build Coastguard Worker	if err := cmd.Run(); err == nil {
638*1fa6dee9SAndroid Build Coastguard Worker		return true
639*1fa6dee9SAndroid Build Coastguard Worker	} else if e, ok := err.(*exec.ExitError); ok {
640*1fa6dee9SAndroid Build Coastguard Worker		os.Exit(e.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())
641*1fa6dee9SAndroid Build Coastguard Worker	}
642*1fa6dee9SAndroid Build Coastguard Worker	os.Exit(1)
643*1fa6dee9SAndroid Build Coastguard Worker	return true
644*1fa6dee9SAndroid Build Coastguard Worker}
645*1fa6dee9SAndroid Build Coastguard Worker
646*1fa6dee9SAndroid Build Coastguard Workerfunc Main() {
647*1fa6dee9SAndroid Build Coastguard Worker	var output, mybin string
648*1fa6dee9SAndroid Build Coastguard Worker	var config Config
649*1fa6dee9SAndroid Build Coastguard Worker	pkgMap := pkgPathMappingVar{&config}
650*1fa6dee9SAndroid Build Coastguard Worker
651*1fa6dee9SAndroid Build Coastguard Worker	flags := flag.NewFlagSet("", flag.ExitOnError)
652*1fa6dee9SAndroid Build Coastguard Worker	flags.BoolVar(&config.Race, "race", false, "enable data race detection.")
653*1fa6dee9SAndroid Build Coastguard Worker	flags.BoolVar(&config.Verbose, "v", false, "Verbose")
654*1fa6dee9SAndroid Build Coastguard Worker	flags.StringVar(&output, "o", "", "Output file")
655*1fa6dee9SAndroid Build Coastguard Worker	flags.StringVar(&mybin, "b", "", "Microfactory binary location")
656*1fa6dee9SAndroid Build Coastguard Worker	flags.StringVar(&config.TrimPath, "trimpath", "", "remove prefix from recorded source file paths")
657*1fa6dee9SAndroid Build Coastguard Worker	flags.Var(&pkgMap, "pkg-path", "Mapping of package prefixes to file paths")
658*1fa6dee9SAndroid Build Coastguard Worker	err := flags.Parse(os.Args[1:])
659*1fa6dee9SAndroid Build Coastguard Worker
660*1fa6dee9SAndroid Build Coastguard Worker	if err == flag.ErrHelp || flags.NArg() != 1 || output == "" {
661*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "-o out/binary <main-package>")
662*1fa6dee9SAndroid Build Coastguard Worker		flags.PrintDefaults()
663*1fa6dee9SAndroid Build Coastguard Worker		os.Exit(1)
664*1fa6dee9SAndroid Build Coastguard Worker	}
665*1fa6dee9SAndroid Build Coastguard Worker
666*1fa6dee9SAndroid Build Coastguard Worker	tracePath := filepath.Join(filepath.Dir(output), "."+filepath.Base(output)+".trace")
667*1fa6dee9SAndroid Build Coastguard Worker	if traceFile, err := os.OpenFile(tracePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666); err == nil {
668*1fa6dee9SAndroid Build Coastguard Worker		defer traceFile.Close()
669*1fa6dee9SAndroid Build Coastguard Worker		config.TraceFunc = func(name string) func() {
670*1fa6dee9SAndroid Build Coastguard Worker			fmt.Fprintf(traceFile, "%d B %s\n", time.Now().UnixNano()/1000, name)
671*1fa6dee9SAndroid Build Coastguard Worker			return func() {
672*1fa6dee9SAndroid Build Coastguard Worker				fmt.Fprintf(traceFile, "%d E %s\n", time.Now().UnixNano()/1000, name)
673*1fa6dee9SAndroid Build Coastguard Worker			}
674*1fa6dee9SAndroid Build Coastguard Worker		}
675*1fa6dee9SAndroid Build Coastguard Worker	}
676*1fa6dee9SAndroid Build Coastguard Worker	if executable, err := os.Executable(); err == nil {
677*1fa6dee9SAndroid Build Coastguard Worker		defer un(config.trace("microfactory %s", executable))
678*1fa6dee9SAndroid Build Coastguard Worker	} else {
679*1fa6dee9SAndroid Build Coastguard Worker		defer un(config.trace("microfactory <unknown>"))
680*1fa6dee9SAndroid Build Coastguard Worker	}
681*1fa6dee9SAndroid Build Coastguard Worker
682*1fa6dee9SAndroid Build Coastguard Worker	if mybin != "" {
683*1fa6dee9SAndroid Build Coastguard Worker		if rebuildMicrofactory(&config, mybin) {
684*1fa6dee9SAndroid Build Coastguard Worker			return
685*1fa6dee9SAndroid Build Coastguard Worker		}
686*1fa6dee9SAndroid Build Coastguard Worker	}
687*1fa6dee9SAndroid Build Coastguard Worker
688*1fa6dee9SAndroid Build Coastguard Worker	if _, err := Build(&config, output, flags.Arg(0)); err != nil {
689*1fa6dee9SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, err)
690*1fa6dee9SAndroid Build Coastguard Worker		os.Exit(1)
691*1fa6dee9SAndroid Build Coastguard Worker	}
692*1fa6dee9SAndroid Build Coastguard Worker}
693*1fa6dee9SAndroid Build Coastguard Worker
694*1fa6dee9SAndroid Build Coastguard Worker// pkgPathMapping can be used with flag.Var to parse -pkg-path arguments of
695*1fa6dee9SAndroid Build Coastguard Worker// <package-prefix>=<path-prefix> mappings.
696*1fa6dee9SAndroid Build Coastguard Workertype pkgPathMappingVar struct{ *Config }
697*1fa6dee9SAndroid Build Coastguard Worker
698*1fa6dee9SAndroid Build Coastguard Workerfunc (pkgPathMappingVar) String() string {
699*1fa6dee9SAndroid Build Coastguard Worker	return "<package-prefix>=<path-prefix>"
700*1fa6dee9SAndroid Build Coastguard Worker}
701*1fa6dee9SAndroid Build Coastguard Worker
702*1fa6dee9SAndroid Build Coastguard Workerfunc (p *pkgPathMappingVar) Set(value string) error {
703*1fa6dee9SAndroid Build Coastguard Worker	equalPos := strings.Index(value, "=")
704*1fa6dee9SAndroid Build Coastguard Worker	if equalPos == -1 {
705*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("Argument must be in the form of: %q", p.String())
706*1fa6dee9SAndroid Build Coastguard Worker	}
707*1fa6dee9SAndroid Build Coastguard Worker
708*1fa6dee9SAndroid Build Coastguard Worker	pkgPrefix := strings.TrimSuffix(value[:equalPos], "/")
709*1fa6dee9SAndroid Build Coastguard Worker	pathPrefix := strings.TrimSuffix(value[equalPos+1:], "/")
710*1fa6dee9SAndroid Build Coastguard Worker
711*1fa6dee9SAndroid Build Coastguard Worker	return p.Map(pkgPrefix, pathPrefix)
712*1fa6dee9SAndroid Build Coastguard Worker}
713