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 }