1  
     2  
     3  
     4  
     5  
     6  
     7  
     8  
     9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  package imports
    20  
    21  import (
    22  	"bytes"
    23  	"cmd/go/internal/cfg"
    24  	"errors"
    25  	"fmt"
    26  	"go/build/constraint"
    27  	"internal/syslist"
    28  	"strings"
    29  	"unicode"
    30  )
    31  
    32  var (
    33  	bSlashSlash = []byte("//")
    34  	bStarSlash  = []byte("*/")
    35  	bSlashStar  = []byte("/*")
    36  	bPlusBuild  = []byte("+build")
    37  
    38  	goBuildComment = []byte("//go:build")
    39  
    40  	errMultipleGoBuild = errors.New("multiple //go:build comments")
    41  )
    42  
    43  func isGoBuildComment(line []byte) bool {
    44  	if !bytes.HasPrefix(line, goBuildComment) {
    45  		return false
    46  	}
    47  	line = bytes.TrimSpace(line)
    48  	rest := line[len(goBuildComment):]
    49  	return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
    50  }
    51  
    52  
    53  
    54  
    55  
    56  
    57  
    58  
    59  
    60  
    61  
    62  
    63  
    64  
    65  
    66  
    67  
    68  
    69  
    70  func ShouldBuild(content []byte, tags map[string]bool) bool {
    71  	
    72  	
    73  	
    74  	content, goBuild, _, err := parseFileHeader(content)
    75  	if err != nil {
    76  		return false
    77  	}
    78  
    79  	
    80  	
    81  	var shouldBuild bool
    82  	switch {
    83  	case goBuild != nil:
    84  		x, err := constraint.Parse(string(goBuild))
    85  		if err != nil {
    86  			return false
    87  		}
    88  		shouldBuild = eval(x, tags, true)
    89  
    90  	default:
    91  		shouldBuild = true
    92  		p := content
    93  		for len(p) > 0 {
    94  			line := p
    95  			if i := bytes.IndexByte(line, '\n'); i >= 0 {
    96  				line, p = line[:i], p[i+1:]
    97  			} else {
    98  				p = p[len(p):]
    99  			}
   100  			line = bytes.TrimSpace(line)
   101  			if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
   102  				continue
   103  			}
   104  			text := string(line)
   105  			if !constraint.IsPlusBuild(text) {
   106  				continue
   107  			}
   108  			if x, err := constraint.Parse(text); err == nil {
   109  				if !eval(x, tags, true) {
   110  					shouldBuild = false
   111  				}
   112  			}
   113  		}
   114  	}
   115  
   116  	return shouldBuild
   117  }
   118  
   119  func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
   120  	end := 0
   121  	p := content
   122  	ended := false       
   123  	inSlashStar := false 
   124  
   125  Lines:
   126  	for len(p) > 0 {
   127  		line := p
   128  		if i := bytes.IndexByte(line, '\n'); i >= 0 {
   129  			line, p = line[:i], p[i+1:]
   130  		} else {
   131  			p = p[len(p):]
   132  		}
   133  		line = bytes.TrimSpace(line)
   134  		if len(line) == 0 && !ended { 
   135  			
   136  			
   137  			
   138  			
   139  			
   140  			
   141  			
   142  			
   143  			end = len(content) - len(p)
   144  			continue Lines
   145  		}
   146  		if !bytes.HasPrefix(line, bSlashSlash) { 
   147  			ended = true
   148  		}
   149  
   150  		if !inSlashStar && isGoBuildComment(line) {
   151  			if goBuild != nil {
   152  				return nil, nil, false, errMultipleGoBuild
   153  			}
   154  			goBuild = line
   155  		}
   156  
   157  	Comments:
   158  		for len(line) > 0 {
   159  			if inSlashStar {
   160  				if i := bytes.Index(line, bStarSlash); i >= 0 {
   161  					inSlashStar = false
   162  					line = bytes.TrimSpace(line[i+len(bStarSlash):])
   163  					continue Comments
   164  				}
   165  				continue Lines
   166  			}
   167  			if bytes.HasPrefix(line, bSlashSlash) {
   168  				continue Lines
   169  			}
   170  			if bytes.HasPrefix(line, bSlashStar) {
   171  				inSlashStar = true
   172  				line = bytes.TrimSpace(line[len(bSlashStar):])
   173  				continue Comments
   174  			}
   175  			
   176  			break Lines
   177  		}
   178  	}
   179  
   180  	return content[:end], goBuild, sawBinaryOnly, nil
   181  }
   182  
   183  
   184  
   185  
   186  
   187  
   188  func matchTag(name string, tags map[string]bool, prefer bool) bool {
   189  	
   190  	
   191  	for _, c := range name {
   192  		if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
   193  			return false
   194  		}
   195  	}
   196  
   197  	if tags["*"] && name != "" && name != "ignore" {
   198  		
   199  		
   200  		
   201  		
   202  		return prefer
   203  	}
   204  
   205  	if tags[name] {
   206  		return true
   207  	}
   208  
   209  	switch name {
   210  	case "linux":
   211  		return tags["android"]
   212  	case "solaris":
   213  		return tags["illumos"]
   214  	case "darwin":
   215  		return tags["ios"]
   216  	case "unix":
   217  		return syslist.UnixOS[cfg.BuildContext.GOOS]
   218  	default:
   219  		return false
   220  	}
   221  }
   222  
   223  
   224  
   225  
   226  
   227  
   228  
   229  func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
   230  	switch x := x.(type) {
   231  	case *constraint.TagExpr:
   232  		return matchTag(x.Tag, tags, prefer)
   233  	case *constraint.NotExpr:
   234  		return !eval(x.X, tags, !prefer)
   235  	case *constraint.AndExpr:
   236  		return eval(x.X, tags, prefer) && eval(x.Y, tags, prefer)
   237  	case *constraint.OrExpr:
   238  		return eval(x.X, tags, prefer) || eval(x.Y, tags, prefer)
   239  	}
   240  	panic(fmt.Sprintf("unexpected constraint expression %T", x))
   241  }
   242  
   243  
   244  
   245  
   246  
   247  
   248  
   249  func Eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
   250  	return eval(x, tags, prefer)
   251  }
   252  
   253  
   254  
   255  
   256  
   257  
   258  
   259  
   260  
   261  
   262  
   263  
   264  
   265  
   266  
   267  
   268  
   269  
   270  
   271  
   272  
   273  func MatchFile(name string, tags map[string]bool) bool {
   274  	if tags["*"] {
   275  		return true
   276  	}
   277  	if dot := strings.Index(name, "."); dot != -1 {
   278  		name = name[:dot]
   279  	}
   280  
   281  	
   282  	
   283  	
   284  	
   285  	
   286  	
   287  	
   288  	i := strings.Index(name, "_")
   289  	if i < 0 {
   290  		return true
   291  	}
   292  	name = name[i:] 
   293  
   294  	l := strings.Split(name, "_")
   295  	if n := len(l); n > 0 && l[n-1] == "test" {
   296  		l = l[:n-1]
   297  	}
   298  	n := len(l)
   299  	if n >= 2 && syslist.KnownOS[l[n-2]] && syslist.KnownArch[l[n-1]] {
   300  		return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true)
   301  	}
   302  	if n >= 1 && syslist.KnownOS[l[n-1]] {
   303  		return matchTag(l[n-1], tags, true)
   304  	}
   305  	if n >= 1 && syslist.KnownArch[l[n-1]] {
   306  		return matchTag(l[n-1], tags, true)
   307  	}
   308  	return true
   309  }
   310  
View as plain text