Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/go/internal/imports

     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  	if name == "darwin" {
   145  		have = have || tags["ios"]
   146  	}
   147  	return have == want
   148  }
   149  
   150  // MatchFile returns false if the name contains a $GOOS or $GOARCH
   151  // suffix which does not match the current system.
   152  // The recognized name formats are:
   153  //
   154  //     name_$(GOOS).*
   155  //     name_$(GOARCH).*
   156  //     name_$(GOOS)_$(GOARCH).*
   157  //     name_$(GOOS)_test.*
   158  //     name_$(GOARCH)_test.*
   159  //     name_$(GOOS)_$(GOARCH)_test.*
   160  //
   161  // Exceptions:
   162  //     if GOOS=android, then files with GOOS=linux are also matched.
   163  //     if GOOS=illumos, then files with GOOS=solaris are also matched.
   164  //     if GOOS=ios, then files with GOOS=darwin are also matched.
   165  //
   166  // If tags["*"] is true, then MatchFile will consider all possible
   167  // GOOS and GOARCH to be available and will consequently
   168  // always return true.
   169  func MatchFile(name string, tags map[string]bool) bool {
   170  	if tags["*"] {
   171  		return true
   172  	}
   173  	if dot := strings.Index(name, "."); dot != -1 {
   174  		name = name[:dot]
   175  	}
   176  
   177  	// Before Go 1.4, a file called "linux.go" would be equivalent to having a
   178  	// build tag "linux" in that file. For Go 1.4 and beyond, we require this
   179  	// auto-tagging to apply only to files with a non-empty prefix, so
   180  	// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
   181  	// systems, such as android, to arrive without breaking existing code with
   182  	// innocuous source code in "android.go". The easiest fix: cut everything
   183  	// in the name before the initial _.
   184  	i := strings.Index(name, "_")
   185  	if i < 0 {
   186  		return true
   187  	}
   188  	name = name[i:] // ignore everything before first _
   189  
   190  	l := strings.Split(name, "_")
   191  	if n := len(l); n > 0 && l[n-1] == "test" {
   192  		l = l[:n-1]
   193  	}
   194  	n := len(l)
   195  	if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
   196  		return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true)
   197  	}
   198  	if n >= 1 && KnownOS[l[n-1]] {
   199  		return matchTag(l[n-1], tags, true)
   200  	}
   201  	if n >= 1 && KnownArch[l[n-1]] {
   202  		return matchTag(l[n-1], tags, true)
   203  	}
   204  	return true
   205  }
   206  
   207  var KnownOS = map[string]bool{
   208  	"aix":       true,
   209  	"android":   true,
   210  	"darwin":    true,
   211  	"dragonfly": true,
   212  	"freebsd":   true,
   213  	"hurd":      true,
   214  	"illumos":   true,
   215  	"ios":       true,
   216  	"js":        true,
   217  	"linux":     true,
   218  	"nacl":      true, // legacy; don't remove
   219  	"netbsd":    true,
   220  	"openbsd":   true,
   221  	"plan9":     true,
   222  	"solaris":   true,
   223  	"windows":   true,
   224  	"zos":       true,
   225  }
   226  
   227  var KnownArch = map[string]bool{
   228  	"386":         true,
   229  	"amd64":       true,
   230  	"amd64p32":    true, // legacy; don't remove
   231  	"arm":         true,
   232  	"armbe":       true,
   233  	"arm64":       true,
   234  	"arm64be":     true,
   235  	"ppc64":       true,
   236  	"ppc64le":     true,
   237  	"mips":        true,
   238  	"mipsle":      true,
   239  	"mips64":      true,
   240  	"mips64le":    true,
   241  	"mips64p32":   true,
   242  	"mips64p32le": true,
   243  	"ppc":         true,
   244  	"riscv":       true,
   245  	"riscv64":     true,
   246  	"s390":        true,
   247  	"s390x":       true,
   248  	"sparc":       true,
   249  	"sparc64":     true,
   250  	"wasm":        true,
   251  }
   252  

View as plain text