mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Build constraints are now inferred from go:build directives rather than +build directives. +build directives are still emitted in generated files as required in Go 1.16 and earlier. Note that go/build/constraint was added in Go 1.16, so gVisor now requires Go 1.16. PiperOrigin-RevId: 387240779
170 lines
4.6 KiB
Go
170 lines
4.6 KiB
Go
// Copyright 2021 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package constraintutil provides utilities for working with Go build
|
|
// constraints.
|
|
package constraintutil
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"go/build/constraint"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// FromReader extracts the build constraint from the Go source or assembly file
|
|
// whose contents are read by r.
|
|
func FromReader(r io.Reader) (constraint.Expr, error) {
|
|
// See go/build.parseFileHeader() for the "official" logic that this is
|
|
// derived from.
|
|
const (
|
|
slashStar = "/*"
|
|
starSlash = "*/"
|
|
gobuildPrefix = "//go:build"
|
|
)
|
|
s := bufio.NewScanner(r)
|
|
var (
|
|
inSlashStar = false // between /* and */
|
|
haveGobuild = false
|
|
e constraint.Expr
|
|
)
|
|
Lines:
|
|
for s.Scan() {
|
|
line := bytes.TrimSpace(s.Bytes())
|
|
if !inSlashStar && constraint.IsGoBuild(string(line)) {
|
|
if haveGobuild {
|
|
return nil, fmt.Errorf("multiple go:build directives")
|
|
}
|
|
haveGobuild = true
|
|
var err error
|
|
e, err = constraint.Parse(string(line))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
ThisLine:
|
|
for len(line) > 0 {
|
|
if inSlashStar {
|
|
if i := bytes.Index(line, []byte(starSlash)); i >= 0 {
|
|
inSlashStar = false
|
|
line = bytes.TrimSpace(line[i+len(starSlash):])
|
|
continue ThisLine
|
|
}
|
|
continue Lines
|
|
}
|
|
if bytes.HasPrefix(line, []byte("//")) {
|
|
continue Lines
|
|
}
|
|
// Note that if /* appears in the line, but not at the beginning,
|
|
// then the line is still non-empty, so skipping this and
|
|
// terminating below is correct.
|
|
if bytes.HasPrefix(line, []byte(slashStar)) {
|
|
inSlashStar = true
|
|
line = bytes.TrimSpace(line[len(slashStar):])
|
|
continue ThisLine
|
|
}
|
|
// A non-empty non-comment line terminates scanning for go:build.
|
|
break Lines
|
|
}
|
|
}
|
|
return e, s.Err()
|
|
}
|
|
|
|
// FromString extracts the build constraint from the Go source or assembly file
|
|
// containing the given data. If no build constraint applies to the file, it
|
|
// returns nil.
|
|
func FromString(str string) (constraint.Expr, error) {
|
|
return FromReader(strings.NewReader(str))
|
|
}
|
|
|
|
// FromFile extracts the build constraint from the Go source or assembly file
|
|
// at the given path. If no build constraint applies to the file, it returns
|
|
// nil.
|
|
func FromFile(path string) (constraint.Expr, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return FromReader(f)
|
|
}
|
|
|
|
// Combine returns a constraint.Expr that evaluates to true iff all expressions
|
|
// in es evaluate to true. If es is empty, Combine returns nil.
|
|
//
|
|
// Preconditions: All constraint.Exprs in es are non-nil.
|
|
func Combine(es []constraint.Expr) constraint.Expr {
|
|
switch len(es) {
|
|
case 0:
|
|
return nil
|
|
case 1:
|
|
return es[0]
|
|
default:
|
|
a := &constraint.AndExpr{es[0], es[1]}
|
|
for i := 2; i < len(es); i++ {
|
|
a = &constraint.AndExpr{a, es[i]}
|
|
}
|
|
return a
|
|
}
|
|
}
|
|
|
|
// CombineFromFiles returns a build constraint expression that evaluates to
|
|
// true iff the build constraints from all of the given Go source or assembly
|
|
// files evaluate to true. If no build constraints apply to any of the given
|
|
// files, it returns nil.
|
|
func CombineFromFiles(paths []string) (constraint.Expr, error) {
|
|
var es []constraint.Expr
|
|
for _, path := range paths {
|
|
e, err := FromFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read build constraints from %q: %v", path, err)
|
|
}
|
|
if e != nil {
|
|
es = append(es, e)
|
|
}
|
|
}
|
|
return Combine(es), nil
|
|
}
|
|
|
|
// Lines returns a string containing build constraint directives for the given
|
|
// constraint.Expr, including two trailing newlines, as appropriate for a Go
|
|
// source or assembly file. At least a go:build directive will be emitted; if
|
|
// the constraint is expressible using +build directives as well, then +build
|
|
// directives will also be emitted.
|
|
//
|
|
// If e is nil, Lines returns the empty string.
|
|
func Lines(e constraint.Expr) string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
|
|
var b strings.Builder
|
|
b.WriteString("//go:build ")
|
|
b.WriteString(e.String())
|
|
b.WriteByte('\n')
|
|
|
|
if pblines, err := constraint.PlusBuildLines(e); err == nil {
|
|
for _, line := range pblines {
|
|
b.WriteString(line)
|
|
b.WriteByte('\n')
|
|
}
|
|
}
|
|
|
|
b.WriteByte('\n')
|
|
return b.String()
|
|
}
|