github.com/tiagovtristao/plz@v13.4.0+incompatible/src/core/build_target.go (about)

     1  package core
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  	"reflect"
     9  	"sort"
    10  	"strings"
    11  	"sync/atomic"
    12  	"time"
    13  )
    14  
    15  // OutDir is the root output directory for everything.
    16  const OutDir string = "plz-out"
    17  
    18  // TmpDir is the root of the temporary directory for building targets & running tests.
    19  const TmpDir string = "plz-out/tmp"
    20  
    21  // GenDir is the output directory for non-binary targets.
    22  const GenDir string = "plz-out/gen"
    23  
    24  // BinDir is the output directory for binary targets.
    25  const BinDir string = "plz-out/bin"
    26  
    27  // DefaultBuildingDescription is the default description for targets when they're building.
    28  const DefaultBuildingDescription = "Building..."
    29  
    30  // SandboxDir is the directory that sandboxed actions are run in.
    31  const SandboxDir = "/tmp/plz_sandbox"
    32  
    33  // Suffixes for temporary directories
    34  const buildDirSuffix = "._build"
    35  const testDirSuffix = "._test"
    36  
    37  // A BuildTarget is a representation of a build target and all information about it;
    38  // its name, dependencies, build commands, etc.
    39  type BuildTarget struct {
    40  	// N.B. The tags on these fields are used by query print to help it print them.
    41  
    42  	// Identifier of this build target
    43  	Label BuildLabel `name:"name"`
    44  	// If this target is in a subrepo, this will be the one it's in.
    45  	Subrepo *Subrepo `print:"false"`
    46  	// Dependencies of this target.
    47  	// Maps the original declaration to whatever dependencies actually got attached,
    48  	// which may be more than one in some cases. Also contains info about exporting etc.
    49  	dependencies []depInfo `name:"deps"`
    50  	// List of build target patterns that can use this build target.
    51  	Visibility []BuildLabel
    52  	// Source files of this rule. Can refer to build rules themselves.
    53  	Sources []BuildInput `name:"srcs"`
    54  	// Named source files of this rule; as above but identified by name.
    55  	NamedSources map[string][]BuildInput `name:"srcs"`
    56  	// Data files of this rule. Similar to sources but used at runtime, typically by tests.
    57  	Data []BuildInput
    58  	// Output files of this rule. All are paths relative to this package.
    59  	outputs []string `name:"outs"`
    60  	// Named output subsets of this rule. All are paths relative to this package but can be
    61  	// captured separately; for example something producing C code might separate its outputs
    62  	// into sources and headers.
    63  	namedOutputs map[string][]string `name:"outs"`
    64  	// Optional output files of this rule. Same as outs but aren't required to be produced always.
    65  	// Can be glob patterns.
    66  	OptionalOutputs []string `name:"optional_outs"`
    67  	// Optional labels applied to this rule. Used for including/excluding rules.
    68  	Labels []string
    69  	// Shell command to run.
    70  	Command string `name:"cmd" hide:"filegroup"`
    71  	// Per-configuration shell commands to run.
    72  	Commands map[string]string `name:"cmd" hide:"filegroup"`
    73  	// Shell command to run for test targets.
    74  	TestCommand string `name:"test_cmd"`
    75  	// Per-configuration test commands to run.
    76  	TestCommands map[string]string `name:"test_cmd"`
    77  	// Represents the state of this build target (see below)
    78  	state int32 `print:"false"`
    79  	// True if this target is a binary (ie. runnable, will appear in plz-out/bin)
    80  	IsBinary bool `name:"binary"`
    81  	// True if this target is a test
    82  	IsTest bool `name:"test"`
    83  	// Indicates that the target can only be depended on by tests or other rules with this set.
    84  	// Used to restrict non-deployable code and also affects coverage detection.
    85  	TestOnly bool `name:"test_only"`
    86  	// True if we're going to containerise the test.
    87  	Containerise bool `name:"container"`
    88  	// True if the build action is sandboxed.
    89  	Sandbox bool
    90  	// True if the test action is sandboxed.
    91  	TestSandbox bool `name:"test_sandbox"`
    92  	// True if the target is a test and has no output file.
    93  	// Default is false, meaning all tests must produce test.results as output.
    94  	NoTestOutput bool `name:"no_test_output"`
    95  	// True if this target needs access to its transitive dependencies to build.
    96  	// This would be false for most 'normal' genrules but true for eg. compiler steps
    97  	// that need to build in everything.
    98  	NeedsTransitiveDependencies bool `name:"needs_transitive_deps"`
    99  	// True if this target blocks recursive exploring for transitive dependencies.
   100  	// This is typically false for _library rules which aren't complete, and true
   101  	// for _binary rules which normally are, and genrules where you don't care about
   102  	// the inputs, only whatever they were turned into.
   103  	OutputIsComplete bool `name:"output_is_complete"`
   104  	// If true, the rule is given an env var at build time that contains the hash of its
   105  	// transitive dependencies, which can be used to identify the output in a predictable way.
   106  	Stamp bool
   107  	// Marks the target as a filegroup.
   108  	IsFilegroup bool `print:"false"`
   109  	// Marks the target as a hash_filegroup.
   110  	IsHashFilegroup bool `print:"false"`
   111  	// Marks the target as a remote_file.
   112  	IsRemoteFile bool `print:"false"`
   113  	// Marks that the target was added in a post-build function.
   114  	AddedPostBuild bool `print:"false"`
   115  	// If true, the interactive progress display will try to infer the target's progress
   116  	// via some heuristics on its output.
   117  	ShowProgress bool `name:"progress"`
   118  	// If ShowProgress is true, this is used to store the current progress of the target.
   119  	Progress float32 `print:"false"`
   120  	// Containerisation settings that override the defaults.
   121  	ContainerSettings *TargetContainerSettings `name:"container"`
   122  	// The results of this test target, if it is one.
   123  	Results TestSuite `print:"false"`
   124  	// Description displayed while the command is building.
   125  	// Default is just "Building" but it can be customised.
   126  	BuildingDescription string `name:"building_description"`
   127  	// Acceptable hashes of the outputs of this rule. If the output doesn't match any of these
   128  	// it's an error at build time. Can be used to validate third-party deps.
   129  	Hashes []string
   130  	// Licences that this target is subject to.
   131  	Licences []string
   132  	// Any secrets that this rule requires.
   133  	// Secrets are similar to sources but are always absolute system paths and affect the hash
   134  	// differently; they are not used to determine the hash for retrieving a file from cache, but
   135  	// if changed locally will still force a rebuild. They're not copied into the source directory
   136  	// (or indeed anywhere by plz).
   137  	Secrets []string
   138  	// BUILD language functions to call before / after target is built. Allows deferred manipulation of the build graph.
   139  	PreBuildFunction  PreBuildFunction  `name:"pre_build"`
   140  	PostBuildFunction PostBuildFunction `name:"post_build"`
   141  	// Languages this rule requires. These are an arbitrary set and the only meaning is that they
   142  	// correspond to entries in Provides; if rules match up then it allows choosing a specific
   143  	// dependency (consider eg. code generated from protobufs; this mechanism allows us to expose
   144  	// one rule but only compile the appropriate code for each library that consumes it).
   145  	Requires []string
   146  	// Dependent rules this rule provides for each language. Matches up to Requires as described above.
   147  	Provides map[string]BuildLabel
   148  	// Stores the hash of this build rule before any post-build function is run.
   149  	RuleHash []byte `name:"exported_deps"` // bit of a hack to call this exported_deps...
   150  	// Tools that this rule will use, ie. other rules that it may use at build time which are not
   151  	// copied into its source directory.
   152  	Tools []BuildInput
   153  	// Named tools, similar to named sources.
   154  	namedTools map[string][]BuildInput `name:"tools"`
   155  	// Flakiness of test, ie. number of times we will rerun it before giving up. 1 is the default.
   156  	Flakiness int `name:"flaky"`
   157  	// Timeouts for build/test actions
   158  	BuildTimeout time.Duration `name:"timeout"`
   159  	TestTimeout  time.Duration `name:"test_timeout"`
   160  	// Extra output files from the test.
   161  	// These are in addition to the usual test.results output file.
   162  	TestOutputs []string
   163  }
   164  
   165  // A PreBuildFunction is a type that allows hooking a pre-build callback.
   166  type PreBuildFunction interface {
   167  	fmt.Stringer
   168  	// Call calls this pre-build function
   169  	Call(target *BuildTarget) error
   170  }
   171  
   172  // A PostBuildFunction is a type that allows hooking a post-build callback.
   173  type PostBuildFunction interface {
   174  	fmt.Stringer
   175  	// Call calls this pre-build function with this target and its output.
   176  	Call(target *BuildTarget, output string) error
   177  }
   178  
   179  type depInfo struct {
   180  	declared BuildLabel     // the originally declared dependency
   181  	deps     []*BuildTarget // list of actual deps
   182  	resolved bool           // has the graph resolved it
   183  	exported bool           // is it an exported dependency
   184  	source   bool           // is it implicit because it's a source (not true if it's a dependency too)
   185  	data     bool           // is it a data item for a test
   186  }
   187  
   188  // A BuildTargetState tracks the current state of this target in regard to whether it's built
   189  // or not. Targets only move forwards through this (i.e. the state of a target only ever increases).
   190  type BuildTargetState int32
   191  
   192  // The available states for a target.
   193  const (
   194  	Inactive   BuildTargetState = iota // Target isn't used in current build
   195  	Semiactive                         // Target would be active if we needed a build
   196  	Active                             // Target is going to be used in current build
   197  	Pending                            // Target is ready to be built but not yet started.
   198  	Building                           // Target is currently being built
   199  	Stopped                            // We stopped building the target because we'd gone as far as needed.
   200  	Built                              // Target has been successfully built
   201  	Cached                             // Target has been retrieved from the cache
   202  	Unchanged                          // Target has been built but hasn't changed since last build
   203  	Reused                             // Outputs of previous build have been reused.
   204  	Failed                             // Target failed for some reason
   205  )
   206  
   207  // String implements the fmt.Stringer interface.
   208  func (s BuildTargetState) String() string {
   209  	if s == Inactive {
   210  		return "Inactive"
   211  	} else if s == Semiactive {
   212  		return "Semiactive"
   213  	} else if s == Active {
   214  		return "Active"
   215  	} else if s == Pending {
   216  		return "Pending"
   217  	} else if s == Building {
   218  		return "Building"
   219  	} else if s == Stopped {
   220  		return "Stopped"
   221  	} else if s == Built {
   222  		return "Built"
   223  	} else if s == Cached {
   224  		return "Cached"
   225  	} else if s == Unchanged {
   226  		return "Unchanged"
   227  	} else if s == Reused {
   228  		return "Reused"
   229  	} else if s == Failed {
   230  		return "Failed"
   231  	}
   232  	return "Unknown"
   233  }
   234  
   235  // TargetContainerSettings are known settings controlling containerisation for a particular target.
   236  type TargetContainerSettings struct {
   237  	// Image to use for this test
   238  	DockerImage string `name:"docker_image"`
   239  	// Username / Uid to run as
   240  	DockerUser string `name:"docker_user"`
   241  	// Location to mount a tmpfs at
   242  	Tmpfs string `name:"tmpfs"`
   243  }
   244  
   245  // ToMap returns this struct as a map.
   246  func (settings *TargetContainerSettings) ToMap() map[string]string {
   247  	m := map[string]string{}
   248  	v := reflect.ValueOf(settings).Elem()
   249  	for i := 0; i < v.NumField(); i++ {
   250  		if s := v.Field(i).String(); s != "" {
   251  			m[v.Type().Field(i).Tag.Get("name")] = s
   252  		}
   253  	}
   254  	return m
   255  }
   256  
   257  // NewBuildTarget constructs & returns a new BuildTarget.
   258  func NewBuildTarget(label BuildLabel) *BuildTarget {
   259  	return &BuildTarget{
   260  		Label:               label,
   261  		state:               int32(Inactive),
   262  		BuildingDescription: DefaultBuildingDescription,
   263  	}
   264  }
   265  
   266  // TmpDir returns the temporary working directory for this target, eg.
   267  // //mickey/donald:goofy -> plz-out/tmp/mickey/donald/goofy._build
   268  // Note the extra subdirectory to keep rules separate from one another, and the .build suffix
   269  // to attempt to keep rules from duplicating the names of sub-packages; obviously that is not
   270  // 100% reliable but we don't have a better solution right now.
   271  func (target *BuildTarget) TmpDir() string {
   272  	return path.Join(TmpDir, target.Label.Subrepo, target.Label.PackageName, target.Label.Name+buildDirSuffix)
   273  }
   274  
   275  // OutDir returns the output directory for this target, eg.
   276  // //mickey/donald:goofy -> plz-out/gen/mickey/donald (or plz-out/bin if it's a binary)
   277  func (target *BuildTarget) OutDir() string {
   278  	if target.IsBinary {
   279  		return path.Join(BinDir, target.Label.Subrepo, target.Label.PackageName)
   280  	}
   281  	return path.Join(GenDir, target.Label.Subrepo, target.Label.PackageName)
   282  }
   283  
   284  // TestDir returns the test directory for this target, eg.
   285  // //mickey/donald:goofy -> plz-out/tmp/mickey/donald/goofy._test
   286  // This is different to TmpDir so we run tests in a clean environment
   287  // and to facilitate containerising tests.
   288  func (target *BuildTarget) TestDir() string {
   289  	return path.Join(TmpDir, target.Label.Subrepo, target.Label.PackageName, target.Label.Name+testDirSuffix)
   290  }
   291  
   292  // TestResultsFile returns the output results file for tests for this target.
   293  func (target *BuildTarget) TestResultsFile() string {
   294  	return path.Join(target.OutDir(), ".test_results_"+target.Label.Name)
   295  }
   296  
   297  // CoverageFile returns the output coverage file for tests for this target.
   298  func (target *BuildTarget) CoverageFile() string {
   299  	return path.Join(target.OutDir(), ".test_coverage_"+target.Label.Name)
   300  }
   301  
   302  // AllSourcePaths returns all the source paths for this target
   303  func (target *BuildTarget) AllSourcePaths(graph *BuildGraph) []string {
   304  	return target.allSourcePaths(graph, BuildInput.Paths)
   305  }
   306  
   307  // AllFullSourcePaths returns all the source paths for this target, with a leading
   308  // plz-out/gen etc if appropriate.
   309  func (target *BuildTarget) AllFullSourcePaths(graph *BuildGraph) []string {
   310  	return target.allSourcePaths(graph, BuildInput.FullPaths)
   311  }
   312  
   313  // AllLocalSourcePaths returns the local part of all the source paths for this target,
   314  // i.e. without this target's package in it.
   315  func (target *BuildTarget) AllLocalSourcePaths(graph *BuildGraph) []string {
   316  	return target.allSourcePaths(graph, BuildInput.LocalPaths)
   317  }
   318  
   319  type buildPathsFunc func(BuildInput, *BuildGraph) []string
   320  
   321  func (target *BuildTarget) allSourcePaths(graph *BuildGraph, full buildPathsFunc) []string {
   322  	ret := make([]string, 0, len(target.Sources))
   323  	for _, source := range target.AllSources() {
   324  		ret = append(ret, target.sourcePaths(graph, source, full)...)
   325  	}
   326  	return ret
   327  }
   328  
   329  // DeclaredDependencies returns all the targets this target declared any kind of dependency on (including sources and tools).
   330  func (target *BuildTarget) DeclaredDependencies() []BuildLabel {
   331  	ret := make(BuildLabels, len(target.dependencies))
   332  	for i, dep := range target.dependencies {
   333  		ret[i] = dep.declared
   334  	}
   335  	sort.Sort(ret)
   336  	return ret
   337  }
   338  
   339  // DeclaredDependenciesStrict returns the original declaration of this target's dependencies.
   340  func (target *BuildTarget) DeclaredDependenciesStrict() []BuildLabel {
   341  	ret := make(BuildLabels, 0, len(target.dependencies))
   342  	for _, dep := range target.dependencies {
   343  		if !dep.exported && !dep.source && !target.IsTool(dep.declared) {
   344  			ret = append(ret, dep.declared)
   345  		}
   346  	}
   347  	sort.Sort(ret)
   348  	return ret
   349  }
   350  
   351  // Dependencies returns the resolved dependencies of this target.
   352  func (target *BuildTarget) Dependencies() []*BuildTarget {
   353  	ret := make(BuildTargets, 0, len(target.dependencies))
   354  	for _, deps := range target.dependencies {
   355  		for _, dep := range deps.deps {
   356  			ret = append(ret, dep)
   357  		}
   358  	}
   359  	sort.Sort(ret)
   360  	return ret
   361  }
   362  
   363  // BuildDependencies returns the build-time dependencies of this target (i.e. not data).
   364  func (target *BuildTarget) BuildDependencies() []*BuildTarget {
   365  	ret := make(BuildTargets, 0, len(target.dependencies))
   366  	for _, deps := range target.dependencies {
   367  		if !deps.data {
   368  			for _, dep := range deps.deps {
   369  				ret = append(ret, dep)
   370  			}
   371  		}
   372  	}
   373  	sort.Sort(ret)
   374  	return ret
   375  }
   376  
   377  // ExportedDependencies returns any exported dependencies of this target.
   378  func (target *BuildTarget) ExportedDependencies() []BuildLabel {
   379  	ret := make(BuildLabels, 0, len(target.dependencies))
   380  	for _, info := range target.dependencies {
   381  		if info.exported {
   382  			ret = append(ret, info.declared)
   383  		}
   384  	}
   385  	return ret
   386  }
   387  
   388  // DependenciesFor returns the dependencies that relate to a given label.
   389  func (target *BuildTarget) DependenciesFor(label BuildLabel) []*BuildTarget {
   390  	info := target.dependencyInfo(label)
   391  	if info != nil {
   392  		return info.deps
   393  	}
   394  	return nil
   395  }
   396  
   397  // DeclaredOutputs returns the outputs from this target's original declaration.
   398  // Hence it's similar to Outputs() but without the resolving of other rule names.
   399  func (target *BuildTarget) DeclaredOutputs() []string {
   400  	return target.outputs
   401  }
   402  
   403  // DeclaredNamedOutputs returns the named outputs from this target's original declaration.
   404  func (target *BuildTarget) DeclaredNamedOutputs() map[string][]string {
   405  	return target.namedOutputs
   406  }
   407  
   408  // DeclaredOutputNames is a convenience function to return the names of the declared
   409  // outputs in a consistent order.
   410  func (target *BuildTarget) DeclaredOutputNames() []string {
   411  	ret := make([]string, 0, len(target.namedOutputs))
   412  	for name := range target.namedOutputs {
   413  		ret = append(ret, name)
   414  	}
   415  	sort.Strings(ret)
   416  	return ret
   417  }
   418  
   419  // Outputs returns a slice of all the outputs of this rule.
   420  func (target *BuildTarget) Outputs() []string {
   421  	var ret []string
   422  	if target.IsFilegroup && !target.IsHashFilegroup {
   423  		ret = make([]string, 0, len(target.Sources))
   424  		// Filegroups just re-output their inputs.
   425  		for _, src := range target.Sources {
   426  			if namedLabel, ok := src.(NamedOutputLabel); ok {
   427  				// Bit of a hack, but this needs different treatment from either of the others.
   428  				for _, dep := range target.DependenciesFor(namedLabel.BuildLabel) {
   429  					ret = append(ret, dep.NamedOutputs(namedLabel.Output)...)
   430  				}
   431  			} else if label := src.nonOutputLabel(); label == nil {
   432  				ret = append(ret, src.LocalPaths(nil)[0])
   433  			} else {
   434  				for _, dep := range target.DependenciesFor(*label) {
   435  					ret = append(ret, dep.Outputs()...)
   436  				}
   437  			}
   438  		}
   439  	} else {
   440  		// Must really copy the slice before sorting it ([:] is too shallow)
   441  		ret = make([]string, len(target.outputs))
   442  		copy(ret, target.outputs)
   443  	}
   444  	if target.namedOutputs != nil {
   445  		for _, outputs := range target.namedOutputs {
   446  			ret = append(ret, outputs...)
   447  		}
   448  	}
   449  	sort.Strings(ret)
   450  	return ret
   451  }
   452  
   453  // FullOutputs returns a slice of all the outputs of this rule with the target's output directory prepended.
   454  func (target *BuildTarget) FullOutputs() []string {
   455  	outs := target.Outputs()
   456  	outDir := target.OutDir()
   457  	for i, out := range outs {
   458  		outs[i] = path.Join(outDir, out)
   459  	}
   460  	return outs
   461  }
   462  
   463  // NamedOutputs returns a slice of all the outputs of this rule with a given name.
   464  // If the name is not declared by this rule it panics.
   465  func (target *BuildTarget) NamedOutputs(name string) []string {
   466  	if target.namedOutputs == nil {
   467  		return nil
   468  	}
   469  	if outs, present := target.namedOutputs[name]; present {
   470  		return outs
   471  	}
   472  	return nil
   473  }
   474  
   475  // GetTmpOutput takes the original output filename as an argument, and returns a temporary output
   476  // filename(plz-out/tmp/) if output has the same name as the package, this avoids the name conflict issue
   477  func (target *BuildTarget) GetTmpOutput(parseOutput string) string {
   478  	if parseOutput == target.Label.PackageName {
   479  		return parseOutput + ".out"
   480  	}
   481  	return parseOutput
   482  }
   483  
   484  // GetTmpOutputAll returns a slice of all the temporary outputs this is used in setting up environment for outputs,
   485  // e.g: OUTS, OUT
   486  func (target *BuildTarget) GetTmpOutputAll(parseOutputs []string) []string {
   487  	tmpOutputs := make([]string, len(parseOutputs))
   488  	for i, out := range parseOutputs {
   489  		tmpOutputs[i] = target.GetTmpOutput(out)
   490  	}
   491  	return tmpOutputs
   492  }
   493  
   494  // SourcePaths returns the source paths for a given set of sources.
   495  func (target *BuildTarget) SourcePaths(graph *BuildGraph, sources []BuildInput) []string {
   496  	ret := make([]string, 0, len(sources))
   497  	for _, source := range sources {
   498  		ret = append(ret, target.sourcePaths(graph, source, BuildInput.Paths)...)
   499  	}
   500  	return ret
   501  }
   502  
   503  // sourcePaths returns the source paths for a single source.
   504  func (target *BuildTarget) sourcePaths(graph *BuildGraph, source BuildInput, f buildPathsFunc) []string {
   505  	if label := source.nonOutputLabel(); label != nil {
   506  		ret := []string{}
   507  		for _, providedLabel := range graph.TargetOrDie(*label).ProvideFor(target) {
   508  			ret = append(ret, f(providedLabel, graph)...)
   509  		}
   510  		return ret
   511  	}
   512  	return f(source, graph)
   513  }
   514  
   515  // allDepsBuilt returns true if all the dependencies of a target are built.
   516  func (target *BuildTarget) allDepsBuilt() bool {
   517  	if !target.allDependenciesResolved() {
   518  		return false // Target still has some deps pending parse.
   519  	}
   520  	for _, deps := range target.dependencies {
   521  		for _, dep := range deps.deps {
   522  			if dep.State() < Built {
   523  				return false
   524  			}
   525  		}
   526  	}
   527  	return true
   528  }
   529  
   530  // allDependenciesResolved returns true once all the dependencies of a target have been
   531  // parsed and resolved to real targets.
   532  func (target *BuildTarget) allDependenciesResolved() bool {
   533  	for _, deps := range target.dependencies {
   534  		if !deps.resolved {
   535  			return false
   536  		}
   537  	}
   538  	return true
   539  }
   540  
   541  // isExperimental returns true if the given target is in the "experimental" tree
   542  func isExperimental(state *BuildState, target *BuildTarget) bool {
   543  	for _, exp := range state.experimentalLabels {
   544  		if exp.Includes(target.Label) {
   545  			return true
   546  		}
   547  	}
   548  	return false
   549  }
   550  
   551  // CanSee returns true if target can see the given dependency, or false if not.
   552  func (target *BuildTarget) CanSee(state *BuildState, dep *BuildTarget) bool {
   553  	// Targets are always visible to other targets in the same directory.
   554  	if target.Label.PackageName == dep.Label.PackageName {
   555  		return true
   556  	} else if isExperimental(state, dep) && !isExperimental(state, target) {
   557  		log.Error("Target %s cannot depend on experimental target %s", target.Label, dep.Label)
   558  		return false
   559  	}
   560  	parent := target.Label.Parent()
   561  	for _, vis := range dep.Visibility {
   562  		if vis.Includes(parent) {
   563  			return true
   564  		}
   565  	}
   566  	if dep.Label.PackageName == parent.PackageName {
   567  		return true
   568  	}
   569  	if isExperimental(state, target) {
   570  		log.Warning("Visibility restrictions suppressed for %s since %s is in the experimental tree", dep.Label, target.Label)
   571  		return true
   572  	}
   573  	return false
   574  }
   575  
   576  // CheckDependencyVisibility checks that all declared dependencies of this target are visible to it.
   577  // Returns an error if not, or nil if all's well.
   578  func (target *BuildTarget) CheckDependencyVisibility(state *BuildState) error {
   579  	for _, d := range target.dependencies {
   580  		dep := state.Graph.TargetOrDie(d.declared)
   581  		if !target.CanSee(state, dep) {
   582  			return fmt.Errorf("Target %s isn't visible to %s", dep.Label, target.Label)
   583  		} else if dep.TestOnly && !(target.IsTest || target.TestOnly) {
   584  			if isExperimental(state, target) {
   585  				log.Warning("Test-only restrictions suppressed for %s since %s is in the experimental tree", dep.Label, target.Label)
   586  			} else {
   587  				return fmt.Errorf("Target %s can't depend on %s, it's marked test_only", target.Label, dep.Label)
   588  			}
   589  		}
   590  	}
   591  	return nil
   592  }
   593  
   594  // CheckDuplicateOutputs checks if any of the outputs of this target duplicate one another.
   595  // Returns an error if so, or nil if all's well.
   596  func (target *BuildTarget) CheckDuplicateOutputs() error {
   597  	outputs := map[string]struct{}{}
   598  	for _, output := range target.Outputs() {
   599  		if _, present := outputs[output]; present {
   600  			return fmt.Errorf("Target %s declares output file %s multiple times", target.Label, output)
   601  		}
   602  		outputs[output] = struct{}{}
   603  	}
   604  	return nil
   605  }
   606  
   607  // CheckSecrets checks that this target's secrets are available.
   608  // We run this check before building because we don't attempt to copy them, but any rule
   609  // requiring them will presumably fail if they aren't available.
   610  // Returns an error if any aren't.
   611  func (target *BuildTarget) CheckSecrets() error {
   612  	for _, secret := range target.Secrets {
   613  		if path := ExpandHomePath(secret); !PathExists(path) {
   614  			return fmt.Errorf("Path %s doesn't exist; it's required to build %s", secret, target.Label)
   615  		}
   616  	}
   617  	return nil
   618  }
   619  
   620  // HasDependency checks if a target already depends on this label.
   621  func (target *BuildTarget) HasDependency(label BuildLabel) bool {
   622  	return target.dependencyInfo(label) != nil
   623  }
   624  
   625  // hasResolvedDependency returns true if a particular dependency has been resolved to real targets yet.
   626  func (target *BuildTarget) hasResolvedDependency(label BuildLabel) bool {
   627  	info := target.dependencyInfo(label)
   628  	return info != nil && info.resolved
   629  }
   630  
   631  // resolveDependency resolves a particular dependency on a target.
   632  func (target *BuildTarget) resolveDependency(label BuildLabel, dep *BuildTarget) {
   633  	info := target.dependencyInfo(label)
   634  	if info == nil {
   635  		target.dependencies = append(target.dependencies, depInfo{declared: label})
   636  		info = &target.dependencies[len(target.dependencies)-1]
   637  	}
   638  	if dep != nil {
   639  		info.deps = append(info.deps, dep)
   640  	}
   641  	info.resolved = true
   642  }
   643  
   644  // dependencyInfo returns the information about a declared dependency, or nil if the target doesn't have it.
   645  func (target *BuildTarget) dependencyInfo(label BuildLabel) *depInfo {
   646  	for i, info := range target.dependencies {
   647  		if info.declared == label {
   648  			return &target.dependencies[i]
   649  		}
   650  	}
   651  	return nil
   652  }
   653  
   654  // IsSourceOnlyDep returns true if the given dependency was only declared on the srcs of the target.
   655  func (target *BuildTarget) IsSourceOnlyDep(label BuildLabel) bool {
   656  	info := target.dependencyInfo(label)
   657  	return info != nil && info.source
   658  }
   659  
   660  // State returns the target's current state.
   661  func (target *BuildTarget) State() BuildTargetState {
   662  	return BuildTargetState(atomic.LoadInt32(&target.state))
   663  }
   664  
   665  // SetState sets a target's current state.
   666  func (target *BuildTarget) SetState(state BuildTargetState) {
   667  	atomic.StoreInt32(&target.state, int32(state))
   668  }
   669  
   670  // SyncUpdateState oves the target's state from before to after via a lock.
   671  // Returns true if successful, false if not (which implies something else changed the state first).
   672  // The nature of our build graph ensures that most transitions are only attempted by
   673  // one thread simultaneously, but this one can be attempted by several at once
   674  // (eg. if a depends on b and c, which finish building simultaneously, they race to queue a).
   675  func (target *BuildTarget) SyncUpdateState(before, after BuildTargetState) bool {
   676  	return atomic.CompareAndSwapInt32(&target.state, int32(before), int32(after))
   677  }
   678  
   679  // AddLabel adds the given label to this target if it doesn't already have it.
   680  func (target *BuildTarget) AddLabel(label string) {
   681  	if !target.HasLabel(label) {
   682  		target.Labels = append(target.Labels, label)
   683  	}
   684  }
   685  
   686  // HasLabel returns true if target has the given label.
   687  func (target *BuildTarget) HasLabel(label string) bool {
   688  	for _, l := range target.Labels {
   689  		if l == label {
   690  			return true
   691  		}
   692  	}
   693  	return label == "test" && target.IsTest
   694  }
   695  
   696  // PrefixedLabels returns all labels of this target with the given prefix.
   697  func (target *BuildTarget) PrefixedLabels(prefix string) []string {
   698  	ret := []string{}
   699  	for _, l := range target.Labels {
   700  		if strings.HasPrefix(l, prefix) {
   701  			ret = append(ret, strings.TrimPrefix(l, prefix))
   702  		}
   703  	}
   704  	return ret
   705  }
   706  
   707  // HasAnyLabel returns true if target has any of these labels.
   708  func (target *BuildTarget) HasAnyLabel(labels []string) bool {
   709  	for _, label := range labels {
   710  		if target.HasLabel(label) {
   711  			return true
   712  		}
   713  	}
   714  	return false
   715  }
   716  
   717  // HasAllLabels returns true if target has all of these labels.
   718  func (target *BuildTarget) HasAllLabels(labels []string) bool {
   719  	for _, label := range labels {
   720  		if !target.HasLabel(label) {
   721  			return false
   722  		}
   723  	}
   724  	return true
   725  }
   726  
   727  // ShouldInclude handles the typical include/exclude logic for a target's labels; returns true if
   728  // target has any include label and not an exclude one.
   729  // Each include/exclude can have multiple comma-separated labels; in this case, all of the labels
   730  // in a given group must match.
   731  func (target *BuildTarget) ShouldInclude(includes, excludes []string) bool {
   732  	if target.HasLabel("manual") || target.HasLabel("manual:"+OsArch) {
   733  		return false
   734  	}
   735  
   736  	if len(includes) == 0 && len(excludes) == 0 {
   737  		return true
   738  	}
   739  
   740  	// Include by default if no includes are specified.
   741  	shouldInclude := len(includes) == 0
   742  	for _, include := range includes {
   743  		if target.HasAllLabels(strings.Split(include, ",")) {
   744  			shouldInclude = true
   745  			break
   746  		}
   747  	}
   748  	for _, exclude := range excludes {
   749  		if target.HasAllLabels(strings.Split(exclude, ",")) {
   750  			shouldInclude = false
   751  			break
   752  		}
   753  	}
   754  	return shouldInclude
   755  }
   756  
   757  // AddProvide adds a new provide entry to this target.
   758  func (target *BuildTarget) AddProvide(language string, label BuildLabel) {
   759  	if target.Provides == nil {
   760  		target.Provides = map[string]BuildLabel{language: label}
   761  	} else {
   762  		target.Provides[language] = label
   763  	}
   764  }
   765  
   766  // ProvideFor returns the build label that we'd provide for the given target.
   767  func (target *BuildTarget) ProvideFor(other *BuildTarget) []BuildLabel {
   768  	ret := []BuildLabel{}
   769  	if target.Provides != nil {
   770  		// Never do this if the other target has a data dependency on us.
   771  		for _, data := range other.Data {
   772  			if label := data.Label(); label != nil && *label == target.Label {
   773  				return []BuildLabel{target.Label}
   774  			}
   775  		}
   776  		for _, require := range other.Requires {
   777  			if label, present := target.Provides[require]; present {
   778  				ret = append(ret, label)
   779  			}
   780  		}
   781  		if len(ret) > 0 {
   782  			return ret
   783  		}
   784  	}
   785  	return []BuildLabel{target.Label}
   786  }
   787  
   788  // AddSource adds a source to the build target, deduplicating against existing entries.
   789  func (target *BuildTarget) AddSource(source BuildInput) {
   790  	target.Sources = target.addSource(target.Sources, source)
   791  }
   792  
   793  func (target *BuildTarget) addSource(sources []BuildInput, source BuildInput) []BuildInput {
   794  	for _, src := range sources {
   795  		if source == src {
   796  			return sources
   797  		}
   798  	}
   799  	// Add a dependency if this is not just a file.
   800  	if label := source.Label(); label != nil {
   801  		target.AddMaybeExportedDependency(*label, false, true)
   802  	}
   803  	return append(sources, source)
   804  }
   805  
   806  // AddNamedSource adds a source to the target which is tagged with a particular name.
   807  // For example, C++ rules add sources tagged as "sources" and "headers" to distinguish
   808  // two conceptually different kinds of input.
   809  func (target *BuildTarget) AddNamedSource(name string, source BuildInput) {
   810  	if target.NamedSources == nil {
   811  		target.NamedSources = map[string][]BuildInput{name: target.addSource(nil, source)}
   812  	} else {
   813  		target.NamedSources[name] = target.addSource(target.NamedSources[name], source)
   814  	}
   815  }
   816  
   817  // AddTool adds a new tool to the target.
   818  func (target *BuildTarget) AddTool(tool BuildInput) {
   819  	target.Tools = append(target.Tools, tool)
   820  	if label := tool.Label(); label != nil {
   821  		target.AddDependency(*label)
   822  	}
   823  }
   824  
   825  // AddDatum adds a new item of data to the target.
   826  func (target *BuildTarget) AddDatum(datum BuildInput) {
   827  	target.Data = append(target.Data, datum)
   828  	if label := datum.Label(); label != nil {
   829  		target.AddDependency(*label)
   830  		target.dependencyInfo(*label).data = true
   831  	}
   832  }
   833  
   834  // AddNamedTool adds a new tool to the target.
   835  func (target *BuildTarget) AddNamedTool(name string, tool BuildInput) {
   836  	if target.namedTools == nil {
   837  		target.namedTools = map[string][]BuildInput{name: {tool}}
   838  	} else {
   839  		target.namedTools[name] = append(target.namedTools[name], tool)
   840  	}
   841  	if label := tool.Label(); label != nil {
   842  		target.AddDependency(*label)
   843  	}
   844  }
   845  
   846  // AddCommand adds a new config-specific command to this build target.
   847  // Adding a general command is still done by simply setting the Command member.
   848  func (target *BuildTarget) AddCommand(config, command string) {
   849  	if target.Command != "" {
   850  		panic(fmt.Sprintf("Adding named command %s to %s, but it already has a general command set", config, target.Label))
   851  	} else if target.Commands == nil {
   852  		target.Commands = map[string]string{config: command}
   853  	} else {
   854  		target.Commands[config] = command
   855  	}
   856  }
   857  
   858  // AddTestCommand adds a new config-specific test command to this build target.
   859  // Adding a general command is still done by simply setting the TestCommand member.
   860  func (target *BuildTarget) AddTestCommand(config, command string) {
   861  	if target.TestCommand != "" {
   862  		panic(fmt.Sprintf("Adding named test command %s to %s, but it already has a general test command set", config, target.Label))
   863  	} else if target.TestCommands == nil {
   864  		target.TestCommands = map[string]string{config: command}
   865  	} else {
   866  		target.TestCommands[config] = command
   867  	}
   868  }
   869  
   870  // GetCommand returns the command we should use to build this target for the current config.
   871  func (target *BuildTarget) GetCommand(state *BuildState) string {
   872  	return target.getCommand(state, target.Commands, target.Command)
   873  }
   874  
   875  // GetCommandConfig returns the command we should use to build this target for the given config.
   876  func (target *BuildTarget) GetCommandConfig(config string) string {
   877  	if config == "" {
   878  		return target.Command
   879  	}
   880  	return target.Commands[config]
   881  }
   882  
   883  // GetTestCommand returns the command we should use to test this target for the current config.
   884  func (target *BuildTarget) GetTestCommand(state *BuildState) string {
   885  	return target.getCommand(state, target.TestCommands, target.TestCommand)
   886  }
   887  
   888  func (target *BuildTarget) getCommand(state *BuildState, commands map[string]string, singleCommand string) string {
   889  	if commands == nil {
   890  		return singleCommand
   891  	} else if command, present := commands[state.Config.Build.Config]; present {
   892  		return command // Has command for current config, good
   893  	} else if command, present := commands[state.Config.Build.FallbackConfig]; present {
   894  		return command // Has command for default config, fall back to that
   895  	}
   896  	// Oh dear, target doesn't have any matching config. Panicking is a bit heavy here, instead
   897  	// fall back to an arbitrary (but consistent) one.
   898  	highestCommand := ""
   899  	highestConfig := ""
   900  	for config, command := range commands {
   901  		if config > highestConfig {
   902  			highestConfig = config
   903  			highestCommand = command
   904  		}
   905  	}
   906  	log.Warning("%s doesn't have a command for %s (or %s), falling back to %s",
   907  		target.Label, state.Config.Build.Config, state.Config.Build.FallbackConfig, highestConfig)
   908  	return highestCommand
   909  }
   910  
   911  // AllSources returns all the sources of this rule.
   912  func (target *BuildTarget) AllSources() []BuildInput {
   913  	ret := target.Sources[:]
   914  	if target.NamedSources != nil {
   915  		keys := make([]string, 0, len(target.NamedSources))
   916  		for k := range target.NamedSources {
   917  			keys = append(keys, k)
   918  		}
   919  		sort.Strings(keys)
   920  		for _, k := range keys {
   921  			ret = append(ret, target.NamedSources[k]...)
   922  		}
   923  	}
   924  	return ret
   925  }
   926  
   927  // AllLocalSources returns all the "local" sources of this rule, i.e. all sources that are
   928  // actually sources in the repo, not other rules or system srcs etc.
   929  func (target *BuildTarget) AllLocalSources() []string {
   930  	ret := []string{}
   931  	for _, src := range target.AllSources() {
   932  		if file, ok := src.(FileLabel); ok {
   933  			ret = append(ret, file.Paths(nil)[0])
   934  		}
   935  	}
   936  	return ret
   937  }
   938  
   939  // HasSource returns true if this target has the given file as a source (named or not, or data).
   940  func (target *BuildTarget) HasSource(source string) bool {
   941  	for _, src := range append(target.AllSources(), target.Data...) {
   942  		if src.String() == source { // Comparison is a bit dodgy tbh
   943  			return true
   944  		}
   945  	}
   946  	return false
   947  }
   948  
   949  // HasAbsoluteSource returns true if this target has the given file as a source (or data).
   950  // The input source includes the target's package name.
   951  func (target *BuildTarget) HasAbsoluteSource(source string) bool {
   952  	return target.HasSource(strings.TrimPrefix(source, target.Label.PackageName+"/"))
   953  }
   954  
   955  // AllTools returns all the tools for this rule in some canonical order.
   956  func (target *BuildTarget) AllTools() []BuildInput {
   957  	if target.namedTools == nil {
   958  		return target.Tools // Leave them in input order, that's sufficiently consistent.
   959  	}
   960  	tools := make([]BuildInput, len(target.Tools), len(target.Tools)+len(target.namedTools)*2)
   961  	copy(tools, target.Tools)
   962  	for _, name := range target.ToolNames() {
   963  		tools = append(tools, target.namedTools[name]...)
   964  	}
   965  	return tools
   966  }
   967  
   968  // ToolNames returns an ordered list of tool names.
   969  func (target *BuildTarget) ToolNames() []string {
   970  	ret := make([]string, 0, len(target.namedTools))
   971  	for name := range target.namedTools {
   972  		ret = append(ret, name)
   973  	}
   974  	sort.Strings(ret)
   975  	return ret
   976  }
   977  
   978  // NamedTools returns the tools with the given name.
   979  func (target *BuildTarget) NamedTools(name string) []BuildInput {
   980  	return target.namedTools[name]
   981  }
   982  
   983  // AllData returns all the data paths for this target.
   984  func (target *BuildTarget) AllData(graph *BuildGraph) []string {
   985  	ret := make([]string, 0, len(target.Data))
   986  	for _, datum := range target.Data {
   987  		ret = append(ret, datum.Paths(graph)...)
   988  	}
   989  	return ret
   990  }
   991  
   992  // AddDependency adds a dependency to this target. It deduplicates against any existing deps.
   993  func (target *BuildTarget) AddDependency(dep BuildLabel) {
   994  	target.AddMaybeExportedDependency(dep, false, false)
   995  }
   996  
   997  // AddMaybeExportedDependency adds a dependency to this target which may be exported. It deduplicates against any existing deps.
   998  func (target *BuildTarget) AddMaybeExportedDependency(dep BuildLabel, exported, source bool) {
   999  	if dep == target.Label {
  1000  		log.Fatalf("Attempted to add %s as a dependency of itself.\n", dep)
  1001  	}
  1002  	info := target.dependencyInfo(dep)
  1003  	if info == nil {
  1004  		target.dependencies = append(target.dependencies, depInfo{declared: dep, exported: exported, source: source})
  1005  	} else {
  1006  		info.exported = info.exported || exported
  1007  		info.source = info.source && source
  1008  		info.data = false // It's not *only* data any more.
  1009  	}
  1010  }
  1011  
  1012  // IsTool returns true if the given build label is a tool used by this target.
  1013  func (target *BuildTarget) IsTool(tool BuildLabel) bool {
  1014  	for _, t := range target.Tools {
  1015  		if t == tool {
  1016  			return true
  1017  		}
  1018  	}
  1019  	for _, tools := range target.namedTools {
  1020  		for _, t := range tools {
  1021  			if t == tool {
  1022  				return true
  1023  			}
  1024  		}
  1025  	}
  1026  	return false
  1027  }
  1028  
  1029  // toolPath returns a path to this target when used as a tool.
  1030  func (target *BuildTarget) toolPath() string {
  1031  	outputs := target.Outputs()
  1032  	ret := make([]string, len(outputs))
  1033  	for i, o := range outputs {
  1034  		ret[i], _ = filepath.Abs(path.Join(target.OutDir(), o))
  1035  	}
  1036  	return strings.Join(ret, " ")
  1037  }
  1038  
  1039  // AddOutput adds a new output to the target if it's not already there.
  1040  func (target *BuildTarget) AddOutput(output string) {
  1041  	target.outputs = target.insert(target.outputs, output)
  1042  }
  1043  
  1044  // AddOptionalOutput adds a new optional output to the target if it's not already there.
  1045  func (target *BuildTarget) AddOptionalOutput(output string) {
  1046  	target.OptionalOutputs = target.insert(target.OptionalOutputs, output)
  1047  }
  1048  
  1049  // AddTestOutput adds a new test output to the target if it's not already there.
  1050  func (target *BuildTarget) AddTestOutput(output string) {
  1051  	target.TestOutputs = target.insert(target.TestOutputs, output)
  1052  }
  1053  
  1054  // AddNamedOutput adds a new output to the target under a named group.
  1055  // No attempt to deduplicate against unnamed outputs is currently made.
  1056  func (target *BuildTarget) AddNamedOutput(name, output string) {
  1057  	if target.namedOutputs == nil {
  1058  		target.namedOutputs = map[string][]string{name: target.insert(nil, output)}
  1059  		return
  1060  	}
  1061  	target.namedOutputs[name] = target.insert(target.namedOutputs[name], output)
  1062  }
  1063  
  1064  // insert adds a string into a slice if it's not already there. Sorted order is maintained.
  1065  func (target *BuildTarget) insert(sl []string, s string) []string {
  1066  	if s == "" {
  1067  		panic("Cannot add an empty string as an output of a target")
  1068  	}
  1069  	for i, x := range sl {
  1070  		if s == x {
  1071  			// Already present.
  1072  			return sl
  1073  		} else if x > s {
  1074  			// Insert in this location. Make an attempt to be efficient.
  1075  			sl = append(sl, "")
  1076  			copy(sl[i+1:], sl[i:])
  1077  			sl[i] = s
  1078  			return sl
  1079  		}
  1080  	}
  1081  	return append(sl, s)
  1082  }
  1083  
  1084  // AddLicence adds a licence to the target if it's not already there.
  1085  func (target *BuildTarget) AddLicence(licence string) {
  1086  	licence = strings.TrimSpace(licence)
  1087  	for _, l := range target.Licences {
  1088  		if l == licence {
  1089  			return
  1090  		}
  1091  	}
  1092  	target.Licences = append(target.Licences, licence)
  1093  }
  1094  
  1095  // AddHash adds a new acceptable hash to the target.
  1096  func (target *BuildTarget) AddHash(hash string) {
  1097  	target.Hashes = append(target.Hashes, hash)
  1098  }
  1099  
  1100  // AddRequire adds a new requirement to the target.
  1101  func (target *BuildTarget) AddRequire(require string) {
  1102  	target.Requires = append(target.Requires, require)
  1103  	// Requirements are also implicit labels
  1104  	target.AddLabel(require)
  1105  }
  1106  
  1107  // SetContainerSetting sets one of the fields on the container settings by name.
  1108  func (target *BuildTarget) SetContainerSetting(name, value string) error {
  1109  	if target.ContainerSettings == nil {
  1110  		target.ContainerSettings = &TargetContainerSettings{}
  1111  	}
  1112  	t := reflect.TypeOf(*target.ContainerSettings)
  1113  	for i := 0; i < t.NumField(); i++ {
  1114  		if strings.ToLower(t.Field(i).Name) == name {
  1115  			v := reflect.ValueOf(target.ContainerSettings)
  1116  			v.Elem().Field(i).SetString(value)
  1117  			return nil
  1118  		}
  1119  	}
  1120  	return fmt.Errorf("Field %s isn't a valid container setting", name)
  1121  }
  1122  
  1123  // OutMode returns the mode to set outputs of a target to.
  1124  func (target *BuildTarget) OutMode() os.FileMode {
  1125  	if target.IsBinary {
  1126  		return 0555
  1127  	}
  1128  	return 0444
  1129  }
  1130  
  1131  // PostBuildOutputFileName returns the post-build output file for this target.
  1132  func (target *BuildTarget) PostBuildOutputFileName() string {
  1133  	return ".build_output_" + target.Label.Name
  1134  }
  1135  
  1136  // Parent finds the parent of a build target, or nil if the target is parentless.
  1137  // Note that this is a fairly informal relationship; we identify it by labels with the convention of
  1138  // a leading _ and trailing hashtag on child rules, rather than storing pointers between them in the graph.
  1139  // The parent returned, if any, will be the ultimate ancestor of the target.
  1140  func (target *BuildTarget) Parent(graph *BuildGraph) *BuildTarget {
  1141  	parent := target.Label.Parent()
  1142  	if parent == target.Label {
  1143  		return nil
  1144  	}
  1145  	return graph.Target(parent)
  1146  }
  1147  
  1148  // HasParent returns true if the target has a parent rule that's not itself.
  1149  func (target *BuildTarget) HasParent() bool {
  1150  	return target.Label.HasParent()
  1151  }
  1152  
  1153  // BuildTargets makes a slice of build targets sortable by their labels.
  1154  type BuildTargets []*BuildTarget
  1155  
  1156  func (slice BuildTargets) Len() int {
  1157  	return len(slice)
  1158  }
  1159  func (slice BuildTargets) Less(i, j int) bool {
  1160  	return slice[i].Label.Less(slice[j].Label)
  1161  }
  1162  func (slice BuildTargets) Swap(i, j int) {
  1163  	slice[i], slice[j] = slice[j], slice[i]
  1164  }