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