OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [go/] [build/] [dir.go] - Blame information for rev 747

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// 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/parser"
13
        "go/token"
14
        "io/ioutil"
15
        "log"
16
        "os"
17
        "path"
18
        "path/filepath"
19
        "runtime"
20
        "sort"
21
        "strconv"
22
        "strings"
23
        "unicode"
24
)
25
 
26
// A Context specifies the supporting context for a build.
27
type Context struct {
28
        GOARCH     string   // target architecture
29
        GOOS       string   // target operating system
30
        CgoEnabled bool     // whether cgo can be used
31
        BuildTags  []string // additional tags to recognize in +build lines
32
 
33
        // By default, ScanDir uses the operating system's
34
        // file system calls to read directories and files.
35
        // Callers can override those calls to provide other
36
        // ways to read data by setting ReadDir and ReadFile.
37
        // ScanDir does not make any assumptions about the
38
        // format of the strings dir and file: they can be
39
        // slash-separated, backslash-separated, even URLs.
40
 
41
        // ReadDir returns a slice of os.FileInfo, sorted by Name,
42
        // describing the content of the named directory.
43
        // The dir argument is the argument to ScanDir.
44
        // If ReadDir is nil, ScanDir uses io.ReadDir.
45
        ReadDir func(dir string) (fi []os.FileInfo, err error)
46
 
47
        // ReadFile returns the content of the file named file
48
        // in the directory named dir.  The dir argument is the
49
        // argument to ScanDir, and the file argument is the
50
        // Name field from an os.FileInfo returned by ReadDir.
51
        // The returned path is the full name of the file, to be
52
        // used in error messages.
53
        //
54
        // If ReadFile is nil, ScanDir uses filepath.Join(dir, file)
55
        // as the path and ioutil.ReadFile to read the data.
56
        ReadFile func(dir, file string) (path string, content []byte, err error)
57
}
58
 
59
func (ctxt *Context) readDir(dir string) ([]os.FileInfo, error) {
60
        if f := ctxt.ReadDir; f != nil {
61
                return f(dir)
62
        }
63
        return ioutil.ReadDir(dir)
64
}
65
 
66
func (ctxt *Context) readFile(dir, file string) (string, []byte, error) {
67
        if f := ctxt.ReadFile; f != nil {
68
                return f(dir, file)
69
        }
70
        p := filepath.Join(dir, file)
71
        content, err := ioutil.ReadFile(p)
72
        return p, content, err
73
}
74
 
75
// The DefaultContext is the default Context for builds.
76
// It uses the GOARCH and GOOS environment variables
77
// if set, or else the compiled code's GOARCH and GOOS.
78
var DefaultContext Context = defaultContext()
79
 
80
var cgoEnabled = map[string]bool{
81
        "darwin/386":    true,
82
        "darwin/amd64":  true,
83
        "linux/386":     true,
84
        "linux/amd64":   true,
85
        "freebsd/386":   true,
86
        "freebsd/amd64": true,
87
        "windows/386":   true,
88
        "windows/amd64": true,
89
}
90
 
91
func defaultContext() Context {
92
        var c Context
93
 
94
        c.GOARCH = envOr("GOARCH", runtime.GOARCH)
95
        c.GOOS = envOr("GOOS", runtime.GOOS)
96
 
97
        s := os.Getenv("CGO_ENABLED")
98
        switch s {
99
        case "1":
100
                c.CgoEnabled = true
101
        case "0":
102
                c.CgoEnabled = false
103
        default:
104
                c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
105
        }
106
 
107
        return c
108
}
109
 
110
func envOr(name, def string) string {
111
        s := os.Getenv(name)
112
        if s == "" {
113
                return def
114
        }
115
        return s
116
}
117
 
118
type DirInfo struct {
119
        Package        string                      // Name of package in dir
120
        PackageComment *ast.CommentGroup           // Package comments from GoFiles
121
        ImportPath     string                      // Import path of package in dir
122
        Imports        []string                    // All packages imported by GoFiles
123
        ImportPos      map[string][]token.Position // Source code location of imports
124
 
125
        // Source files
126
        GoFiles  []string // .go files in dir (excluding CgoFiles, TestGoFiles, XTestGoFiles)
127
        HFiles   []string // .h files in dir
128
        CFiles   []string // .c files in dir
129
        SFiles   []string // .s (and, when using cgo, .S files in dir)
130
        CgoFiles []string // .go files that import "C"
131
 
132
        // Cgo directives
133
        CgoPkgConfig []string // Cgo pkg-config directives
134
        CgoCFLAGS    []string // Cgo CFLAGS directives
135
        CgoLDFLAGS   []string // Cgo LDFLAGS directives
136
 
137
        // Test information
138
        TestGoFiles   []string // _test.go files in package
139
        XTestGoFiles  []string // _test.go files outside package
140
        TestImports   []string // All packages imported by (X)TestGoFiles
141
        TestImportPos map[string][]token.Position
142
}
143
 
