// CheckAndUpdate checks whether we should update Please and does so if needed. // If it requires an update it will never return, it will either die on failure or on success will exec the new Please. // Conversely, if an update isn't required it will return. It may adjust the version in the configuration. // updatesEnabled indicates whether updates are enabled (i.e. not run with --noupdate) // updateCommand indicates whether an update is specifically requested (due to e.g. `plz update`) // forceUpdate indicates whether the user passed --force on the command line, in which case we // will always update even if the version exists. func CheckAndUpdate(config *core.Configuration, updatesEnabled, updateCommand, forceUpdate bool) { if !forceUpdate && !shouldUpdate(config, updatesEnabled, updateCommand) { return } word := describe(config.Please.Version, core.PleaseVersion, true) log.Warning("%s to Please version %s (currently %s)", word, config.Please.Version, core.PleaseVersion) // Must lock here so that the update process doesn't race when running two instances // simultaneously. core.AcquireRepoLock() defer core.ReleaseRepoLock() // If the destination exists and the user passed --force, remove it to force a redownload. newDir := core.ExpandHomePath(path.Join(config.Please.Location, config.Please.Version.String())) log.Notice("%s", newDir) if forceUpdate && core.PathExists(newDir) { if err := os.RemoveAll(newDir); err != nil { log.Fatalf("Failed to remove existing directory: %s", err) } } // Download it. newPlease := downloadAndLinkPlease(config) // Now run the new one. args := append([]string{newPlease}, os.Args[1:]...) log.Info("Executing %s", strings.Join(args, " ")) if err := syscall.Exec(newPlease, args, os.Environ()); err != nil { log.Fatalf("Failed to exec new Please version %s: %s", newPlease, err) } // Shouldn't ever get here. We should have either exec'd or died above. panic("please update failed in an an unexpected and exciting way") }
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 }