...

Source file src/pkg/cmd/go/internal/imports/build.go

     1	// Copyright 2018 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	// Copied from Go distribution src/go/build/build.go, syslist.go
     6	
     7	package imports
     8	
     9	import (
    10		"bytes"
    11		"strings"
    12		"unicode"
    13	)
    14	
    15	var slashslash = []byte("//")
    16	
    17	// ShouldBuild reports whether it is okay to use this file,
    18	// The rule is that in the file's leading run of // comments
    19	// and blank lines, which must be followed by a blank line
    20	// (to avoid including a Go package clause doc comment),
    21	// lines beginning with '// +build' are taken as build directives.
    22	//
    23	// The file is accepted only if each such line lists something
    24	// matching the file. For example:
    25	//
    26	//	// +build windows linux
    27	//
    28	// marks the file as applicable only on Windows and Linux.
    29	//
    30	// If tags["*"] is true, then ShouldBuild will consider every
    31	// build tag except "ignore" to be both true and false for
    32	// the purpose of satisfying build tags, in order to estimate
    33	// (conservatively) whether a file could ever possibly be used
    34	// in any build.
    35	//
    36	func ShouldBuild(content []byte, tags map[string]bool) bool {
    37		// Pass 1. Identify leading run of // comments and blank lines,
    38		// which must be followed by a blank line.
    39		end := 0
    40		p := content
    41		for len(p) > 0 {
    42			line := p
    43			if i := bytes.IndexByte(line, '\n'); i >= 0 {
    44				line, p = line[:i], p[i+1:]
    45			} else {
    46				p = p[len(p):]
    47			}
    48			line = bytes.TrimSpace(line)
    49			if len(line) == 0 { // Blank line
    50				end = len(content) - len(p)
    51				continue
    52			}
    53			if !bytes.HasPrefix(line, slashslash) { // Not comment line
    54				break
    55			}
    56		}
    57		content = content[:end]
    58	
    59		// Pass 2.  Process each line in the run.
    60		p = content
    61		allok := true
    62		for len(p) > 0 {
    63			line := p
    64			if i := bytes.IndexByte(line, '\n'); i >= 0 {
    65				line, p = line[:i], p[i+1:]
    66			} else {
    67				p = p[len(p):]
    68			}
    69			line = bytes.TrimSpace(line)
    70			if !bytes.HasPrefix(line, slashslash) {
    71				continue
    72			}
    73			line = bytes.TrimSpace(line[len(slashslash):])
    74			if len(line) > 0 && line[0] == '+' {
    75				// Looks like a comment +line.
    76				f := strings.Fields(string(line))
    77				if f[0] == "+build" {
    78					ok := false
    79					for _, tok := range f[1:] {
    80						if matchTags(tok, tags) {
    81							ok = true
    82						}
    83					}
    84					if !ok {
    85						allok = false
    86					}
    87				}
    88			}
    89		}
    90	
    91		return allok
    92	}
    93	
    94	// matchTags reports whether the name is one of:
    95	//
    96	//	tag (if tags[tag] is true)
    97	//	!tag (if tags[tag] is false)
    98	//	a comma-separated list of any of these
    99	//
   100	func matchTags(name string, tags map[string]bool) bool {
   101		if name == "" {
   102			return false
   103		}
   104		if i := strings.Index(name, ","); i >= 0 {
   105			// comma-separated list
   106			ok1 := matchTags(name[:i], tags)
   107			ok2 := matchTags(name[i+1:], tags)
   108			return ok1 && ok2
   109		}
   110		if strings.HasPrefix(name, "!!") { // bad syntax, reject always
   111			return false
   112		}
   113		if strings.HasPrefix(name, "!") { // negation
   114			return len(name) > 1 && matchTag(name[1:], tags, false)
   115		}
   116		return matchTag(name, tags, true)
   117	}
   118	
   119	// matchTag reports whether the tag name is valid and satisfied by tags[name]==want.
   120	func matchTag(name string, tags map[string]bool, want bool) bool {
   121		// Tags must be letters, digits, underscores or dots.
   122		// Unlike in Go identifiers, all digits are fine (e.g., "386").
   123		for _, c := range name {
   124			if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
   125				return false
   126			}
   127		}
   128	
   129		if tags["*"] && name != "" && name != "ignore" {
   130			// Special case for gathering all possible imports:
   131			// if we put * in the tags map then all tags
   132			// except "ignore" are considered both present and not
   133			// (so we return true no matter how 'want' is set).
   134			return true
   135		}
   136	
   137		have := tags[name]
   138		if name == "linux" {
   139			have = have || tags["android"]
   140		}
   141		if name == "solaris" {
   142			have = have || tags["illumos"]
   143		}
   144		return have == want
   145	}
   146	
   147	// MatchFile returns false if the name contains a $GOOS or $GOARCH
   148	// suffix which does not match the current system.
   149	// The recognized name formats are:
   150	//
   151	//     name_$(GOOS).*
   152	//     name_$(GOARCH).*
   153	//     name_$(GOOS)_$(GOARCH).*
   154	//     name_$(GOOS)_test.*
   155	//     name_$(GOARCH)_test.*
   156	//     name_$(GOOS)_$(GOARCH)_test.*
   157	//
   158	// Exceptions:
   159	//     if GOOS=android, then files with GOOS=linux are also matched.
   160	//     if GOOS=illumos, then files with GOOS=solaris are also matched.
   161	//
   162	// If tags["*"] is true, then MatchFile will consider all possible
   163	// GOOS and GOARCH to be available and will consequently
   164	// always return true.
   165	func MatchFile(name string, tags map[string]bool) bool {
   166		if tags["*"] {
   167			return true
   168		}
   169		if dot := strings.Index(name, "."); dot != -1 {
   170			name = name[:dot]
   171		}
   172	
   173		// Before Go 1.4, a file called "linux.go" would be equivalent to having a
   174		// build tag "linux" in that file. For Go 1.4 and beyond, we require this
   175		// auto-tagging to apply only to files with a non-empty prefix, so
   176		// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
   177		// systems, such as android, to arrive without breaking existing code with
   178		// innocuous source code in "android.go". The easiest fix: cut everything
   179		// in the name before the initial _.
   180		i := strings.Index(name, "_")
   181		if i < 0 {
   182			return true
   183		}
   184		name = name[i:] // ignore everything before first _
   185	
   186		l := strings.Split(name, "_")
   187		if n := len(l); n > 0 && l[n-1] == "test" {
   188			l = l[:n-1]
   189		}
   190		n := len(l)
   191		if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
   192			return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true)
   193		}
   194		if n >= 1 && KnownOS[l[n-1]] {
   195			return matchTag(l[n-1], tags, true)
   196		}
   197		if n >= 1 && KnownArch[l[n-1]] {
   198			return matchTag(l[n-1], tags, true)
   199		}
   200		return true
   201	}
   202	
   203	var KnownOS = map[string]bool{
   204		"aix":       true,
   205		"android":   true,
   206		"darwin":    true,
   207		"dragonfly": true,
   208		"freebsd":   true,
   209		"hurd":      true,
   210		"illumos":   true,
   211		"js":        true,
   212		"linux":     true,
   213		"nacl":      true,
   214		"netbsd":    true,
   215		"openbsd":   true,
   216		"plan9":     true,
   217		"solaris":   true,
   218		"windows":   true,
   219		"zos":       true,
   220	}
   221	
   222	var KnownArch = map[string]bool{
   223		"386":         true,
   224		"amd64":       true,
   225		"amd64p32":    true,
   226		"arm":         true,
   227		"armbe":       true,
   228		"arm64":       true,
   229		"arm64be":     true,
   230		"ppc64":       true,
   231		"ppc64le":     true,
   232		"mips":        true,
   233		"mipsle":      true,
   234		"mips64":      true,
   235		"mips64le":    true,
   236		"mips64p32":   true,
   237		"mips64p32le": true,
   238		"ppc":         true,
   239		"riscv":       true,
   240		"riscv64":     true,
   241		"s390":        true,
   242		"s390x":       true,
   243		"sparc":       true,
   244		"sparc64":     true,
   245		"wasm":        true,
   246	}
   247	

View as plain text