144
func (d *DirInfo) IsCommand() bool {
145
        // TODO(rsc): This is at least a little bogus.
146
        return d.Package == "main"
147
}
148
 
149
// ScanDir calls DefaultContext.ScanDir.
150
func ScanDir(dir string) (info *DirInfo, err error) {
151
        return DefaultContext.ScanDir(dir)
152
}
153
 
154
// TODO(rsc): Move this comment to a more appropriate place.
155
 
156
// ScanDir returns a structure with details about the Go package
157
// found in the given directory.
158
//
159
// Most .go, .c, .h, and .s files in the directory are considered part
160
// of the package.  The exceptions are:
161
//
162
//      - .go files in package main (unless no other package is found)
163
//      - .go files in package documentation
164
//      - files starting with _ or .
165
//      - files with build constraints not satisfied by the context
166
//
167
// Build Constraints
168
//
169
// A build constraint is a line comment beginning with the directive +build
170
// that lists the conditions under which a file should be included in the package.
171
// Constraints may appear in any kind of source file (not just Go), but
172
// they must be appear near the top of the file, preceded
173
// only by blank lines and other line comments.
174
//
175
// A build constraint is evaluated as the OR of space-separated options;
176
// each option evaluates as the AND of its comma-separated terms;
177
// and each term is an alphanumeric word or, preceded by !, its negation.
178
// That is, the build constraint:
179
//
180
//      // +build linux,386 darwin,!cgo
181
//
182
// corresponds to the boolean formula:
183
//
184
//      (linux AND 386) OR (darwin AND (NOT cgo))
185
//
186
// During a particular build, the following words are satisfied:
187
//
188
//      - the target operating system, as spelled by runtime.GOOS
189
//      - the target architecture, as spelled by runtime.GOARCH
190
//      - "cgo", if ctxt.CgoEnabled is true
191
//      - any additional words listed in ctxt.BuildTags
192
//
193
// If a file's name, after stripping the extension and a possible _test suffix,
194
// matches *_GOOS, *_GOARCH, or *_GOOS_GOARCH for any known operating
195
// system and architecture values, then the file is considered to have an implicit
196
// build constraint requiring those terms.
197
//
198
// Examples
199
//
200
// To keep a file from being considered for the build:
201
//
202
//      // +build ignore
203
//
204
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
205
//
206
// To build a file only when using cgo, and only on Linux and OS X:
207
//
208
//      // +build linux,cgo darwin,cgo
209
//
210
// Such a file is usually paired with another file implementing the
211
// default functionality for other systems, which in this case would
212
// carry the constraint:
213
//
214
//      // +build !linux !darwin !cgo
215
//
216
// Naming a file dns_windows.go will cause it to be included only when
217
// building the package for Windows; similarly, math_386.s will be included
218
// only when building the package for 32-bit x86.
219
//
220
func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
221
        dirs, err := ctxt.readDir(dir)
222
        if err != nil {
223
                return nil, err
224
        }
225
 
226
        var Sfiles []string // files with ".S" (capital S)
227
        var di DirInfo
228
        imported := make(map[string][]token.Position)
229
        testImported := make(map[string][]token.Position)
230
        fset := token.NewFileSet()
