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 }