OSDN Git Service

7a81d50303dd71ba8ec6eb2f02ac0f38feeba48b
[pf3gnuchains/gcc-fork.git] / libgo / go / 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 ioutil.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 and 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 }