231
        for _, d := range dirs {
232
                if d.IsDir() {
233
                        continue
234
                }
235
                name := d.Name()
236
                if strings.HasPrefix(name, "_") ||
237
                        strings.HasPrefix(name, ".") {
238
                        continue
239
                }
240
                if !ctxt.goodOSArchFile(name) {
241
                        continue
242
                }
243
 
244
                ext := path.Ext(name)
245
                switch ext {
246
                case ".go", ".c", ".s", ".h", ".S":
247
                        // tentatively okay
248
                default:
249
                        // skip
250
                        continue
251
                }
252
 
253
                // Look for +build comments to accept or reject the file.
254
                filename, data, err := ctxt.readFile(dir, name)
255
                if err != nil {
256
                        return nil, err
257
                }
258
                if !ctxt.shouldBuild(data) {
259
                        continue
260
                }
261
 
262
                // Going to save the file.  For non-Go files, can stop here.
263
                switch ext {
264
                case ".c":
265
                        di.CFiles = append(di.CFiles, name)
266
                        continue
267
                case ".h":
268
                        di.HFiles = append(di.HFiles, name)
269
                        continue
270
                case ".s":
271
                        di.SFiles = append(di.SFiles, name)
272
                        continue
273
                case ".S":
274
                        Sfiles = append(Sfiles, name)
275
                        continue
276
                }
277
 
278
                pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
279
                if err != nil {
280
                        return nil, err
281
                }
282
 
283
                pkg := string(pf.Name.Name)
284
                if pkg == "main" && di.Package != "" && di.Package != "main" {
285
                        continue
286
                }
287
                if pkg == "documentation" {
288
                        continue
289
                }
290
 
291
                isTest := strings.HasSuffix(name, "_test.go")
292
                if isTest && strings.HasSuffix(pkg, "_test") {
293
                        pkg = pkg[:len(pkg)-len("_test")]
294
                }
295
 
296
                if pkg != di.Package && di.Package == "main" {
297
                        // Found non-main package but was recording
298
                        // information about package main.  Reset.
299
                        di = DirInfo{}
300
                }
301
                if di.Package == "" {
302
                        di.Package = pkg
303
                } else if pkg != di.Package {
304
                        return nil, fmt.Errorf("%s: found packages %s and %s", dir, pkg, di.Package)
305
                }
306
                if pf.Doc != nil {
307
                        if di.PackageComment != nil {
308
                                di.PackageComment.List = append(di.PackageComment.List, pf.Doc.List...)
309
                        } else {
310
                                di.PackageComment = pf.Doc
311
                        }
312
                }
313
 
314
                // Record imports and information about cgo.
315
                isCgo := false
316
                for _, decl := range pf.Decls {
317
                        d, ok := decl.(*ast.GenDecl)
318
                        if !ok {
319
                                continue
320
                        }
321
                        for _, dspec := range d.Specs {
322
                                spec, ok := dspec.(*ast.ImportSpec)
323
                                if !ok {
324
                                        continue
325
                                }
326
                                quoted := string(spec.Path.Value)
327
                                path, err := strconv.Unquote(quoted)
328
                                if err != nil {
329
                                        log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
330
                                }
331
                                if isTest {
332
                                        testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
333
                                } else {
334
                                        imported[path] = append(imported[path], fset.Position(spec.Pos()))
335
                                }
336
                                if path == "C" {
337
                                        if isTest {
338
                                                return nil, fmt.Errorf("%s: use of cgo in test not supported", filename)
339
                                        }
340
                                        cg := spec.Doc
341
                                        if cg == nil && len(d.Specs) == 1 {
342
                                                cg = d.Doc
343
                                        }
344
                                        if cg != nil {
345
                                                if err := ctxt.saveCgo(filename, &di, cg); err != nil {
346
                                                        return nil, err
347
                                                }
348
                                        }
349
                                        isCgo = true
350
                                }
351
                        }
352
                }
353
                if isCgo {
354
                        if ctxt.CgoEnabled {
355
                                di.CgoFiles = append(di.CgoFiles, name)
356
                        }
357
                } else if isTest {
358
                        if pkg == string(pf.Name.Name) {
359
                                di.TestGoFiles = append(di.TestGoFiles, name)
360
                        } else {
361
                                di.XTestGoFiles = append(di.XTestGoFiles, name)
362
                        }
363
                } else {
364
                        di.GoFiles = append(di.GoFiles, name)
365
                }
366
        }
367
        if di.Package == "" {
368
                return nil, fmt.Errorf("%s: no Go source files", dir)
369
        }
370
        di.Imports = make([]string, len(imported))
371
        di.ImportPos = imported
372
        i := 0
373
        for p := range imported {
374
                di.Imports[i] = p
375
                i++
376
        }
377
        di.TestImports = make([]string, len(testImported))
378
        di.TestImportPos = testImported
379
        i = 0
380
        for p := range testImported {
381
                di.TestImports[i] = p
382
                i++
383
        }
384
 
385
        // add the .S files only if we are using cgo
386
        // (which means gcc will compile them).
387
        // The standard assemblers expect .s files.
388
        if len(di.CgoFiles) > 0 {
389
                di.SFiles = append(di.SFiles, Sfiles...)
390
                sort.Strings(di.SFiles)
391
        }
392
 
393
        // File name lists are sorted because ReadDir sorts.
394
        sort.Strings(di.Imports)
395
        sort.Strings(di.TestImports)
396
        return &di, nil
397
}
398
 
399
var slashslash = []byte("//")
400
 
