func TestBuildLotsOfTargets(t *testing.T) { config, _ := core.ReadConfigFiles(nil) state := core.NewBuildState(numWorkers, nil, 4, config) pkg := core.NewPackage("pkg") state.Graph.AddPackage(pkg) for i := 1; i <= size; i++ { addTarget(state, i) } var wg sync.WaitGroup wg.Add(numWorkers) for i := 0; i < numWorkers; i++ { go func() { please(i, state) wg.Done() }() } // Consume and discard any results go func() { for result := range state.Results { assert.NotEqual(t, core.TargetBuildFailed, result.Status) log.Info("%s", result.Description) } }() state.TaskDone() // Initial target adding counts as one. wg.Wait() }
func newState(label string) (*core.BuildState, *core.BuildTarget) { config, _ := core.ReadConfigFiles(nil) state := core.NewBuildState(1, nil, 4, config) target := core.NewBuildTarget(core.ParseBuildLabel(label, "")) target.Command = fmt.Sprintf("echo 'output of %s' > $OUT", target.Label) state.Graph.AddTarget(target) return state, target }
func TestBazelCompatReplacements(t *testing.T) { // Check that we don't do any of these things normally. target := makeTarget("//path/to:target", "cp $< $@", nil) assert.Equal(t, "cp $< $@", replaceSequences(target)) // In Bazel compat mode we do though. state := core.NewBuildState(1, nil, 1, core.DefaultConfiguration()) state.Config.Bazel.Compatibility = true assert.Equal(t, "cp $SRCS $OUTS", replaceSequences(target)) // @D is the output dir, for us it's the tmp dir. target.Command = "cp $SRCS $@D" assert.Equal(t, "cp $SRCS $TMP_DIR", replaceSequences(target)) // This parenthesised syntax seems to be allowed too. target.Command = "cp $(<) $(@)" assert.Equal(t, "cp $SRCS $OUTS", replaceSequences(target)) }
func TestGetSubincludeFile(t *testing.T) { assertError := func(t *testing.T, ret, msg string) { assert.True(t, strings.HasPrefix(ret, "__"), msg) } state := core.NewBuildState(10, nil, 2, core.DefaultConfiguration()) pkg := core.NewPackage("src/parse") pkg2 := core.NewPackage("src/core") assert.Equal(t, pyDeferParse, getSubincludeFile(pkg, "//src/core:target"), "Package not loaded yet, should defer") assertError(t, getSubincludeFile(pkg, "//src/parse:target"), "Should produce an error on attempts for local subincludes.") assertError(t, getSubincludeFile(pkg, ":target"), "Should produce an error on attempts for local subincludes.") state.Graph.AddPackage(pkg) state.Graph.AddPackage(pkg2) assertError(t, getSubincludeFile(pkg, "//src/core:target"), "Produces an error, target does not exist in package.") target := core.NewBuildTarget(core.ParseBuildLabel("//src/core:target", "")) state.Graph.AddTarget(target) assertError(t, getSubincludeFile(pkg, "//src/core:target"), "Errors, target is not visible to subincluding package.") target.Visibility = []core.BuildLabel{core.ParseBuildLabel("//src/parse:all", "")} assertError(t, getSubincludeFile(pkg, "//src/core:target"), "Errors, target doesn't have any outputs to include.") target.AddOutput("test.py") assert.Equal(t, pyDeferParse, getSubincludeFile(pkg, "//src/core:target"), "Target isn't built yet, so still deferred") target.SetState(core.Built) assert.Equal(t, "plz-out/gen/src/core/test.py", getSubincludeFile(pkg, "//src/core:target"), "Success at last") }
func TestGetLabels(t *testing.T) { state := core.NewBuildState(10, nil, 2, core.DefaultConfiguration()) target1 := core.NewBuildTarget(core.ParseBuildLabel("//src/parse:target1", "")) target2 := core.NewBuildTarget(core.ParseBuildLabel("//src/parse:target2", "")) target3 := core.NewBuildTarget(core.ParseBuildLabel("//src/parse:target3", "")) target1.AddLabel("go") target2.AddLabel("py") target3.AddLabel("cc") target1.AddDependency(target2.Label) target1.AddDependency(target3.Label) target2.AddDependency(target3.Label) state.Graph.AddTarget(target1) state.Graph.AddTarget(target2) state.Graph.AddTarget(target3) state.Graph.AddDependency(target1.Label, target2.Label) state.Graph.AddDependency(target1.Label, target3.Label) state.Graph.AddDependency(target2.Label, target3.Label) // Note labels always come out in sorted order. assert.Equal(t, []string{"cc", "go", "py"}, getLabels(target1, "", core.Inactive)) assert.Equal(t, []string{"cc", "py"}, getLabels(target2, "", core.Inactive)) assert.Equal(t, []string{"cc"}, getLabels(target3, "", core.Inactive)) assert.Equal(t, []string{"y"}, getLabels(target1, "p", core.Inactive)) }
// makeState creates a new build state with optionally one or two packages in it. // Used in various tests above. func makeState(withPackage1, withPackage2 bool) *core.BuildState { state := core.NewBuildState(5, nil, 4, core.DefaultConfiguration()) if withPackage1 { pkg := core.NewPackage("package1") state.Graph.AddPackage(pkg) pkg.Targets["target1"] = makeTarget("//package1:target1", "//package1:target2", "//package2:target1") pkg.Targets["target2"] = makeTarget("//package1:target2", "//package2:target1") pkg.Targets["target3"] = makeTarget("//package1:target3", "//package2:target2") state.Graph.AddTarget(pkg.Targets["target1"]) state.Graph.AddTarget(pkg.Targets["target2"]) state.Graph.AddTarget(pkg.Targets["target3"]) addDeps(state.Graph, pkg) } if withPackage2 { pkg := core.NewPackage("package2") state.Graph.AddPackage(pkg) pkg.Targets["target1"] = makeTarget("//package2:target1", "//package2:target2", "//package1:target3") pkg.Targets["target2"] = makeTarget("//package2:target2") state.Graph.AddTarget(pkg.Targets["target1"]) state.Graph.AddTarget(pkg.Targets["target2"]) addDeps(state.Graph, pkg) } return state }
func init() { core.NewBuildState(1, nil, 1, core.DefaultConfiguration()) }
func TestMain(m *testing.M) { core.NewBuildState(10, nil, 2, core.DefaultConfiguration()) // Need to set this before calling parseSource. core.State.Config.Please.BuildFileName = []string{"TEST_BUILD"} os.Exit(m.Run()) }
func TestMain(m *testing.M) { // Need to set this before calling parseSource. It's a bit of a hack but whatevs. buildFileNames = []string{"TEST_BUILD"} core.NewBuildState(10, nil, 2, core.DefaultConfiguration()) os.Exit(m.Run()) }
func init() { cache = newCache(cachePath) core.NewBuildState(1, nil, 4, core.DefaultConfiguration()) }
func main() { parser, extraArgs, flagsErr := cli.ParseFlags("Please", &opts, os.Args) // Note that we must leave flagsErr for later, because it may be affected by aliases. if opts.OutputFlags.Version { fmt.Printf("Please version %s\n", core.PleaseVersion) os.Exit(0) // Ignore other errors if --version was passed. } if opts.OutputFlags.Colour { output.SetColouredOutput(true) } else if opts.OutputFlags.NoColour { output.SetColouredOutput(false) } if opts.OutputFlags.ShowAllOutput { opts.OutputFlags.PlainOutput = true } // Init logging, but don't do file output until we've chdir'd. cli.InitLogging(opts.OutputFlags.Verbosity) command := activeCommand(parser) if command == "init" { if flagsErr != nil { // This error otherwise doesn't get checked until later. cli.ParseFlagsFromArgsOrDie("Please", core.PleaseVersion.String(), &opts, os.Args) } // If we're running plz init then we obviously don't expect to read a config file. utils.InitConfig(opts.Init.Dir, opts.Init.BazelCompatibility) os.Exit(0) } if opts.BuildFlags.RepoRoot == "" { core.FindRepoRoot(true) log.Debug("Found repo root at %s", core.RepoRoot) } else { core.RepoRoot = opts.BuildFlags.RepoRoot } // Please always runs from the repo root, so move there now. if err := os.Chdir(core.RepoRoot); err != nil { log.Fatalf("%s", err) } // Reset this now we're at the repo root. if opts.OutputFlags.LogFile != "" { cli.InitFileLogging(path.Join(core.RepoRoot, opts.OutputFlags.LogFile), opts.OutputFlags.LogFileLevel) } config = readConfig(command == "update") // Set this in case anything wants to use it soon core.NewBuildState(config.Please.NumThreads, nil, opts.OutputFlags.Verbosity, config) // Now we've read the config file, we may need to re-run the parser; the aliases in the config // can affect how we parse otherwise illegal flag combinations. if flagsErr != nil || len(extraArgs) > 0 { argv := strings.Join(os.Args, " ") for k, v := range config.Aliases { argv = strings.Replace(argv, k, v, 1) } parser = cli.ParseFlagsFromArgsOrDie("Please", core.PleaseVersion.String(), &opts, strings.Fields(argv)) command = activeCommand(parser) } if !buildFunctions[command]() { os.Exit(7) // Something distinctive, is sometimes useful to identify this externally. } }
func Please(targets []core.BuildLabel, config *core.Configuration, prettyOutput, shouldBuild, shouldTest bool) (bool, *core.BuildState) { if opts.BuildFlags.NumThreads > 0 { config.Please.NumThreads = opts.BuildFlags.NumThreads } else if config.Please.NumThreads <= 0 { config.Please.NumThreads = runtime.NumCPU() + 2 } if opts.NoCacheCleaner { config.Cache.DirCacheCleaner = "" } if opts.BuildFlags.Config != "" { config.Build.Config = opts.BuildFlags.Config } var c *core.Cache if !opts.FeatureFlags.NoCache { c = cache.NewCache(config) } state := core.NewBuildState(config.Please.NumThreads, c, opts.OutputFlags.Verbosity, config) state.VerifyHashes = !opts.FeatureFlags.NoHashVerification state.NumTestRuns = opts.Test.NumRuns + opts.Cover.NumRuns // Only one of these can be passed. state.TestArgs = append(opts.Test.Args.Args, opts.Cover.Args.Args...) // Similarly here. state.NeedCoverage = !opts.Cover.Args.Target.IsEmpty() state.NeedBuild = shouldBuild state.NeedTests = shouldTest state.NeedHashesOnly = len(opts.Hash.Args.Targets) > 0 state.PrepareOnly = opts.Build.Prepare state.CleanWorkdirs = !opts.FeatureFlags.KeepWorkdirs state.ForceRebuild = len(opts.Rebuild.Args.Targets) > 0 state.ShowTestOutput = opts.Test.ShowOutput || opts.Cover.ShowOutput state.ShowAllOutput = opts.OutputFlags.ShowAllOutput state.SetIncludeAndExclude(opts.BuildFlags.Include, opts.BuildFlags.Exclude) metrics.InitFromConfig(config) // Acquire the lock before we start building if (shouldBuild || shouldTest) && !opts.FeatureFlags.NoLock { core.AcquireRepoLock() defer core.ReleaseRepoLock() } if opts.BuildFlags.Engine != "" { state.Config.Please.ParserEngine = opts.BuildFlags.Engine } // Start looking for the initial targets to kick the build off go findOriginalTasks(state, targets) // Start up all the build workers var wg sync.WaitGroup wg.Add(config.Please.NumThreads) for i := 0; i < config.Please.NumThreads; i++ { go func(tid int) { please(tid, state, opts.ParsePackageOnly, opts.BuildFlags.Include, opts.BuildFlags.Exclude) wg.Done() }(i) } // Wait until they've all exited, which they'll do once they have no tasks left. go func() { wg.Wait() close(state.Results) // This will signal MonitorState (below) to stop. }() // Draw stuff to the screen while there are still results coming through. shouldRun := !opts.Run.Args.Target.IsEmpty() success := output.MonitorState(state, config.Please.NumThreads, !prettyOutput, opts.BuildFlags.KeepGoing, shouldBuild, shouldTest, shouldRun, opts.OutputFlags.TraceFile) metrics.Stop() if c != nil { (*c).Shutdown() } return success, state }