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  }