401
// shouldBuild reports whether it is okay to use this file,
402
// The rule is that in the file's leading run of // comments
403
// and blank lines, which must be followed by a blank line
404
// (to avoid including a Go package clause doc comment),
405
// lines beginning with '// +build' are taken as build directives.
406
//
407
// The file is accepted only if each such line lists something
408
// matching the file.  For example:
409
//
410
//      // +build windows linux
411
//
412
// marks the file as applicable only on Windows and Linux.
413
//
414
func (ctxt *Context) shouldBuild(content []byte) bool {
415
        // Pass 1. Identify leading run of // comments and blank lines,
416
        // which must be followed by a blank line.
417
        end := 0
418
        p := content
419
        for len(p) > 0 {
420
                line := p
421
                if i := bytes.IndexByte(line, '\n'); i >= 0 {
422
                        line, p = line[:i], p[i+1:]
423
                } else {
424
                        p = p[len(p):]
425
                }
426
                line = bytes.TrimSpace(line)
427
                if len(line) == 0 { // Blank line
428
                        end = cap(content) - cap(line) // &line[0] - &content[0]
429
                        continue
430
                }
431
                if !bytes.HasPrefix(line, slashslash) { // Not comment line
432
                        break
433
                }
434
        }
435
        content = content[:end]
436
 
437
        // Pass 2.  Process each line in the run.
438
        p = content
439
        for len(p) > 0 {
440
                line := p
441
                if i := bytes.IndexByte(line, '\n'); i >= 0 {
442
                        line, p = line[:i], p[i+1:]
443
                } else {
444
                        p = p[len(p):]
445
                }
446
                line = bytes.TrimSpace(line)
447
                if bytes.HasPrefix(line, slashslash) {
448
                        line = bytes.TrimSpace(line[len(slashslash):])
449
                        if len(line) > 0 && line[0] == '+' {
450
                                // Looks like a comment +line.
451
                                f := strings.Fields(string(line))
452
                                if f[0] == "+build" {
453
                                        ok := false
454
                                        for _, tok := range f[1:] {
455
                                                if ctxt.match(tok) {
456
                                                        ok = true
457
                                                        break
458
                                                }
459
                                        }
460
                                        if !ok {
461
                                                return false // this one doesn't match
462
                                        }
463
                                }
464
                        }
465
                }
466
        }
467
        return true // everything matches
468
}
469
 
470
// saveCgo saves the information from the #cgo lines in the import "C" comment.
471
// These lines set CFLAGS and LDFLAGS and pkg-config directives that affect
472
// the way cgo's C code is built.
473
//
474
// TODO(rsc): This duplicates code in cgo.
475
// Once the dust settles, remove this code from cgo.
476
func (ctxt *Context) saveCgo(filename string, di *DirInfo, cg *ast.CommentGroup) error {
477
        text := cg.Text()
478
        for _, line := range strings.Split(text, "\n") {
479
                orig := line
480
 
481
                // Line is
482
                //      #cgo [GOOS/GOARCH...] LDFLAGS: stuff
483
                //
484
                line = strings.TrimSpace(line)
485
                if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
486
                        continue
487
                }
488
 
489
                // Split at colon.
490
                line = strings.TrimSpace(line[4:])
491
                i := strings.Index(line, ":")
492
                if i < 0 {
493
                        return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
494
                }
495
                line, argstr := line[:i], line[i+1:]
496
 
497
                // Parse GOOS/GOARCH stuff.
498
                f := strings.Fields(line)
499
                if len(f) < 1 {
500
                        return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
501
                }
502
 
503
                cond, verb := f[:len(f)-1], f[len(f)-1]
504
                if len(cond) > 0 {
505
                        ok := false
506
                        for _, c := range cond {
507
                                if ctxt.match(c) {
508
                                        ok = true
509
                                        break
510
                                }
511
                        }
512
                        if !ok {
513
                                continue
514
                        }
515
                }
516
 
517
                args, err := splitQuoted(argstr)
518
                if err != nil {
519
                        return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
520
                }
521
                for _, arg := range args {
522
                        if !safeName(arg) {
523
                                return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
524
                        }
525
                }
526
 
527
                switch verb {
528
                case "CFLAGS":
529
                        di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
530
                case "LDFLAGS":
531
                        di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
532
                case "pkg-config":
533
                        di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
534
                default:
535
                        return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
536
                }
537
        }
538
        return nil
539
}
540
 
541
var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:")
542
 
543
func safeName(s string) bool {
544
        if s == "" {
545
                return false
546
        }
547
        for i := 0; i < len(s); i++ {
548
                if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
549
                        return false
550
                }
551
        }
552
        return true
553
}
554
 
