Source file src/pkg/go/build/build.go
1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package build 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "go/ast" 12 "go/doc" 13 "go/parser" 14 "go/token" 15 "io" 16 "io/ioutil" 17 "log" 18 "os" 19 pathpkg "path" 20 "path/filepath" 21 "runtime" 22 "sort" 23 "strconv" 24 "strings" 25 "unicode" 26 ) 27 28 // A Context specifies the supporting context for a build. 29 type Context struct { 30 GOARCH string // target architecture 31 GOOS string // target operating system 32 GOROOT string // Go root 33 GOPATH string // Go path 34 CgoEnabled bool // whether cgo can be used 35 BuildTags []string // additional tags to recognize in +build lines 36 UseAllFiles bool // use files regardless of +build lines, file names 37 Compiler string // compiler to assume when computing target paths 38 39 // By default, Import uses the operating system's file system calls 40 // to read directories and files. To read from other sources, 41 // callers can set the following functions. They all have default 42 // behaviors that use the local file system, so clients need only set 43 // the functions whose behaviors they wish to change. 44 45 // JoinPath joins the sequence of path fragments into a single path. 46 // If JoinPath is nil, Import uses filepath.Join. 47 JoinPath func(elem ...string) string 48 49 // SplitPathList splits the path list into a slice of individual paths. 50 // If SplitPathList is nil, Import uses filepath.SplitList. 51 SplitPathList func(list string) []string 52 53 // IsAbsPath reports whether path is an absolute path. 54 // If IsAbsPath is nil, Import uses filepath.IsAbs. 55 IsAbsPath func(path string) bool 56 57 // IsDir reports whether the path names a directory. 58 // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method. 59 IsDir func(path string) bool 60 61 // HasSubdir reports whether dir is a subdirectory of 62 // (perhaps multiple levels below) root. 63 // If so, HasSubdir sets rel to a slash-separated path that 64 // can be joined to root to produce a path equivalent to dir. 65 // If HasSubdir is nil, Import uses an implementation built on 66 // filepath.EvalSymlinks. 67 HasSubdir func(root, dir string) (rel string, ok bool) 68 69 // ReadDir returns a slice of os.FileInfo, sorted by Name, 70 // describing the content of the named directory. 71 // If ReadDir is nil, Import uses io.ReadDir. 72 ReadDir func(dir string) (fi []os.FileInfo, err error) 73 74 // OpenFile opens a file (not a directory) for reading. 75 // If OpenFile is nil, Import uses os.Open. 76 OpenFile func(path string) (r io.ReadCloser, err error) 77 } 78 79 // joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join. 80 func (ctxt *Context) joinPath(elem ...string) string { 81 if f := ctxt.JoinPath; f != nil { 82 return f(elem...) 83 } 84 return filepath.Join(elem...) 85 } 86 87 // splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList. 88 func (ctxt *Context) splitPathList(s string) []string { 89 if f := ctxt.SplitPathList; f != nil { 90 return f(s) 91 } 92 return filepath.SplitList(s) 93 } 94 95 // isAbsPath calls ctxt.IsAbsSPath (if not nil) or else filepath.IsAbs. 96 func (ctxt *Context) isAbsPath(path string) bool { 97 if f := ctxt.IsAbsPath; f != nil { 98 return f(path) 99 } 100 return filepath.IsAbs(path) 101 } 102 103 // isDir calls ctxt.IsDir (if not nil) or else uses os.Stat. 104 func (ctxt *Context) isDir(path string) bool { 105 if f := ctxt.IsDir; f != nil { 106 return f(path) 107 } 108 fi, err := os.Stat(path) 109 return err == nil && fi.IsDir() 110 } 111 112 // hasSubdir calls ctxt.HasSubdir (if not nil) or else uses 113 // the local file system to answer the question. 114 func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) { 115 if f := ctxt.HasSubdir; f != nil { 116 return f(root, dir) 117 } 118 119 if p, err := filepath.EvalSymlinks(root); err == nil { 120 root = p 121 } 122 if p, err := filepath.EvalSymlinks(dir); err == nil { 123 dir = p 124 } 125 const sep = string(filepath.Separator) 126 root = filepath.Clean(root) 127 if !strings.HasSuffix(root, sep) { 128 root += sep 129 } 130 dir = filepath.Clean(dir) 131 if !strings.HasPrefix(dir, root) { 132 return "", false 133 } 134 return filepath.ToSlash(dir[len(root):]), true 135 } 136 137 // readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir. 138 func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) { 139 if f := ctxt.ReadDir; f != nil { 140 return f(path) 141 } 142 return ioutil.ReadDir(path) 143 } 144 145 // openFile calls ctxt.OpenFile (if not nil) or else os.Open. 146 func (ctxt *Context) openFile(path string) (io.ReadCloser, error) { 147 if fn := ctxt.OpenFile; fn != nil { 148 return fn(path) 149 } 150 151 f, err := os.Open(path) 152 if err != nil { 153 return nil, err // nil interface 154 } 155 return f, nil 156 } 157 158 // isFile determines whether path is a file by trying to open it. 159 // It reuses openFile instead of adding another function to the 160 // list in Context. 161 func (ctxt *Context) isFile(path string) bool { 162 f, err := ctxt.openFile(path) 163 if err != nil { 164 return false 165 } 166 f.Close() 167 return true 168 } 169 170 // gopath returns the list of Go path directories. 171 func (ctxt *Context) gopath() []string { 172 var all []string 173 for _, p := range ctxt.splitPathList(ctxt.GOPATH) { 174 if p == "" || p == ctxt.GOROOT { 175 // Empty paths are uninteresting. 176 // If the path is the GOROOT, ignore it. 177 // People sometimes set GOPATH=$GOROOT, which is useless 178 // but would cause us to find packages with import paths 179 // like "pkg/math". 180 // Do not get confused by this common mistake. 181 continue 182 } 183 all = append(all, p) 184 } 185 return all 186 } 187 188 // SrcDirs returns a list of package source root directories. 189 // It draws from the current Go root and Go path but omits directories 190 // that do not exist. 191 func (ctxt *Context) SrcDirs() []string { 192 var all []string 193 if ctxt.GOROOT != "" { 194 dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg") 195 if ctxt.isDir(dir) { 196 all = append(all, dir) 197 } 198 } 199 for _, p := range ctxt.gopath() { 200 dir := ctxt.joinPath(p, "src") 201 if ctxt.isDir(dir) { 202 all = append(all, dir) 203 } 204 } 205 return all 206 } 207 208 // Default is the default Context for builds. 209 // It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables 210 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT. 211 var Default Context = defaultContext() 212 213 var cgoEnabled = map[string]bool{ 214 "darwin/386": true, 215 "darwin/amd64": true, 216 "linux/386": true, 217 "linux/amd64": true, 218 "freebsd/386": true, 219 "freebsd/amd64": true, 220 "windows/386": true, 221 "windows/amd64": true, 222 } 223 224 func defaultContext() Context { 225 var c Context 226 227 c.GOARCH = envOr("GOARCH", runtime.GOARCH) 228 c.GOOS = envOr("GOOS", runtime.GOOS) 229 c.GOROOT = runtime.GOROOT() 230 c.GOPATH = envOr("GOPATH", "") 231 c.Compiler = runtime.Compiler 232 233 switch os.Getenv("CGO_ENABLED") { 234 case "1": 235 c.CgoEnabled = true 236 case "0": 237 c.CgoEnabled = false 238 default: 239 c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH] 240 } 241 242 return c 243 } 244 245 func envOr(name, def string) string { 246 s := os.Getenv(name) 247 if s == "" { 248 return def 249 } 250 return s 251 } 252 253 // An ImportMode controls the behavior of the Import method. 254 type ImportMode uint 255 256 const ( 257 // If FindOnly is set, Import stops after locating the directory 258 // that should contain the sources for a package. It does not 259 // read any files in the directory. 260 FindOnly ImportMode = 1 << iota 261 262 // If AllowBinary is set, Import can be satisfied by a compiled 263 // package object without corresponding sources. 264 AllowBinary 265 ) 266 267 // A Package describes the Go package found in a directory. 268 type Package struct { 269 Dir string // directory containing package sources 270 Name string // package name 271 Doc string // documentation synopsis 272 ImportPath string // import path of package ("" if unknown) 273 Root string // root of Go tree where this package lives 274 SrcRoot string // package source root directory ("" if unknown) 275 PkgRoot string // package install root directory ("" if unknown) 276 BinDir string // command install directory ("" if unknown) 277 Goroot bool // package found in Go root 278 PkgObj string // installed .a file 279 280 // Source files 281 GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) 282 CgoFiles []string // .go source files that import "C" 283 CFiles []string // .c source files 284 HFiles []string // .h source files 285 SFiles []string // .s source files 286 SysoFiles []string // .syso system object files to add to archive 287 288 // Cgo directives 289 CgoPkgConfig []string // Cgo pkg-config directives 290 CgoCFLAGS []string // Cgo CFLAGS directives 291 CgoLDFLAGS []string // Cgo LDFLAGS directives 292 293 // Dependency information 294 Imports []string // imports from GoFiles, CgoFiles 295 ImportPos map[string][]token.Position // line information for Imports 296 297 // Test information 298 TestGoFiles []string // _test.go files in package 299 TestImports []string // imports from TestGoFiles 300 TestImportPos map[string][]token.Position // line information for TestImports 301 XTestGoFiles []string // _test.go files outside package 302 XTestImports []string // imports from XTestGoFiles 303 XTestImportPos map[string][]token.Position // line information for XTestImports 304 } 305 306 // IsCommand reports whether the package is considered a 307 // command to be installed (not just a library). 308 // Packages named "main" are treated as commands. 309 func (p *Package) IsCommand() bool { 310 return p.Name == "main" 311 } 312 313 // ImportDir is like Import but processes the Go package found in 314 // the named directory. 315 func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) { 316 return ctxt.Import(".", dir, mode) 317 } 318 319 // NoGoError is the error used by Import to describe a directory 320 // containing no Go source files. 321 type NoGoError struct { 322 Dir string 323 } 324 325 func (e *NoGoError) Error() string { 326 return "no Go source files in " + e.Dir 327 } 328 329 // Import returns details about the Go package named by the import path, 330 // interpreting local import paths relative to the srcDir directory. 331 // If the path is a local import path naming a package that can be imported 332 // using a standard import path, the returned package will set p.ImportPath 333 // to that path. 334 // 335 // In the directory containing the package, .go, .c, .h, and .s files are 336 // considered part of the package except for: 337 // 338 // - .go files in package documentation 339 // - files starting with _ or . (likely editor temporary files) 340 // - files with build constraints not satisfied by the context 341 // 342 // If an error occurs, Import returns a non-nil error also returns a non-nil 343 // *Package containing partial information. 344 // 345 func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) { 346 p := &Package{ 347 ImportPath: path, 348 } 349 350 var pkga string 351 var pkgerr error 352 switch ctxt.Compiler { 353 case "gccgo": 354 dir, elem := pathpkg.Split(p.ImportPath) 355 pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a" 356 case "gc": 357 pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + p.ImportPath + ".a" 358 default: 359 // Save error for end of function. 360 pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler) 361 } 362 363 binaryOnly := false 364 if IsLocalImport(path) { 365 pkga = "" // local imports have no installed path 366 if srcDir == "" { 367 return p, fmt.Errorf("import %q: import relative to unknown directory", path) 368 } 369 if !ctxt.isAbsPath(path) { 370 p.Dir = ctxt.joinPath(srcDir, path) 371 } 372 // Determine canonical import path, if any. 373 if ctxt.GOROOT != "" { 374 root := ctxt.joinPath(ctxt.GOROOT, "src", "pkg") 375 if sub, ok := ctxt.hasSubdir(root, p.Dir); ok { 376 p.Goroot = true 377 p.ImportPath = sub 378 p.Root = ctxt.GOROOT 379 goto Found 380 } 381 } 382 all := ctxt.gopath() 383 for i, root := range all { 384 rootsrc := ctxt.joinPath(root, "src") 385 if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok { 386 // We found a potential import path for dir, 387 // but check that using it wouldn't find something 388 // else first. 389 if ctxt.GOROOT != "" { 390 if dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", sub); ctxt.isDir(dir) { 391 goto Found 392 } 393 } 394 for _, earlyRoot := range all[:i] { 395 if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) { 396 goto Found 397 } 398 } 399 400 // sub would not name some other directory instead of this one. 401 // Record it. 402 p.ImportPath = sub 403 p.Root = root 404 goto Found 405 } 406 } 407 // It's okay that we didn't find a root containing dir. 408 // Keep going with the information we have. 409 } else { 410 if strings.HasPrefix(path, "/") { 411 return p, fmt.Errorf("import %q: cannot import absolute path", path) 412 } 413 // Determine directory from import path. 414 if ctxt.GOROOT != "" { 415 dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path) 416 isDir := ctxt.isDir(dir) 417 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) 418 if isDir || binaryOnly { 419 p.Dir = dir 420 p.Goroot = true 421 p.Root = ctxt.GOROOT 422 goto Found 423 } 424 } 425 for _, root := range ctxt.gopath() { 426 dir := ctxt.joinPath(root, "src", path) 427 isDir := ctxt.isDir(dir) 428 binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga)) 429 if isDir || binaryOnly { 430 p.Dir = dir 431 p.Root = root 432 goto Found 433 } 434 } 435 return p, fmt.Errorf("import %q: cannot find package", path) 436 } 437 438 Found: 439 if p.Root != "" { 440 if p.Goroot { 441 p.SrcRoot = ctxt.joinPath(p.Root, "src", "pkg") 442 } else { 443 p.SrcRoot = ctxt.joinPath(p.Root, "src") 444 } 445 p.PkgRoot = ctxt.joinPath(p.Root, "pkg") 446 p.BinDir = ctxt.joinPath(p.Root, "bin") 447 if pkga != "" { 448 p.PkgObj = ctxt.joinPath(p.Root, pkga) 449 } 450 } 451 452 if mode&FindOnly != 0 { 453 return p, pkgerr 454 } 455 if binaryOnly && (mode&AllowBinary) != 0 { 456 return p, pkgerr 457 } 458 459 dirs, err := ctxt.readDir(p.Dir) 460 if err != nil { 461 return p, err 462 } 463 464 var Sfiles []string // files with ".S" (capital S) 465 var firstFile string 466 imported := make(map[string][]token.Position) 467 testImported := make(map[string][]token.Position) 468 xTestImported := make(map[string][]token.Position) 469 fset := token.NewFileSet() 470 for _, d := range dirs { 471 if d.IsDir() { 472 continue 473 } 474 name := d.Name() 475 if strings.HasPrefix(name, "_") || 476 strings.HasPrefix(name, ".") { 477 continue 478 } 479 if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) { 480 continue 481 } 482 483 i := strings.LastIndex(name, ".") 484 if i < 0 { 485 i = len(name) 486 } 487 ext := name[i:] 488 switch ext { 489 case ".go", ".c", ".s", ".h", ".S": 490 // tentatively okay - read to make sure 491 case ".syso": 492 // binary objects to add to package archive 493 // Likely of the form foo_windows.syso, but 494 // the name was vetted above with goodOSArchFile. 495 p.SysoFiles = append(p.SysoFiles, name) 496 continue 497 default: 498 // skip 499 continue 500 } 501 502 filename := ctxt.joinPath(p.Dir, name) 503 f, err := ctxt.openFile(filename) 504 if err != nil { 505 return p, err 506 } 507 data, err := ioutil.ReadAll(f) 508 f.Close() 509 if err != nil { 510 return p, fmt.Errorf("read %s: %v", filename, err) 511 } 512 513 // Look for +build comments to accept or reject the file. 514 if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) { 515 continue 516 } 517 518 // Going to save the file. For non-Go files, can stop here. 519 switch ext { 520 case ".c": 521 p.CFiles = append(p.CFiles, name) 522 continue 523 case ".h": 524 p.HFiles = append(p.HFiles, name) 525 continue 526 case ".s": 527 p.SFiles = append(p.SFiles, name) 528 continue 529 case ".S": 530 Sfiles = append(Sfiles, name) 531 continue 532 } 533 534 pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) 535 if err != nil { 536 return p, err 537 } 538 539 pkg := string(pf.Name.Name) 540 if pkg == "documentation" { 541 continue 542 } 543 544 isTest := strings.HasSuffix(name, "_test.go") 545 isXTest := false 546 if isTest && strings.HasSuffix(pkg, "_test") { 547 isXTest = true 548 pkg = pkg[:len(pkg)-len("_test")] 549 } 550 551 if p.Name == "" { 552 p.Name = pkg 553 firstFile = name 554 } else if pkg != p.Name { 555 return p, fmt.Errorf("found packages %s (%s) and %s (%s) in %s", p.Name, firstFile, pkg, name, p.Dir) 556 } 557 if pf.Doc != nil && p.Doc == "" { 558 p.Doc = doc.Synopsis(pf.Doc.Text()) 559 } 560 561 // Record imports and information about cgo. 562 isCgo := false 563 for _, decl := range pf.Decls { 564 d, ok := decl.(*ast.GenDecl) 565 if !ok { 566 continue 567 } 568 for _, dspec := range d.Specs { 569 spec, ok := dspec.(*ast.ImportSpec) 570 if !ok { 571 continue 572 } 573 quoted := string(spec.Path.Value) 574 path, err := strconv.Unquote(quoted) 575 if err != nil { 576 log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) 577 } 578 if isXTest { 579 xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos())) 580 } else if isTest { 581 testImported[path] = append(testImported[path], fset.Position(spec.Pos())) 582 } else { 583 imported[path] = append(imported[path], fset.Position(spec.Pos())) 584 } 585 if path == "C" { 586 if isTest { 587 return p, fmt.Errorf("use of cgo in test %s not supported", filename) 588 } 589 cg := spec.Doc 590 if cg == nil && len(d.Specs) == 1 { 591 cg = d.Doc 592 } 593 if cg != nil { 594 if err := ctxt.saveCgo(filename, p, cg); err != nil { 595 return p, err 596 } 597 } 598 isCgo = true 599 } 600 } 601 } 602 if isCgo { 603 if ctxt.CgoEnabled { 604 p.CgoFiles = append(p.CgoFiles, name) 605 } 606 } else if isXTest { 607 p.XTestGoFiles = append(p.XTestGoFiles, name) 608 } else if isTest { 609 p.TestGoFiles = append(p.TestGoFiles, name) 610 } else { 611 p.GoFiles = append(p.GoFiles, name) 612 } 613 } 614 if p.Name == "" { 615 return p, &NoGoError{p.Dir} 616 } 617 618 p.Imports, p.ImportPos = cleanImports(imported) 619 p.TestImports, p.TestImportPos = cleanImports(testImported) 620 p.XTestImports, p.XTestImportPos = cleanImports(xTestImported) 621 622 // add the .S files only if we are using cgo 623 // (which means gcc will compile them). 624 // The standard assemblers expect .s files. 625 if len(p.CgoFiles) > 0 { 626 p.SFiles = append(p.SFiles, Sfiles...) 627 sort.Strings(p.SFiles) 628 } 629 630 return p, pkgerr 631 } 632 633 func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) { 634 all := make([]string, 0, len(m)) 635 for path := range m { 636 all = append(all, path) 637 } 638 sort.Strings(all) 639 return all, m 640 } 641 642 // Import is shorthand for Default.Import. 643 func Import(path, srcDir string, mode ImportMode) (*Package, error) { 644 return Default.Import(path, srcDir, mode) 645 } 646 647 // ImportDir is shorthand for Default.ImportDir. 648 func ImportDir(dir string, mode ImportMode) (*Package, error) { 649 return Default.ImportDir(dir, mode) 650 } 651 652 var slashslash = []byte("//") 653 654 // shouldBuild reports whether it is okay to use this file, 655 // The rule is that in the file's leading run of // comments 656 // and blank lines, which must be followed by a blank line 657 // (to avoid including a Go package clause doc comment), 658 // lines beginning with '// +build' are taken as build directives. 659 // 660 // The file is accepted only if each such line lists something 661 // matching the file. For example: 662 // 663 // // +build windows linux 664 // 665 // marks the file as applicable only on Windows and Linux. 666 // 667 func (ctxt *Context) shouldBuild(content []byte) bool { 668 // Pass 1. Identify leading run of // comments and blank lines, 669 // which must be followed by a blank line. 670 end := 0 671 p := content 672 for len(p) > 0 { 673 line := p 674 if i := bytes.IndexByte(line, '\n'); i >= 0 { 675 line, p = line[:i], p[i+1:] 676 } else { 677 p = p[len(p):] 678 } 679 line = bytes.TrimSpace(line) 680 if len(line) == 0 { // Blank line 681 end = cap(content) - cap(line) // &line[0] - &content[0] 682 continue 683 } 684 if !bytes.HasPrefix(line, slashslash) { // Not comment line 685 break 686 } 687 } 688 content = content[:end] 689 690 // Pass 2. Process each line in the run. 691 p = content 692 for len(p) > 0 { 693 line := p 694 if i := bytes.IndexByte(line, '\n'); i >= 0 { 695 line, p = line[:i], p[i+1:] 696 } else { 697 p = p[len(p):] 698 } 699 line = bytes.TrimSpace(line) 700 if bytes.HasPrefix(line, slashslash) { 701 line = bytes.TrimSpace(line[len(slashslash):]) 702 if len(line) > 0 && line[0] == '+' { 703 // Looks like a comment +line. 704 f := strings.Fields(string(line)) 705 if f[0] == "+build" { 706 ok := false 707 for _, tok := range f[1:] { 708 if ctxt.match(tok) { 709 ok = true 710 break 711 } 712 } 713 if !ok { 714 return false // this one doesn't match 715 } 716 } 717 } 718 } 719 } 720 return true // everything matches 721 } 722 723 // saveCgo saves the information from the #cgo lines in the import "C" comment. 724 // These lines set CFLAGS and LDFLAGS and pkg-config directives that affect 725 // the way cgo's C code is built. 726 // 727 // TODO(rsc): This duplicates code in cgo. 728 // Once the dust settles, remove this code from cgo. 729 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error { 730 text := cg.Text() 731 for _, line := range strings.Split(text, "\n") { 732 orig := line 733 734 // Line is 735 // #cgo [GOOS/GOARCH...] LDFLAGS: stuff 736 // 737 line = strings.TrimSpace(line) 738 if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { 739 continue 740 } 741 742 // Split at colon. 743 line = strings.TrimSpace(line[4:]) 744 i := strings.Index(line, ":") 745 if i < 0 { 746 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 747 } 748 line, argstr := line[:i], line[i+1:] 749 750 // Parse GOOS/GOARCH stuff. 751 f := strings.Fields(line) 752 if len(f) < 1 { 753 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 754 } 755 756 cond, verb := f[:len(f)-1], f[len(f)-1] 757 if len(cond) > 0 { 758 ok := false 759 for _, c := range cond { 760 if ctxt.match(c) { 761 ok = true 762 break 763 } 764 } 765 if !ok { 766 continue 767 } 768 } 769 770 args, err := splitQuoted(argstr) 771 if err != nil { 772 return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) 773 } 774 for _, arg := range args { 775 if !safeName(arg) { 776 return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) 777 } 778 } 779 780 switch verb { 781 case "CFLAGS": 782 di.CgoCFLAGS = append(di.CgoCFLAGS, args...) 783 case "LDFLAGS": 784 di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) 785 case "pkg-config": 786 di.CgoPkgConfig = append(di.CgoPkgConfig, args...) 787 default: 788 return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) 789 } 790 } 791 return nil 792 } 793 794 var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:") 795 796 func safeName(s string) bool { 797 if s == "" { 798 return false 799 } 800 for i := 0; i < len(s); i++ { 801 if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 { 802 return false 803 } 804 } 805 return true 806 } 807 808 // splitQuoted splits the string s around each instance of one or more consecutive 809 // white space characters while taking into account quotes and escaping, and 810 // returns an array of substrings of s or an empty list if s contains only white space. 811 // Single quotes and double quotes are recognized to prevent splitting within the 812 // quoted region, and are removed from the resulting substrings. If a quote in s 813 // isn't closed err will be set and r will have the unclosed argument as the 814 // last element. The backslash is used for escaping. 815 // 816 // For example, the following string: 817 // 818 // a b:"c d" 'e''f' "g\"" 819 // 820 // Would be parsed as: 821 // 822 // []string{"a", "b:c d", "ef", `g"`} 823 // 824 func splitQuoted(s string) (r []string, err error) { 825 var args []string 826 arg := make([]rune, len(s)) 827 escaped := false 828 quoted := false 829 quote := '\x00' 830 i := 0 831 for _, rune := range s { 832 switch { 833 case escaped: 834 escaped = false 835 case rune == '\\': 836 escaped = true 837 continue 838 case quote != '\x00': 839 if rune == quote { 840 quote = '\x00' 841 continue 842 } 843 case rune == '"' || rune == '\'': 844 quoted = true 845 quote = rune 846 continue 847 case unicode.IsSpace(rune): 848 if quoted || i > 0 { 849 quoted = false 850 args = append(args, string(arg[:i])) 851 i = 0 852 } 853 continue 854 } 855 arg[i] = rune 856 i++ 857 } 858 if quoted || i > 0 { 859 args = append(args, string(arg[:i])) 860 } 861 if quote != 0 { 862 err = errors.New("unclosed quote") 863 } else if escaped { 864 err = errors.New("unfinished escaping") 865 } 866 return args, err 867 } 868 869 // match returns true if the name is one of: 870 // 871 // $GOOS 872 // $GOARCH 873 // cgo (if cgo is enabled) 874 // !cgo (if cgo is disabled) 875 // tag (if tag is listed in ctxt.BuildTags) 876 // !tag (if tag is not listed in ctxt.BuildTags) 877 // a comma-separated list of any of these 878 // 879 func (ctxt *Context) match(name string) bool { 880 if name == "" { 881 return false 882 } 883 if i := strings.Index(name, ","); i >= 0 { 884 // comma-separated list 885 return ctxt.match(name[:i]) && ctxt.match(name[i+1:]) 886 } 887 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 888 return false 889 } 890 if strings.HasPrefix(name, "!") { // negation 891 return len(name) > 1 && !ctxt.match(name[1:]) 892 } 893 894 // Tags must be letters, digits, underscores. 895 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 896 for _, c := range name { 897 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' { 898 return false 899 } 900 } 901 902 // special tags 903 if ctxt.CgoEnabled && name == "cgo" { 904 return true 905 } 906 if name == ctxt.GOOS || name == ctxt.GOARCH { 907 return true 908 } 909 910 // other tags 911 for _, tag := range ctxt.BuildTags { 912 if tag == name { 913 return true 914 } 915 } 916 917 return false 918 } 919 920 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH 921 // suffix which does not match the current system. 922 // The recognized name formats are: 923 // 924 // name_$(GOOS).* 925 // name_$(GOARCH).* 926 // name_$(GOOS)_$(GOARCH).* 927 // name_$(GOOS)_test.* 928 // name_$(GOARCH)_test.* 929 // name_$(GOOS)_$(GOARCH)_test.* 930 // 931 func (ctxt *Context) goodOSArchFile(name string) bool { 932 if dot := strings.Index(name, "."); dot != -1 { 933 name = name[:dot] 934 } 935 l := strings.Split(name, "_") 936 if n := len(l); n > 0 && l[n-1] == "test" { 937 l = l[:n-1] 938 } 939 n := len(l) 940 if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { 941 return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH 942 } 943 if n >= 1 && knownOS[l[n-1]] { 944 return l[n-1] == ctxt.GOOS 945 } 946 if n >= 1 && knownArch[l[n-1]] { 947 return l[n-1] == ctxt.GOARCH 948 } 949 return true 950 } 951 952 var knownOS = make(map[string]bool) 953 var knownArch = make(map[string]bool) 954 955 func init() { 956 for _, v := range strings.Fields(goosList) { 957 knownOS[v] = true 958 } 959 for _, v := range strings.Fields(goarchList) { 960 knownArch[v] = true 961 } 962 } 963 964 // ToolDir is the directory containing build tools. 965 var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) 966 967 // IsLocalImport reports whether the import path is 968 // a local import path, like ".", "..", "./foo", or "../foo". 969 func IsLocalImport(path string) bool { 970 return path == "." || path == ".." || 971 strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../") 972 } 973 974 // ArchChar returns the architecture character for the given goarch. 975 // For example, ArchChar("amd64") returns "6". 976 func ArchChar(goarch string) (string, error) { 977 switch goarch { 978 case "386": 979 return "8", nil 980 case "amd64": 981 return "6", nil 982 case "arm": 983 return "5", nil 984 } 985 return "", errors.New("unsupported GOARCH " + goarch) 986 }