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