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