github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/please.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	_ "net/http/pprof"
     7  	"os"
     8  	"path"
     9  	"runtime"
    10  	"runtime/pprof"
    11  	"strings"
    12  	"syscall"
    13  	"time"
    14  
    15  	"github.com/jessevdk/go-flags"
    16  	"gopkg.in/op/go-logging.v1"
    17  
    18  	"build"
    19  	"cache"
    20  	"clean"
    21  	"cli"
    22  	"core"
    23  	"export"
    24  	"follow"
    25  	"fs"
    26  	"gc"
    27  	"hashes"
    28  	"help"
    29  	"metrics"
    30  	"output"
    31  	"parse"
    32  	"query"
    33  	"run"
    34  	"sync"
    35  	"test"
    36  	"tool"
    37  	"update"
    38  	"utils"
    39  	"watch"
    40  )
    41  
    42  var log = logging.MustGetLogger("plz")
    43  
    44  var config *core.Configuration
    45  
    46  var opts struct {
    47  	Usage      string `usage:"Please is a high-performance multi-language build system.\n\nIt uses BUILD files to describe what to build and how to build it.\nSee https://please.build for more information about how it works and what Please can do for you."`
    48  	BuildFlags struct {
    49  		Config     string          `short:"c" long:"config" description:"Build config to use. Defaults to opt."`
    50  		Arch       cli.Arch        `short:"a" long:"arch" description:"Architecture to compile for."`
    51  		RepoRoot   cli.Filepath    `short:"r" long:"repo_root" description:"Root of repository to build."`
    52  		KeepGoing  bool            `short:"k" long:"keep_going" description:"Don't stop on first failed target."`
    53  		NumThreads int             `short:"n" long:"num_threads" description:"Number of concurrent build operations. Default is number of CPUs + 2."`
    54  		Include    []string        `short:"i" long:"include" description:"Label of targets to include in automatic detection."`
    55  		Exclude    []string        `short:"e" long:"exclude" description:"Label of targets to exclude from automatic detection."`
    56  		Option     ConfigOverrides `short:"o" long:"override" env:"PLZ_OVERRIDES" env-delim:";" description:"Options to override from .plzconfig (e.g. -o please.selfupdate:false)"`
    57  		Profile    string          `long:"profile" env:"PLZ_CONFIG_PROFILE" description:"Configuration profile to load; e.g. --profile=dev will load .plzconfig.dev if it exists."`
    58  	} `group:"Options controlling what to build & how to build it"`
    59  
    60  	OutputFlags struct {
    61  		Verbosity         int          `short:"v" long:"verbosity" description:"Verbosity of output (higher number = more output, default 1 -> warnings and errors only)" default:"1"`
    62  		LogFile           cli.Filepath `long:"log_file" description:"File to echo full logging output to" default:"plz-out/log/build.log"`
    63  		LogFileLevel      int          `long:"log_file_level" description:"Log level for file output" default:"4"`
    64  		InteractiveOutput bool         `long:"interactive_output" description:"Show interactive output ina  terminal"`
    65  		PlainOutput       bool         `short:"p" long:"plain_output" description:"Don't show interactive output."`
    66  		Colour            bool         `long:"colour" description:"Forces coloured output from logging & other shell output."`
    67  		NoColour          bool         `long:"nocolour" description:"Forces colourless output from logging & other shell output."`
    68  		TraceFile         cli.Filepath `long:"trace_file" description:"File to write Chrome tracing output into"`
    69  		ShowAllOutput     bool         `long:"show_all_output" description:"Show all output live from all commands. Implies --plain_output."`
    70  		CompletionScript  bool         `long:"completion_script" description:"Prints the bash / zsh completion script to stdout"`
    71  		Version           bool         `long:"version" description:"Print the version of the tool"`
    72  	} `group:"Options controlling output & logging"`
    73  
    74  	FeatureFlags struct {
    75  		NoUpdate           bool `long:"noupdate" description:"Disable Please attempting to auto-update itself."`
    76  		NoCache            bool `long:"nocache" description:"Disable caches (NB. not incrementality)"`
    77  		NoHashVerification bool `long:"nohash_verification" description:"Hash verification errors are nonfatal."`
    78  		NoLock             bool `long:"nolock" description:"Don't attempt to lock the repo exclusively. Use with care."`
    79  		KeepWorkdirs       bool `long:"keep_workdirs" description:"Don't clean directories in plz-out/tmp after successfully building targets."`
    80  	} `group:"Options that enable / disable certain features"`
    81  
    82  	Profile          string `long:"profile_file" hidden:"true" description:"Write profiling output to this file"`
    83  	MemProfile       string `long:"mem_profile_file" hidden:"true" description:"Write a memory profile to this file"`
    84  	ProfilePort      int    `long:"profile_port" hidden:"true" description:"Serve profiling info on this port."`
    85  	ParsePackageOnly bool   `description:"Parses a single package only. All that's necessary for some commands." no-flag:"true"`
    86  	Complete         string `long:"complete" hidden:"true" env:"PLZ_COMPLETE" description:"Provide completion options for this build target."`
    87  	VisibilityParse  bool   `description:"Parse all targets that the original targets are visible to. Used for some query steps." no-flag:"true"`
    88  
    89  	Build struct {
    90  		Prepare    bool     `long:"prepare" description:"Prepare build directory for these targets but don't build them."`
    91  		Shell      bool     `long:"shell" description:"Like --prepare, but opens a shell in the build directory with the appropriate environment variables."`
    92  		ShowStatus bool     `long:"show_status" hidden:"true" description:"Show status of each target in output after build"`
    93  		Args       struct { // Inner nesting is necessary to make positional-args work :(
    94  			Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to build"`
    95  		} `positional-args:"true" required:"true"`
    96  	} `command:"build" description:"Builds one or more targets"`
    97  
    98  	Rebuild struct {
    99  		Args struct {
   100  			Targets []core.BuildLabel `positional-arg-name:"targets" required:"true" description:"Targets to rebuild"`
   101  		} `positional-args:"true" required:"true"`
   102  	} `command:"rebuild" description:"Forces a rebuild of one or more targets"`
   103  
   104  	Hash struct {
   105  		Detailed bool `long:"detailed" description:"Produces a detailed breakdown of the hash"`
   106  		Update   bool `short:"u" long:"update" description:"Rewrites the hashes in the BUILD file to the new values"`
   107  		Args     struct {
   108  			Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to build"`
   109  		} `positional-args:"true" required:"true"`
   110  	} `command:"hash" description:"Calculates hash for one or more targets"`
   111  
   112  	Test struct {
   113  		FailingTestsOk  bool         `long:"failing_tests_ok" hidden:"true" description:"Exit with status 0 even if tests fail (nonzero only if catastrophe happens)"`
   114  		NumRuns         int          `long:"num_runs" short:"n" description:"Number of times to run each test target."`
   115  		TestResultsFile cli.Filepath `long:"test_results_file" default:"plz-out/log/test_results.xml" description:"File to write combined test results to."`
   116  		ShowOutput      bool         `short:"s" long:"show_output" description:"Always show output of tests, even on success."`
   117  		Debug           bool         `short:"d" long:"debug" description:"Allows starting an interactive debugger on test failure. Does not work with all test types (currently only python/pytest, C and C++). Implies -c dbg unless otherwise set."`
   118  		Failed          bool         `short:"f" long:"failed" description:"Runs just the test cases that failed from the immediately previous run."`
   119  		// Slightly awkward since we can specify a single test with arguments or multiple test targets.
   120  		Args struct {
   121  			Target core.BuildLabel `positional-arg-name:"target" description:"Target to test"`
   122  			Args   []string        `positional-arg-name:"arguments" description:"Arguments or test selectors"`
   123  		} `positional-args:"true"`
   124  	} `command:"test" description:"Builds and tests one or more targets"`
   125  
   126  	Cover struct {
   127  		FailingTestsOk      bool         `long:"failing_tests_ok" hidden:"true" description:"Exit with status 0 even if tests fail (nonzero only if catastrophe happens)"`
   128  		NoCoverageReport    bool         `long:"nocoverage_report" description:"Suppress the per-file coverage report displayed in the shell"`
   129  		LineCoverageReport  bool         `short:"l" long:"line_coverage_report" description:" Show a line-by-line coverage report for all affected files."`
   130  		NumRuns             int          `short:"n" long:"num_runs" description:"Number of times to run each test target."`
   131  		IncludeAllFiles     bool         `short:"a" long:"include_all_files" description:"Include all dependent files in coverage (default is just those from relevant packages)"`
   132  		IncludeFile         []string     `long:"include_file" description:"Filenames to filter coverage display to"`
   133  		TestResultsFile     cli.Filepath `long:"test_results_file" default:"plz-out/log/test_results.xml" description:"File to write combined test results to."`
   134  		CoverageResultsFile cli.Filepath `long:"coverage_results_file" default:"plz-out/log/coverage.json" description:"File to write combined coverage results to."`
   135  		ShowOutput          bool         `short:"s" long:"show_output" description:"Always show output of tests, even on success."`
   136  		Debug               bool         `short:"d" long:"debug" description:"Allows starting an interactive debugger on test failure. Does not work with all test types (currently only python/pytest, C and C++). Implies -c dbg unless otherwise set."`
   137  		Failed              bool         `short:"f" long:"failed" description:"Runs just the test cases that failed from the immediately previous run."`
   138  		Args                struct {
   139  			Target core.BuildLabel `positional-arg-name:"target" description:"Target to test" group:"one test"`
   140  			Args   []string        `positional-arg-name:"arguments" description:"Arguments or test selectors" group:"one test"`
   141  		} `positional-args:"true"`
   142  	} `command:"cover" description:"Builds and tests one or more targets, and calculates coverage."`
   143  
   144  	Run struct {
   145  		Env      bool `long:"env" description:"Overrides environment variables (e.g. PATH) in the new process."`
   146  		Parallel struct {
   147  			NumTasks       int  `short:"n" long:"num_tasks" default:"10" description:"Maximum number of subtasks to run in parallel"`
   148  			Quiet          bool `short:"q" long:"quiet" description:"Suppress output from successful subprocesses."`
   149  			PositionalArgs struct {
   150  				Targets []core.BuildLabel `positional-arg-name:"target" description:"Targets to run"`
   151  			} `positional-args:"true" required:"true"`
   152  			Args []string `short:"a" long:"arg" description:"Arguments to pass to the called processes."`
   153  		} `command:"parallel" description:"Runs a sequence of targets in parallel"`
   154  		Sequential struct {
   155  			Quiet          bool `short:"q" long:"quiet" description:"Suppress output from successful subprocesses."`
   156  			PositionalArgs struct {
   157  				Targets []core.BuildLabel `positional-arg-name:"target" description:"Targets to run"`
   158  			} `positional-args:"true" required:"true"`
   159  			Args []string `short:"a" long:"arg" description:"Arguments to pass to the called processes."`
   160  		} `command:"sequential" description:"Runs a sequence of targets sequentially."`
   161  		Args struct {
   162  			Target core.BuildLabel `positional-arg-name:"target" required:"true" description:"Target to run"`
   163  			Args   []string        `positional-arg-name:"arguments" description:"Arguments to pass to target when running (to pass flags to the target, put -- before them)"`
   164  		} `positional-args:"true"`
   165  	} `command:"run" subcommands-optional:"true" description:"Builds and runs a single target"`
   166  
   167  	Clean struct {
   168  		NoBackground bool     `long:"nobackground" short:"f" description:"Don't fork & detach until clean is finished."`
   169  		Remote       bool     `long:"remote" description:"Clean entire remote cache when no targets are given (default is local only)"`
   170  		Args         struct { // Inner nesting is necessary to make positional-args work :(
   171  			Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to clean (default is to clean everything)"`
   172  		} `positional-args:"true"`
   173  	} `command:"clean" description:"Cleans build artifacts" subcommands-optional:"true"`
   174  
   175  	Watch struct {
   176  		Run  bool `short:"r" long:"run" description:"Runs the specified targets when they change (default is to build or test as appropriate)."`
   177  		Args struct {
   178  			Targets []core.BuildLabel `positional-arg-name:"targets" required:"true" description:"Targets to watch the sources of for changes"`
   179  		} `positional-args:"true" required:"true"`
   180  	} `command:"watch" description:"Watches sources of targets for changes and rebuilds them"`
   181  
   182  	Update struct {
   183  		Force    bool        `long:"force" description:"Forces a re-download of the new version."`
   184  		NoVerify bool        `long:"noverify" description:"Skips signature verification of downloaded version"`
   185  		Latest   bool        `long:"latest" description:"Update to latest available version (overrides config)."`
   186  		Version  cli.Version `long:"version" description:"Updates to a particular version (overrides config)."`
   187  	} `command:"update" description:"Checks for an update and updates if needed."`
   188  
   189  	Op struct {
   190  	} `command:"op" description:"Re-runs previous command."`
   191  
   192  	Init struct {
   193  		Dir                cli.Filepath `long:"dir" description:"Directory to create config in" default:"."`
   194  		BazelCompatibility bool         `long:"bazel_compat" description:"Initialises config for Bazel compatibility mode."`
   195  	} `command:"init" description:"Initialises a .plzconfig file in the current directory"`
   196  
   197  	Gc struct {
   198  		Conservative bool `short:"c" long:"conservative" description:"Runs a more conservative / safer GC."`
   199  		TargetsOnly  bool `short:"t" long:"targets_only" description:"Only print the targets to delete"`
   200  		SrcsOnly     bool `short:"s" long:"srcs_only" description:"Only print the source files to delete"`
   201  		NoPrompt     bool `short:"y" long:"no_prompt" description:"Remove targets without prompting"`
   202  		DryRun       bool `short:"n" long:"dry_run" description:"Don't remove any targets or files, just print what would be done"`
   203  		Git          bool `short:"g" long:"git" description:"Use 'git rm' to remove unused files instead of just 'rm'."`
   204  		Args         struct {
   205  			Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to limit gc to."`
   206  		} `positional-args:"true"`
   207  	} `command:"gc" description:"Analyzes the repo to determine unneeded targets."`
   208  
   209  	Export struct {
   210  		Output string `short:"o" long:"output" required:"true" description:"Directory to export into"`
   211  		Args   struct {
   212  			Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to export."`
   213  		} `positional-args:"true"`
   214  
   215  		Outputs struct {
   216  			Args struct {
   217  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to export."`
   218  			} `positional-args:"true"`
   219  		} `command:"outputs" description:"Exports outputs of a set of targets"`
   220  	} `command:"export" subcommands-optional:"true" description:"Exports a set of targets and files from the repo."`
   221  
   222  	Follow struct {
   223  		Retries int          `long:"retries" description:"Number of times to retry the connection"`
   224  		Delay   cli.Duration `long:"delay" default:"1s" description:"Delay between timeouts"`
   225  		Args    struct {
   226  			URL cli.URL `positional-arg-name:"URL" required:"true" description:"URL of remote server to connect to, e.g. 10.23.0.5:7777"`
   227  		} `positional-args:"true"`
   228  	} `command:"follow" description:"Connects to a remote Please instance to stream build events from."`
   229  
   230  	Help struct {
   231  		Args struct {
   232  			Topic help.Topic `positional-arg-name:"topic" description:"Topic to display help on"`
   233  		} `positional-args:"true"`
   234  	} `command:"help" alias:"halp" description:"Displays help about various parts of plz or its build rules"`
   235  
   236  	Tool struct {
   237  		Args struct {
   238  			Tool tool.Tool `positional-arg-name:"tool" description:"Tool to invoke (jarcat, lint, etc)"`
   239  			Args []string  `positional-arg-name:"arguments" description:"Arguments to pass to the tool"`
   240  		} `positional-args:"true"`
   241  	} `command:"tool" hidden:"true" description:"Invoke one of Please's sub-tools"`
   242  
   243  	Query struct {
   244  		Deps struct {
   245  			Unique bool `long:"unique" short:"u" description:"Only output each dependency once"`
   246  			Args   struct {
   247  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to query" required:"true"`
   248  			} `positional-args:"true" required:"true"`
   249  		} `command:"deps" description:"Queries the dependencies of a target."`
   250  		ReverseDeps struct {
   251  			Args struct {
   252  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to query" required:"true"`
   253  			} `positional-args:"true" required:"true"`
   254  		} `command:"reverseDeps" alias:"revdeps" description:"Queries all the reverse dependencies of a target."`
   255  		SomePath struct {
   256  			Args struct {
   257  				Target1 core.BuildLabel `positional-arg-name:"target1" description:"First build target" required:"true"`
   258  				Target2 core.BuildLabel `positional-arg-name:"target2" description:"Second build target" required:"true"`
   259  			} `positional-args:"true" required:"true"`
   260  		} `command:"somepath" description:"Queries for a path between two targets"`
   261  		AllTargets struct {
   262  			Hidden bool `long:"hidden" description:"Show hidden targets as well"`
   263  			Args   struct {
   264  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to query"`
   265  			} `positional-args:"true"`
   266  		} `command:"alltargets" description:"Lists all targets in the graph"`
   267  		Print struct {
   268  			Fields []string `short:"f" long:"field" description:"Individual fields to print of the target"`
   269  			Args   struct {
   270  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to print" required:"true"`
   271  			} `positional-args:"true" required:"true"`
   272  		} `command:"print" description:"Prints a representation of a single target"`
   273  		Completions struct {
   274  			Cmd  string `long:"cmd" description:"Command to complete for" default:"build"`
   275  			Args struct {
   276  				Fragments cli.StdinStrings `positional-arg-name:"fragment" description:"Initial fragment to attempt to complete"`
   277  			} `positional-args:"true"`
   278  		} `command:"completions" subcommands-optional:"true" description:"Prints possible completions for a string."`
   279  		AffectedTargets struct {
   280  			Tests        bool `long:"tests" description:"Shows only affected tests, no other targets."`
   281  			Intransitive bool `long:"intransitive" description:"Shows only immediately affected targets, not transitive dependencies."`
   282  			Args         struct {
   283  				Files cli.StdinStrings `positional-arg-name:"files" required:"true" description:"Files to query affected tests for"`
   284  			} `positional-args:"true"`
   285  		} `command:"affectedtargets" description:"Prints any targets affected by a set of files."`
   286  		Input struct {
   287  			Args struct {
   288  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to display inputs for" required:"true"`
   289  			} `positional-args:"true" required:"true"`
   290  		} `command:"input" alias:"inputs" description:"Prints all transitive inputs of a target."`
   291  		Output struct {
   292  			Args struct {
   293  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to display outputs for" required:"true"`
   294  			} `positional-args:"true" required:"true"`
   295  		} `command:"output" alias:"outputs" description:"Prints all outputs of a target."`
   296  		Graph struct {
   297  			Args struct {
   298  				Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to render graph for"`
   299  			} `positional-args:"true"`
   300  		} `command:"graph" description:"Prints a JSON representation of the build graph."`
   301  		WhatOutputs struct {
   302  			EchoFiles bool `long:"echo_files" description:"Echo the file for which the printed output is responsible."`
   303  			Args      struct {
   304  				Files cli.StdinStrings `positional-arg-name:"files" required:"true" description:"Files to query targets responsible for"`
   305  			} `positional-args:"true"`
   306  		} `command:"whatoutputs" description:"Prints out target(s) responsible for outputting provided file(s)"`
   307  		Rules struct {
   308  			Args struct {
   309  				Targets []core.BuildLabel `position-arg-name:"targets" description:"Additional targets to load rules from"`
   310  			} `positional-args:"true"`
   311  		} `command:"rules" description:"Prints built-in rules to stdout as JSON"`
   312  		Changes struct {
   313  			Since           string `short:"s" long:"since" default:"origin/master" description:"Revision to compare against"`
   314  			CheckoutCommand string `long:"checkout_command" default:"git checkout %s" description:"Command to run to check out the before/after revisions."`
   315  			CurrentCommand  string `long:"current_revision_command" default:"git rev-parse HEAD" description:"Command to run to get the current revision (which will be checked out again at the end)"`
   316  			Args            struct {
   317  				Files cli.StdinStrings `positional-arg-name:"files" description:"Files to consider changed"`
   318  			} `positional-args:"true"`
   319  		} `command:"changes" description:"Calculates the difference between two different states of the build graph"`
   320  	} `command:"query" description:"Queries information about the build graph"`
   321  }
   322  
   323  // Definitions of what we do for each command.
   324  // Functions are called after args are parsed and return true for success.
   325  var buildFunctions = map[string]func() bool{
   326  	"build": func() bool {
   327  		success, _ := runBuild(opts.Build.Args.Targets, true, false)
   328  		return success
   329  	},
   330  	"rebuild": func() bool {
   331  		// It would be more pure to require --nocache for this, but in basically any context that
   332  		// you use 'plz rebuild', you don't want the cache coming in and mucking things up.
   333  		// 'plz clean' followed by 'plz build' would still work in those cases, anyway.
   334  		opts.FeatureFlags.NoCache = true
   335  		success, _ := runBuild(opts.Rebuild.Args.Targets, true, false)
   336  		return success
   337  	},
   338  	"hash": func() bool {
   339  		success, state := runBuild(opts.Hash.Args.Targets, true, false)
   340  		if opts.Hash.Detailed {
   341  			for _, target := range state.ExpandOriginalTargets() {
   342  				build.PrintHashes(state, state.Graph.TargetOrDie(target))
   343  			}
   344  		}
   345  		if opts.Hash.Update {
   346  			hashes.RewriteHashes(state, state.ExpandOriginalTargets())
   347  		}
   348  		return success
   349  	},
   350  	"test": func() bool {
   351  		targets := testTargets(opts.Test.Args.Target, opts.Test.Args.Args, opts.Test.Failed, opts.Test.TestResultsFile)
   352  		os.RemoveAll(string(opts.Test.TestResultsFile))
   353  		success, state := runBuild(targets, true, true)
   354  		test.WriteResultsToFileOrDie(state.Graph, string(opts.Test.TestResultsFile))
   355  		return success || opts.Test.FailingTestsOk
   356  	},
   357  	"cover": func() bool {
   358  		if opts.BuildFlags.Config != "" {
   359  			log.Warning("Build config overridden; coverage may not be available for some languages")
   360  		} else {
   361  			opts.BuildFlags.Config = "cover"
   362  		}
   363  		targets := testTargets(opts.Cover.Args.Target, opts.Cover.Args.Args, opts.Cover.Failed, opts.Cover.TestResultsFile)
   364  		os.RemoveAll(string(opts.Cover.TestResultsFile))
   365  		os.RemoveAll(string(opts.Cover.CoverageResultsFile))
   366  		success, state := runBuild(targets, true, true)
   367  		test.WriteResultsToFileOrDie(state.Graph, string(opts.Cover.TestResultsFile))
   368  		test.AddOriginalTargetsToCoverage(state, opts.Cover.IncludeAllFiles)
   369  		test.RemoveFilesFromCoverage(state.Coverage, state.Config.Cover.ExcludeExtension)
   370  		test.WriteCoverageToFileOrDie(state.Coverage, string(opts.Cover.CoverageResultsFile))
   371  		if opts.Cover.LineCoverageReport {
   372  			output.PrintLineCoverageReport(state, opts.Cover.IncludeFile)
   373  		} else if !opts.Cover.NoCoverageReport {
   374  			output.PrintCoverage(state, opts.Cover.IncludeFile)
   375  		}
   376  		return success || opts.Cover.FailingTestsOk
   377  	},
   378  	"run": func() bool {
   379  		if success, state := runBuild([]core.BuildLabel{opts.Run.Args.Target}, true, false); success {
   380  			run.Run(state, opts.Run.Args.Target, opts.Run.Args.Args, opts.Run.Env)
   381  		}
   382  		return false // We should never return from run.Run so if we make it here something's wrong.
   383  	},
   384  	"parallel": func() bool {
   385  		if success, state := runBuild(opts.Run.Parallel.PositionalArgs.Targets, true, false); success {
   386  			os.Exit(run.Parallel(state, state.ExpandOriginalTargets(), opts.Run.Parallel.Args, opts.Run.Parallel.NumTasks, opts.Run.Parallel.Quiet, opts.Run.Env))
   387  		}
   388  		return false
   389  	},
   390  	"sequential": func() bool {
   391  		if success, state := runBuild(opts.Run.Sequential.PositionalArgs.Targets, true, false); success {
   392  			os.Exit(run.Sequential(state, state.ExpandOriginalTargets(), opts.Run.Sequential.Args, opts.Run.Sequential.Quiet, opts.Run.Env))
   393  		}
   394  		return false
   395  	},
   396  	"clean": func() bool {
   397  		config.Cache.DirClean = false
   398  		if len(opts.Clean.Args.Targets) == 0 {
   399  			if len(opts.BuildFlags.Include) == 0 && len(opts.BuildFlags.Exclude) == 0 {
   400  				// Clean everything, doesn't require parsing at all.
   401  				if !opts.Clean.Remote {
   402  					// Don't construct the remote caches if they didn't pass --remote.
   403  					config.Cache.RPCURL = ""
   404  					config.Cache.HTTPURL = ""
   405  				}
   406  				clean.Clean(config, newCache(config), !opts.Clean.NoBackground)
   407  				return true
   408  			}
   409  			opts.Clean.Args.Targets = core.WholeGraph
   410  		}
   411  		if success, state := runBuild(opts.Clean.Args.Targets, false, false); success {
   412  			clean.Targets(state, state.ExpandOriginalTargets(), !opts.FeatureFlags.NoCache)
   413  			return true
   414  		}
   415  		return false
   416  	},
   417  	"watch": func() bool {
   418  		success, state := runBuild(opts.Watch.Args.Targets, false, false)
   419  		if success {
   420  			watch.Watch(state, state.ExpandOriginalTargets(), opts.Watch.Run)
   421  		}
   422  		return success
   423  	},
   424  	"update": func() bool {
   425  		fmt.Printf("Up to date (version %s).\n", core.PleaseVersion)
   426  		return true // We'd have died already if something was wrong.
   427  	},
   428  	"op": func() bool {
   429  		cmd := core.ReadLastOperationOrDie()
   430  		log.Notice("OP PLZ: %s", strings.Join(cmd, " "))
   431  		// Annoyingly we don't seem to have any access to execvp() which would be rather useful here...
   432  		executable, err := os.Executable()
   433  		if err == nil {
   434  			err = syscall.Exec(executable, append([]string{executable}, cmd...), os.Environ())
   435  		}
   436  		log.Fatalf("SORRY OP: %s", err) // On success Exec never returns.
   437  		return false
   438  	},
   439  	"gc": func() bool {
   440  		success, state := runBuild(core.WholeGraph, false, false)
   441  		if success {
   442  			state.OriginalTargets = state.Config.Gc.Keep
   443  			gc.GarbageCollect(state, opts.Gc.Args.Targets, state.ExpandOriginalTargets(), state.Config.Gc.Keep, state.Config.Gc.KeepLabel,
   444  				opts.Gc.Conservative, opts.Gc.TargetsOnly, opts.Gc.SrcsOnly, opts.Gc.NoPrompt, opts.Gc.DryRun, opts.Gc.Git)
   445  		}
   446  		return success
   447  	},
   448  	"export": func() bool {
   449  		success, state := runBuild(opts.Export.Args.Targets, false, false)
   450  		if success {
   451  			export.ToDir(state, opts.Export.Output, state.ExpandOriginalTargets())
   452  		}
   453  		return success
   454  	},
   455  	"follow": func() bool {
   456  		// This is only temporary, ConnectClient will alter it to match the server.
   457  		state := core.NewBuildState(1, nil, opts.OutputFlags.Verbosity, config)
   458  		return follow.ConnectClient(state, opts.Follow.Args.URL.String(), opts.Follow.Retries, time.Duration(opts.Follow.Delay))
   459  	},
   460  	"outputs": func() bool {
   461  		success, state := runBuild(opts.Export.Outputs.Args.Targets, true, false)
   462  		if success {
   463  			export.Outputs(state, opts.Export.Output, state.ExpandOriginalTargets())
   464  		}
   465  		return success
   466  	},
   467  	"help": func() bool {
   468  		return help.Help(string(opts.Help.Args.Topic))
   469  	},
   470  	"tool": func() bool {
   471  		tool.Run(config, opts.Tool.Args.Tool, opts.Tool.Args.Args)
   472  		return false // If the function returns (which it shouldn't), something went wrong.
   473  	},
   474  	"deps": func() bool {
   475  		return runQuery(true, opts.Query.Deps.Args.Targets, func(state *core.BuildState) {
   476  			query.Deps(state, state.ExpandOriginalTargets(), opts.Query.Deps.Unique)
   477  		})
   478  	},
   479  	"reverseDeps": func() bool {
   480  		opts.VisibilityParse = true
   481  		return runQuery(false, opts.Query.ReverseDeps.Args.Targets, func(state *core.BuildState) {
   482  			query.ReverseDeps(state.Graph, state.ExpandOriginalTargets())
   483  		})
   484  	},
   485  	"somepath": func() bool {
   486  		return runQuery(true,
   487  			[]core.BuildLabel{opts.Query.SomePath.Args.Target1, opts.Query.SomePath.Args.Target2},
   488  			func(state *core.BuildState) {
   489  				query.SomePath(state.Graph, opts.Query.SomePath.Args.Target1, opts.Query.SomePath.Args.Target2)
   490  			},
   491  		)
   492  	},
   493  	"alltargets": func() bool {
   494  		return runQuery(true, opts.Query.AllTargets.Args.Targets, func(state *core.BuildState) {
   495  			query.AllTargets(state.Graph, state.ExpandOriginalTargets(), opts.Query.AllTargets.Hidden)
   496  		})
   497  	},
   498  	"print": func() bool {
   499  		return runQuery(false, opts.Query.Print.Args.Targets, func(state *core.BuildState) {
   500  			query.Print(state.Graph, state.ExpandOriginalTargets(), opts.Query.Print.Fields)
   501  		})
   502  	},
   503  	"affectedtargets": func() bool {
   504  		files := opts.Query.AffectedTargets.Args.Files
   505  		targets := core.WholeGraph
   506  		if opts.Query.AffectedTargets.Intransitive {
   507  			state := core.NewBuildState(1, nil, 1, config)
   508  			targets = core.FindOwningPackages(state, files)
   509  		}
   510  		return runQuery(true, targets, func(state *core.BuildState) {
   511  			query.AffectedTargets(state, files.Get(), opts.BuildFlags.Include, opts.BuildFlags.Exclude, opts.Query.AffectedTargets.Tests, !opts.Query.AffectedTargets.Intransitive)
   512  		})
   513  	},
   514  	"input": func() bool {
   515  		return runQuery(true, opts.Query.Input.Args.Targets, func(state *core.BuildState) {
   516  			query.TargetInputs(state.Graph, state.ExpandOriginalTargets())
   517  		})
   518  	},
   519  	"output": func() bool {
   520  		return runQuery(true, opts.Query.Output.Args.Targets, func(state *core.BuildState) {
   521  			query.TargetOutputs(state.Graph, state.ExpandOriginalTargets())
   522  		})
   523  	},
   524  	"completions": func() bool {
   525  		// Somewhat fiddly because the inputs are not necessarily well-formed at this point.
   526  		opts.ParsePackageOnly = true
   527  		fragments := opts.Query.Completions.Args.Fragments.Get()
   528  		if opts.Query.Completions.Cmd == "help" {
   529  			// Special-case completing help topics rather than build targets.
   530  			if len(fragments) == 0 {
   531  				help.Topics("")
   532  			} else {
   533  				help.Topics(fragments[0])
   534  			}
   535  			return true
   536  		}
   537  		if len(fragments) == 0 || len(fragments) == 1 && strings.Trim(fragments[0], "/ ") == "" {
   538  			os.Exit(0) // Don't do anything for empty completion, it's normally too slow.
   539  		}
   540  		labels, parseLabels, hidden := query.CompletionLabels(config, fragments, core.RepoRoot)
   541  		if success, state := Please(parseLabels, config, false, false, false); success {
   542  			binary := opts.Query.Completions.Cmd == "run"
   543  			test := opts.Query.Completions.Cmd == "test" || opts.Query.Completions.Cmd == "cover"
   544  			query.Completions(state.Graph, labels, binary, test, hidden)
   545  			return true
   546  		}
   547  		return false
   548  	},
   549  	"graph": func() bool {
   550  		return runQuery(true, opts.Query.Graph.Args.Targets, func(state *core.BuildState) {
   551  			if len(opts.Query.Graph.Args.Targets) == 0 {
   552  				state.OriginalTargets = opts.Query.Graph.Args.Targets // It special-cases doing the full graph.
   553  			}
   554  			query.Graph(state, state.ExpandOriginalTargets())
   555  		})
   556  	},
   557  	"whatoutputs": func() bool {
   558  		return runQuery(true, core.WholeGraph, func(state *core.BuildState) {
   559  			query.WhatOutputs(state.Graph, opts.Query.WhatOutputs.Args.Files.Get(), opts.Query.WhatOutputs.EchoFiles)
   560  		})
   561  	},
   562  	"rules": func() bool {
   563  		targets := opts.Query.Rules.Args.Targets
   564  		success, state := Please(opts.Query.Rules.Args.Targets, config, true, true, false)
   565  		if !success {
   566  			return false
   567  		}
   568  		targets = state.ExpandOriginalTargets()
   569  		parse.PrintRuleArgs(state, targets)
   570  		return true
   571  	},
   572  	"changes": func() bool {
   573  		original := query.MustGetRevision(opts.Query.Changes.CurrentCommand)
   574  		files := opts.Query.Changes.Args.Files.Get()
   575  		query.MustCheckout(opts.Query.Changes.Since, opts.Query.Changes.CheckoutCommand)
   576  		success, before := runBuild(core.WholeGraph, false, false)
   577  		if !success {
   578  			return false
   579  		}
   580  		query.MustCheckout(original, opts.Query.Changes.CheckoutCommand)
   581  		success, after := runBuild(core.WholeGraph, false, false)
   582  		if !success {
   583  			return false
   584  		}
   585  		for _, target := range query.DiffGraphs(before, after, files) {
   586  			fmt.Printf("%s\n", target)
   587  		}
   588  		return true
   589  	},
   590  }
   591  
   592  // ConfigOverrides are used to implement completion on the -o flag.
   593  type ConfigOverrides map[string]string
   594  
   595  // Complete implements the flags.Completer interface.
   596  func (overrides ConfigOverrides) Complete(match string) []flags.Completion {
   597  	return core.DefaultConfiguration().Completions(match)
   598  }
   599  
   600  // Used above as a convenience wrapper for query functions.
   601  func runQuery(needFullParse bool, labels []core.BuildLabel, onSuccess func(state *core.BuildState)) bool {
   602  	if !needFullParse {
   603  		opts.ParsePackageOnly = true
   604  	}
   605  	if len(labels) == 0 {
   606  		labels = core.WholeGraph
   607  	}
   608  	if success, state := runBuild(labels, false, false); success {
   609  		onSuccess(state)
   610  		return true
   611  	}
   612  	return false
   613  }
   614  
   615  func please(tid int, state *core.BuildState, parsePackageOnly bool, include, exclude []string) {
   616  	for {
   617  		label, dependor, t := state.NextTask()
   618  		switch t {
   619  		case core.Stop, core.Kill:
   620  			return
   621  		case core.Parse, core.SubincludeParse:
   622  			t := t
   623  			label := label
   624  			dependor := dependor
   625  			state.ParsePool <- func() {
   626  				parse.Parse(tid, state, label, dependor, parsePackageOnly, include, exclude, t == core.SubincludeParse)
   627  				if opts.VisibilityParse && state.IsOriginalTarget(label) {
   628  					parseForVisibleTargets(state, label)
   629  				}
   630  				state.TaskDone()
   631  			}
   632  		case core.Build, core.SubincludeBuild:
   633  			build.Build(tid, state, label)
   634  			state.TaskDone()
   635  		case core.Test:
   636  			test.Test(tid, state, label)
   637  			state.TaskDone()
   638  		}
   639  	}
   640  }
   641  
   642  // parseForVisibleTargets adds parse tasks for any targets that the given label is visible to.
   643  func parseForVisibleTargets(state *core.BuildState, label core.BuildLabel) {
   644  	if target := state.Graph.Target(label); target != nil {
   645  		for _, vis := range target.Visibility {
   646  			findOriginalTask(state, vis, false)
   647  		}
   648  	}
   649  }
   650  
   651  // prettyOutputs determines from input flags whether we should show 'pretty' output (ie. interactive).
   652  func prettyOutput(interactiveOutput bool, plainOutput bool, verbosity int) bool {
   653  	if interactiveOutput && plainOutput {
   654  		log.Fatal("Can't pass both --interactive_output and --plain_output")
   655  	}
   656  	return interactiveOutput || (!plainOutput && cli.StdErrIsATerminal && verbosity < 4)
   657  }
   658  
   659  // newCache constructs a new cache based on the current config / flags.
   660  func newCache(config *core.Configuration) core.Cache {
   661  	if opts.FeatureFlags.NoCache {
   662  		return nil
   663  	}
   664  	return cache.NewCache(config)
   665  }
   666  
   667  // Please starts & runs the main build process through to its completion.
   668  func Please(targets []core.BuildLabel, config *core.Configuration, prettyOutput, shouldBuild, shouldTest bool) (bool, *core.BuildState) {
   669  	if opts.BuildFlags.NumThreads > 0 {
   670  		config.Please.NumThreads = opts.BuildFlags.NumThreads
   671  	} else if config.Please.NumThreads <= 0 {
   672  		config.Please.NumThreads = runtime.NumCPU() + 2
   673  	}
   674  	debugTests := opts.Test.Debug || opts.Cover.Debug
   675  	if opts.BuildFlags.Config != "" {
   676  		config.Build.Config = opts.BuildFlags.Config
   677  	} else if debugTests {
   678  		config.Build.Config = "dbg"
   679  	}
   680  	c := newCache(config)
   681  	state := core.NewBuildState(config.Please.NumThreads, c, opts.OutputFlags.Verbosity, config)
   682  	state.VerifyHashes = !opts.FeatureFlags.NoHashVerification
   683  	state.NumTestRuns = opts.Test.NumRuns + opts.Cover.NumRuns            // Only one of these can be passed.
   684  	state.TestArgs = append(opts.Test.Args.Args, opts.Cover.Args.Args...) // Similarly here.
   685  	state.NeedCoverage = !opts.Cover.Args.Target.IsEmpty()
   686  	state.NeedBuild = shouldBuild
   687  	state.NeedTests = shouldTest
   688  	state.NeedHashesOnly = len(opts.Hash.Args.Targets) > 0
   689  	state.PrepareOnly = opts.Build.Prepare || opts.Build.Shell
   690  	state.PrepareShell = opts.Build.Shell
   691  	state.CleanWorkdirs = !opts.FeatureFlags.KeepWorkdirs
   692  	state.ForceRebuild = len(opts.Rebuild.Args.Targets) > 0
   693  	state.ShowTestOutput = opts.Test.ShowOutput || opts.Cover.ShowOutput
   694  	state.DebugTests = debugTests
   695  	state.ShowAllOutput = opts.OutputFlags.ShowAllOutput
   696  	state.SetIncludeAndExclude(opts.BuildFlags.Include, opts.BuildFlags.Exclude)
   697  	parse.InitParser(state)
   698  	if config.Events.Port != 0 && shouldBuild {
   699  		shutdown := follow.InitialiseServer(state, config.Events.Port)
   700  		defer shutdown()
   701  	}
   702  	if config.Events.Port != 0 || config.Display.SystemStats {
   703  		go follow.UpdateResources(state)
   704  	}
   705  	metrics.InitFromConfig(config)
   706  	// Acquire the lock before we start building
   707  	if (shouldBuild || shouldTest) && !opts.FeatureFlags.NoLock {
   708  		core.AcquireRepoLock()
   709  		defer core.ReleaseRepoLock()
   710  	}
   711  	if state.DebugTests && len(targets) != 1 {
   712  		log.Fatalf("-d/--debug flag can only be used with a single test target")
   713  	}
   714  	// Start looking for the initial targets to kick the build off
   715  	go findOriginalTasks(state, targets)
   716  	// Start up all the build workers
   717  	var wg sync.WaitGroup
   718  	wg.Add(config.Please.NumThreads)
   719  	for i := 0; i < config.Please.NumThreads; i++ {
   720  		go func(tid int) {
   721  			please(tid, state, opts.ParsePackageOnly, opts.BuildFlags.Include, opts.BuildFlags.Exclude)
   722  			wg.Done()
   723  		}(i)
   724  	}
   725  	// Wait until they've all exited, which they'll do once they have no tasks left.
   726  	go func() {
   727  		wg.Wait()
   728  		close(state.Results) // This will signal MonitorState (below) to stop.
   729  	}()
   730  	// Draw stuff to the screen while there are still results coming through.
   731  	shouldRun := !opts.Run.Args.Target.IsEmpty()
   732  	success := output.MonitorState(state, config.Please.NumThreads, !prettyOutput, opts.BuildFlags.KeepGoing, shouldBuild, shouldTest, shouldRun, opts.Build.ShowStatus, string(opts.OutputFlags.TraceFile))
   733  	metrics.Stop()
   734  	build.StopWorkers()
   735  	if c != nil {
   736  		c.Shutdown()
   737  	}
   738  	return success, state
   739  }
   740  
   741  // findOriginalTasks finds the original parse tasks for the original set of targets.
   742  func findOriginalTasks(state *core.BuildState, targets []core.BuildLabel) {
   743  	if state.Config.Bazel.Compatibility && fs.FileExists("WORKSPACE") {
   744  		// We have to parse the WORKSPACE file before anything else to understand subrepos.
   745  		// This is a bit crap really since it inhibits parallelism for the first step.
   746  		parse.Parse(0, state, core.NewBuildLabel("workspace", "all"), core.OriginalTarget, false, state.Include, state.Exclude, false)
   747  	}
   748  	if opts.BuildFlags.Arch.Arch != "" {
   749  		// Set up a new subrepo for this architecture.
   750  		state.Graph.AddSubrepo(core.SubrepoForArch(state, opts.BuildFlags.Arch))
   751  	}
   752  	for _, target := range targets {
   753  		if target == core.BuildLabelStdin {
   754  			for label := range cli.ReadStdin() {
   755  				findOriginalTask(state, core.ParseBuildLabels([]string{label})[0], true)
   756  			}
   757  		} else {
   758  			findOriginalTask(state, target, true)
   759  		}
   760  	}
   761  	state.TaskDone() // initial target adding counts as one.
   762  }
   763  
   764  func findOriginalTask(state *core.BuildState, target core.BuildLabel, addToList bool) {
   765  	if opts.BuildFlags.Arch.Arch != "" {
   766  		target.PackageName = path.Join(opts.BuildFlags.Arch.String(), target.PackageName)
   767  	}
   768  	if target.IsAllSubpackages() {
   769  		for pkg := range utils.FindAllSubpackages(state.Config, target.PackageName, "") {
   770  			state.AddOriginalTarget(core.NewBuildLabel(pkg, "all"), addToList)
   771  		}
   772  	} else {
   773  		state.AddOriginalTarget(target, addToList)
   774  	}
   775  }
   776  
   777  // testTargets handles test targets which can be given in two formats; a list of targets or a single
   778  // target with a list of trailing arguments.
   779  // Alternatively they can be completely omitted in which case we test everything under the working dir.
   780  // One can also pass a 'failed' flag which runs the failed tests from last time.
   781  func testTargets(target core.BuildLabel, args []string, failed bool, resultsFile cli.Filepath) []core.BuildLabel {
   782  	if failed {
   783  		targets, args := test.LoadPreviousFailures(string(resultsFile))
   784  		// Have to reset these - it doesn't matter which gets which.
   785  		opts.Test.Args.Args = args
   786  		opts.Cover.Args.Args = nil
   787  		return targets
   788  	} else if target.Name == "" {
   789  		return core.InitialPackage()
   790  	} else if len(args) > 0 && core.LooksLikeABuildLabel(args[0]) {
   791  		opts.Cover.Args.Args = []string{}
   792  		opts.Test.Args.Args = []string{}
   793  		return append(core.ParseBuildLabels(args), target)
   794  	}
   795  	return []core.BuildLabel{target}
   796  }
   797  
   798  // readConfig sets various things up and reads the initial configuration.
   799  func readConfig(forceUpdate bool) *core.Configuration {
   800  	if opts.FeatureFlags.NoHashVerification {
   801  		log.Warning("You've disabled hash verification; this is intended to help temporarily while modifying build targets. You shouldn't use this regularly.")
   802  	}
   803  
   804  	config, err := core.ReadConfigFiles([]string{
   805  		core.MachineConfigFileName,
   806  		core.ExpandHomePath(core.UserConfigFileName),
   807  		path.Join(core.RepoRoot, core.ConfigFileName),
   808  		path.Join(core.RepoRoot, core.ArchConfigFileName),
   809  		path.Join(core.RepoRoot, core.LocalConfigFileName),
   810  	}, opts.BuildFlags.Profile)
   811  	if err != nil {
   812  		log.Fatalf("Error reading config file: %s", err)
   813  	} else if err := config.ApplyOverrides(opts.BuildFlags.Option); err != nil {
   814  		log.Fatalf("Can't override requested config setting: %s", err)
   815  	}
   816  	// Now apply any flags that override this
   817  	if opts.Update.Latest {
   818  		config.Please.Version.Unset()
   819  	} else if opts.Update.Version.IsSet {
   820  		config.Please.Version = opts.Update.Version
   821  	}
   822  	update.CheckAndUpdate(config, !opts.FeatureFlags.NoUpdate, forceUpdate, opts.Update.Force, !opts.Update.NoVerify)
   823  	return config
   824  }
   825  
   826  // Runs the actual build
   827  // Which phases get run are controlled by shouldBuild and shouldTest.
   828  func runBuild(targets []core.BuildLabel, shouldBuild, shouldTest bool) (bool, *core.BuildState) {
   829  	if len(targets) == 0 {
   830  		targets = core.InitialPackage()
   831  	}
   832  	pretty := prettyOutput(opts.OutputFlags.InteractiveOutput, opts.OutputFlags.PlainOutput, opts.OutputFlags.Verbosity)
   833  	return Please(targets, config, pretty, shouldBuild, shouldTest)
   834  }
   835  
   836  // readConfigAndSetRoot reads the .plzconfig files and moves to the repo root.
   837  func readConfigAndSetRoot(forceUpdate bool) *core.Configuration {
   838  	if opts.BuildFlags.RepoRoot == "" {
   839  		log.Debug("Found repo root at %s", core.MustFindRepoRoot())
   840  	} else {
   841  		core.RepoRoot = string(opts.BuildFlags.RepoRoot)
   842  	}
   843  
   844  	// Please always runs from the repo root, so move there now.
   845  	if err := os.Chdir(core.RepoRoot); err != nil {
   846  		log.Fatalf("%s", err)
   847  	}
   848  	// Reset this now we're at the repo root.
   849  	if opts.OutputFlags.LogFile != "" {
   850  		if !path.IsAbs(string(opts.OutputFlags.LogFile)) {
   851  			opts.OutputFlags.LogFile = cli.Filepath(path.Join(core.RepoRoot, string(opts.OutputFlags.LogFile)))
   852  		}
   853  		cli.InitFileLogging(string(opts.OutputFlags.LogFile), opts.OutputFlags.LogFileLevel)
   854  	}
   855  
   856  	return readConfig(forceUpdate)
   857  }
   858  
   859  // handleCompletions handles shell completion. Typically it just prints to stdout but
   860  // may do a little more if we think we need to handle aliases.
   861  func handleCompletions(parser *flags.Parser, items []flags.Completion) {
   862  	if len(items) > 0 {
   863  		cli.PrintCompletions(items)
   864  	} else {
   865  		cli.InitLogging(0)                // Ensure this is quiet
   866  		opts.FeatureFlags.NoUpdate = true // Ensure we don't try to update
   867  		config := readConfigAndSetRoot(false)
   868  		if len(config.Aliases) > 0 {
   869  			for k, v := range config.Aliases {
   870  				parser.AddCommand(k, v, v, &struct{}{})
   871  			}
   872  			// Run again without this registered as a completion handler
   873  			parser.CompletionHandler = nil
   874  			parser.ParseArgs(os.Args[1:])
   875  		}
   876  	}
   877  	// Regardless of what happened, always exit with 0 at this point.
   878  	os.Exit(0)
   879  }
   880  
   881  func main() {
   882  	parser, extraArgs, flagsErr := cli.ParseFlags("Please", &opts, os.Args, handleCompletions)
   883  	// Note that we must leave flagsErr for later, because it may be affected by aliases.
   884  	if opts.OutputFlags.Version {
   885  		fmt.Printf("Please version %s\n", core.PleaseVersion)
   886  		os.Exit(0) // Ignore other flags if --version was passed.
   887  	}
   888  	if opts.OutputFlags.Colour {
   889  		output.SetColouredOutput(true)
   890  	} else if opts.OutputFlags.NoColour {
   891  		output.SetColouredOutput(false)
   892  	}
   893  	if opts.OutputFlags.ShowAllOutput {
   894  		opts.OutputFlags.PlainOutput = true
   895  	}
   896  	// Init logging, but don't do file output until we've chdir'd.
   897  	cli.InitLogging(opts.OutputFlags.Verbosity)
   898  
   899  	command := cli.ActiveCommand(parser.Command)
   900  	if opts.Complete != "" {
   901  		// Completion via PLZ_COMPLETE env var sidesteps other commands
   902  		opts.Query.Completions.Cmd = command
   903  		opts.Query.Completions.Args.Fragments = []string{opts.Complete}
   904  		command = "completions"
   905  	} else if command == "init" {
   906  		if flagsErr != nil { // This error otherwise doesn't get checked until later.
   907  			cli.ParseFlagsFromArgsOrDie("Please", core.PleaseVersion.String(), &opts, os.Args)
   908  		}
   909  		// If we're running plz init then we obviously don't expect to read a config file.
   910  		utils.InitConfig(string(opts.Init.Dir), opts.Init.BazelCompatibility)
   911  		os.Exit(0)
   912  	} else if command == "help" || command == "follow" {
   913  		config = core.DefaultConfiguration()
   914  		if !buildFunctions[command]() {
   915  			os.Exit(1)
   916  		}
   917  		os.Exit(0)
   918  	} else if opts.OutputFlags.CompletionScript {
   919  		utils.PrintCompletionScript()
   920  		os.Exit(0)
   921  	}
   922  	// Read the config now
   923  	config = readConfigAndSetRoot(command == "update")
   924  	if parser.Command.Active != nil && parser.Command.Active.Name == "query" {
   925  		// Query commands don't need either of these set.
   926  		opts.OutputFlags.PlainOutput = true
   927  		config.Cache.DirClean = false
   928  	}
   929  
   930  	// Now we've read the config file, we may need to re-run the parser; the aliases in the config
   931  	// can affect how we parse otherwise illegal flag combinations.
   932  	if flagsErr != nil || len(extraArgs) > 0 {
   933  		for idx, arg := range os.Args[1:] {
   934  			// Please should not touch anything that comes after `--`
   935  			if arg == "--" {
   936  				break
   937  			}
   938  			for k, v := range config.Aliases {
   939  				if arg == k {
   940  					// We could insert every token in v into os.Args at this point and then we could have
   941  					// aliases defined in terms of other aliases but that seems rather like overkill so just
   942  					// stick the replacement in wholesale instead.
   943  					os.Args[idx+1] = v
   944  				}
   945  			}
   946  		}
   947  		argv := strings.Join(os.Args[1:], " ")
   948  		command = cli.ParseFlagsFromArgsOrDie("Please", core.PleaseVersion.String(), &opts, strings.Fields(os.Args[0]+" "+argv))
   949  	}
   950  
   951  	if opts.ProfilePort != 0 {
   952  		go func() {
   953  			log.Warning("%s", http.ListenAndServe(fmt.Sprintf("127.0.0.1:%d", opts.ProfilePort), nil))
   954  		}()
   955  	}
   956  	if opts.Profile != "" {
   957  		f, err := os.Create(opts.Profile)
   958  		if err != nil {
   959  			log.Fatalf("Failed to open profile file: %s", err)
   960  		}
   961  		if err := pprof.StartCPUProfile(f); err != nil {
   962  			log.Fatalf("could not start profiler: %s", err)
   963  		}
   964  		defer pprof.StopCPUProfile()
   965  	}
   966  	if opts.MemProfile != "" {
   967  		f, err := os.Create(opts.MemProfile)
   968  		if err != nil {
   969  			log.Fatalf("Failed to open memory profile file: %s", err)
   970  		}
   971  		defer f.Close()
   972  		defer pprof.WriteHeapProfile(f)
   973  	}
   974  
   975  	if !buildFunctions[command]() {
   976  		os.Exit(7) // Something distinctive, is sometimes useful to identify this externally.
   977  	}
   978  }