555
// splitQuoted splits the string s around each instance of one or more consecutive
556
// white space characters while taking into account quotes and escaping, and
557
// returns an array of substrings of s or an empty list if s contains only white space.
558
// Single quotes and double quotes are recognized to prevent splitting within the
559
// quoted region, and are removed from the resulting substrings. If a quote in s
560
// isn't closed err will be set and r will have the unclosed argument as the
561
// last element.  The backslash is used for escaping.
562
//
563
// For example, the following string:
564
//
565
//     a b:"c d" 'e''f'  "g\""
566
//
567
// Would be parsed as:
568
//
569
//     []string{"a", "b:c d", "ef", `g"`}
570
//
571
func splitQuoted(s string) (r []string, err error) {
572
        var args []string
573
        arg := make([]rune, len(s))
574
        escaped := false
575
        quoted := false
576
        quote := '\x00'
577
        i := 0
578
        for _, rune := range s {
579
                switch {
580
                case escaped:
581
                        escaped = false
582
                case rune == '\\':
583
                        escaped = true
584
                        continue
585
                case quote != '\x00':
586
                        if rune == quote {
587
                                quote = '\x00'
588
                                continue
589
                        }
590
                case rune == '"' || rune == '\'':
591
                        quoted = true
592
                        quote = rune
593
                        continue
594
                case unicode.IsSpace(rune):
595
                        if quoted || i > 0 {
596
                                quoted = false
597
                                args = append(args, string(arg[:i]))
598
                                i = 0
599
                        }
600
                        continue
601
                }
602
                arg[i] = rune
603
                i++
604
        }
605
        if quoted || i > 0 {
606
                args = append(args, string(arg[:i]))
607
        }
608
        if quote != 0 {
609
                err = errors.New("unclosed quote")
610
        } else if escaped {
611
                err = errors.New("unfinished escaping")
612
        }
613
        return args, err
614
}
615
 
616
// match returns true if the name is one of:
617
//
618
//      $GOOS
619
//      $GOARCH
620
//      cgo (if cgo is enabled)
621
//      !cgo (if cgo is disabled)
622
//      tag (if tag is listed in ctxt.BuildTags)
623
//      !tag (if tag is not listed in ctxt.BuildTags)
624
//      a slash-separated list of any of these
625
//
626
func (ctxt *Context) match(name string) bool {
627
        if name == "" {
628
                return false
629
        }
630
        if i := strings.Index(name, ","); i >= 0 {
631
                // comma-separated list
632
                return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
633
        }
634
        if strings.HasPrefix(name, "!!") { // bad syntax, reject always
635
                return false
636
        }
637
        if strings.HasPrefix(name, "!") { // negation
638
                return !ctxt.match(name[1:])
639
        }
640
 
641
        // Tags must be letters, digits, underscores.
642
        // Unlike in Go identifiers, all digits is fine (e.g., "386").
643
        for _, c := range name {
644
                if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
645
                        return false
646
                }
647
        }
648
 
649
        // special tags
650
        if ctxt.CgoEnabled && name == "cgo" {
651
                return true
652
        }
653
        if name == ctxt.GOOS || name == ctxt.GOARCH {
654
                return true
655
        }
656
 
657
        // other tags
658
        for _, tag := range ctxt.BuildTags {
659
                if tag == name {
660
                        return true
661
                }
662
        }
663
 
664
        return false
665
}
666
 
667
// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
668
// suffix which does not match the current system.
669
// The recognized name formats are:
670
//
671
//     name_$(GOOS).*
672
//     name_$(GOARCH).*
673
//     name_$(GOOS)_$(GOARCH).*
674
//     name_$(GOOS)_test.*
675
//     name_$(GOARCH)_test.*
676
//     name_$(GOOS)_$(GOARCH)_test.*
677
//
678
func (ctxt *Context) goodOSArchFile(name string) bool {
679
        if dot := strings.Index(name, "."); dot != -1 {
680
                name = name[:dot]
681
        }
682
        l := strings.Split(name, "_")
683
        if n := len(l); n > 0 && l[n-1] == "test" {
684
                l = l[:n-1]
685
        }
686
        n := len(l)
687
        if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
688
                return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
689
        }
690
        if n >= 1 && knownOS[l[n-1]] {
691
                return l[n-1] == ctxt.GOOS
692
        }
693
        if n >= 1 && knownArch[l[n-1]] {
694
                return l[n-1] == ctxt.GOARCH
695
        }
696
        return true
697
}
698
 
699
var knownOS = make(map[string]bool)
700
var knownArch = make(map[string]bool)
701
 
702
func init() {
703
        for _, v := range strings.Fields(goosList) {
704
                knownOS[v] = true
705
        }
706
        for _, v := range strings.Fields(goarchList) {
707
                knownArch[v] = true
708
        }
709
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.