github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/core/config.go (about) 1 // Utilities for reading the Please config files. 2 3 package core 4 5 import ( 6 "crypto/sha1" 7 "encoding/gob" 8 "fmt" 9 "os" 10 "path" 11 "reflect" 12 "runtime" 13 "sort" 14 "strconv" 15 "strings" 16 "sync" 17 "time" 18 19 "github.com/jessevdk/go-flags" 20 "gopkg.in/gcfg.v1" 21 22 "cli" 23 ) 24 25 // OsArch is the os/arch pair, like linux_amd64 etc. 26 const OsArch = runtime.GOOS + "_" + runtime.GOARCH 27 28 // ConfigFileName is the file name for the typical repo config - this is normally checked in 29 const ConfigFileName string = ".plzconfig" 30 31 // ArchConfigFileName is the architecture-specific config file which overrides the repo one. 32 // Also normally checked in if needed. 33 const ArchConfigFileName string = ".plzconfig_" + OsArch 34 35 // LocalConfigFileName is the file name for the local repo config - this is not normally checked 36 // in and used to override settings on the local machine. 37 const LocalConfigFileName string = ".plzconfig.local" 38 39 // MachineConfigFileName is the file name for the machine-level config - can use this to override 40 // things for a particular machine (eg. build machine with different caching behaviour). 41 const MachineConfigFileName = "/etc/plzconfig" 42 43 // UserConfigFileName is the file name for user-specific config (for all their repos). 44 const UserConfigFileName = "~/.please/plzconfig" 45 46 // The available container implementations that we support. 47 const ( 48 ContainerImplementationNone = "none" 49 ContainerImplementationDocker = "docker" 50 ) 51 52 func readConfigFile(config *Configuration, filename string) error { 53 log.Debug("Reading config from %s...", filename) 54 if err := gcfg.ReadFileInto(config, filename); err != nil && os.IsNotExist(err) { 55 return nil // It's not an error to not have the file at all. 56 } else if gcfg.FatalOnly(err) != nil { 57 return err 58 } else if err != nil { 59 log.Warning("Error in config file: %s", err) 60 } 61 return nil 62 } 63 64 // ReadConfigFiles reads all the config locations, in order, and merges them into a config object. 65 // Values are filled in by defaults initially and then overridden by each file in turn. 66 func ReadConfigFiles(filenames []string, profile string) (*Configuration, error) { 67 config := DefaultConfiguration() 68 for _, filename := range filenames { 69 if err := readConfigFile(config, filename); err != nil { 70 return config, err 71 } 72 if profile != "" { 73 if err := readConfigFile(config, filename+"."+profile); err != nil { 74 return config, err 75 } 76 } 77 } 78 // Set default values for slices. These add rather than overwriting so we can't set 79 // them upfront as we would with other config values. 80 if usingBazelWorkspace { 81 setDefault(&config.Parse.BuildFileName, []string{"BUILD.bazel", "BUILD"}) 82 } else { 83 setDefault(&config.Parse.BuildFileName, []string{"BUILD"}) 84 } 85 setDefault(&config.Build.Path, []string{"/usr/local/bin", "/usr/bin", "/bin"}) 86 setDefault(&config.Build.PassEnv, []string{}) 87 setDefault(&config.Cover.FileExtension, []string{".go", ".py", ".java", ".js", ".cc", ".h", ".c"}) 88 setDefault(&config.Cover.ExcludeExtension, []string{".pb.go", "_pb2.py", ".pb.cc", ".pb.h", "_test.py", "_test.go", "_pb.go", "_bindata.go", "_test_main.cc"}) 89 setDefault(&config.Proto.Language, []string{"cc", "py", "java", "go", "js"}) 90 91 // Default values for these guys depend on config.Please.Location. 92 defaultPath(&config.Go.TestTool, config.Please.Location, "please_go_test") 93 defaultPath(&config.Go.FilterTool, config.Please.Location, "please_go_filter") 94 defaultPath(&config.Python.PexTool, config.Please.Location, "please_pex") 95 defaultPath(&config.Java.JavacWorker, config.Please.Location, "javac_worker") 96 defaultPath(&config.Java.JarCatTool, config.Please.Location, "jarcat") 97 defaultPath(&config.Java.PleaseMavenTool, config.Please.Location, "please_maven") 98 defaultPath(&config.Java.JUnitRunner, config.Please.Location, "junit_runner.jar") 99 100 // Default values for these guys depend on config.Java.JavaHome if that's been set. 101 if config.Java.JavaHome != "" { 102 defaultPathIfExists(&config.Java.JlinkTool, config.Java.JavaHome, "bin/jlink") 103 } 104 105 if (config.Cache.RPCPrivateKey == "") != (config.Cache.RPCPublicKey == "") { 106 return config, fmt.Errorf("Must pass both rpcprivatekey and rpcpublickey properties for cache") 107 } 108 109 // We can only verify options by reflection (we need struct tags) so run them quickly through this. 110 return config, config.ApplyOverrides(map[string]string{ 111 "test.defaultcontainer": config.Test.DefaultContainer, 112 "python.testrunner": config.Python.TestRunner, 113 }) 114 } 115 116 // setDefault sets a slice of strings in the config if the set one is empty. 117 func setDefault(conf *[]string, def []string) { 118 if len(*conf) == 0 { 119 *conf = def 120 } 121 } 122 123 // defaultPath sets a variable to a location in a directory if it's not already set. 124 func defaultPath(conf *string, dir, file string) { 125 if *conf == "" { 126 *conf = path.Join(dir, file) 127 } 128 } 129 130 // defaultPathIfExists sets a variable to a location in a directory if it's not already set and if the location exists. 131 func defaultPathIfExists(conf *string, dir, file string) { 132 if *conf == "" { 133 location := path.Join(dir, file) 134 // check that the location is valid 135 if _, err := os.Stat(location); err == nil { 136 *conf = location 137 } 138 } 139 } 140 141 // DefaultConfiguration returns the default configuration object with no overrides. 142 func DefaultConfiguration() *Configuration { 143 config := Configuration{buildEnvStored: &storedBuildEnv{}} 144 config.Please.Location = "~/.please" 145 config.Please.SelfUpdate = true 146 config.Please.Autoclean = true 147 config.Please.DownloadLocation = "https://get.please.build" 148 config.Please.NumOldVersions = 10 149 config.Build.Arch = cli.NewArch(runtime.GOOS, runtime.GOARCH) 150 config.Build.Lang = "en_GB.UTF-8" // Not the language of the UI, the language passed to rules. 151 config.Build.Nonce = "1402" // Arbitrary nonce to invalidate config when needed. 152 config.Build.Timeout = cli.Duration(10 * time.Minute) 153 config.Build.Config = "opt" // Optimised builds by default 154 config.Build.FallbackConfig = "opt" // Optimised builds as a fallback on any target that doesn't have a matching one set 155 config.Build.PleaseSandboxTool = "please_sandbox" 156 config.BuildConfig = map[string]string{} 157 config.BuildEnv = map[string]string{} 158 config.Aliases = map[string]string{} 159 config.Cache.HTTPTimeout = cli.Duration(5 * time.Second) 160 config.Cache.RPCTimeout = cli.Duration(5 * time.Second) 161 config.Cache.Dir = ".plz-cache" 162 config.Cache.DirCacheHighWaterMark = 10 * cli.GiByte 163 config.Cache.DirCacheLowWaterMark = 8 * cli.GiByte 164 config.Cache.DirClean = true 165 config.Cache.Workers = runtime.NumCPU() + 2 // Mirrors the number of workers in please.go. 166 config.Cache.RPCMaxMsgSize.UnmarshalFlag("200MiB") 167 config.Metrics.PushFrequency = cli.Duration(400 * time.Millisecond) 168 config.Metrics.PushTimeout = cli.Duration(500 * time.Millisecond) 169 config.Test.Timeout = cli.Duration(10 * time.Minute) 170 config.Test.DefaultContainer = ContainerImplementationDocker 171 config.Docker.DefaultImage = "ubuntu:trusty" 172 config.Docker.AllowLocalFallback = false 173 config.Docker.Timeout = cli.Duration(20 * time.Minute) 174 config.Docker.ResultsTimeout = cli.Duration(20 * time.Second) 175 config.Docker.RemoveTimeout = cli.Duration(20 * time.Second) 176 config.Go.GoTool = "go" 177 config.Go.CgoCCTool = "gcc" 178 config.Go.GoPath = "$TMP_DIR:$TMP_DIR/src:$TMP_DIR/$PKG_DIR:$TMP_DIR/third_party/go:$TMP_DIR/third_party/" 179 config.Python.PipTool = "pip3" 180 config.Python.DefaultInterpreter = "python3" 181 config.Python.TestRunner = "unittest" 182 config.Python.UsePyPI = true 183 // Annoyingly pip on OSX doesn't seem to work with this flag (you get the dreaded 184 // "must supply either home or prefix/exec-prefix" error). Goodness knows why *adding* this 185 // flag - which otherwise seems exactly what we want - provokes that error, but the logic 186 // of pip is rather a mystery to me. 187 if runtime.GOOS != "darwin" { 188 config.Python.PipFlags = "--isolated" 189 } 190 config.Java.DefaultTestPackage = "" 191 config.Java.SourceLevel = "8" 192 config.Java.TargetLevel = "8" 193 config.Java.ReleaseLevel = "" 194 config.Java.DefaultMavenRepo = []cli.URL{"https://repo1.maven.org/maven2"} 195 config.Java.JavacFlags = "-Werror -Xlint:-options" // bootstrap class path warnings are pervasive without this. 196 config.Java.JlinkTool = "jlink" 197 config.Java.JavaHome = "" 198 config.Cpp.CCTool = "gcc" 199 config.Cpp.CppTool = "g++" 200 config.Cpp.LdTool = "ld" 201 config.Cpp.ArTool = "ar" 202 config.Cpp.AsmTool = "nasm" 203 config.Cpp.DefaultOptCflags = "--std=c99 -O3 -pipe -DNDEBUG -Wall -Werror" 204 config.Cpp.DefaultDbgCflags = "--std=c99 -g3 -pipe -DDEBUG -Wall -Werror" 205 config.Cpp.DefaultOptCppflags = "--std=c++11 -O3 -pipe -DNDEBUG -Wall -Werror" 206 config.Cpp.DefaultDbgCppflags = "--std=c++11 -g3 -pipe -DDEBUG -Wall -Werror" 207 config.Cpp.Coverage = true 208 config.Proto.ProtocTool = "protoc" 209 // We're using the most common names for these; typically gRPC installs the builtin plugins 210 // as grpc_python_plugin etc. 211 config.Proto.ProtocGoPlugin = "protoc-gen-go" 212 config.Proto.GrpcPythonPlugin = "grpc_python_plugin" 213 config.Proto.GrpcJavaPlugin = "protoc-gen-grpc-java" 214 config.Proto.GrpcCCPlugin = "grpc_cpp_plugin" 215 config.Proto.PythonDep = "//third_party/python:protobuf" 216 config.Proto.JavaDep = "//third_party/java:protobuf" 217 config.Proto.GoDep = "//third_party/go:protobuf" 218 config.Proto.JsDep = "" 219 config.Proto.PythonGrpcDep = "//third_party/python:grpc" 220 config.Proto.JavaGrpcDep = "//third_party/java:grpc-all" 221 config.Proto.GoGrpcDep = "//third_party/go:grpc" 222 config.Bazel.Compatibility = usingBazelWorkspace 223 return &config 224 } 225 226 // A Configuration contains all the settings that can be configured about Please. 227 // This is parsed from .plzconfig etc; we also auto-generate help messages from its tags. 228 type Configuration struct { 229 Please struct { 230 Version cli.Version `help:"Defines the version of plz that this repo is supposed to use currently. If it's not present or the version matches the currently running version no special action is taken; otherwise if SelfUpdate is set Please will attempt to download an appropriate version, otherwise it will issue a warning and continue.\n\nNote that if this is not set, you can run plz update to update to the latest version available on the server." var:"PLZ_VERSION"` 231 Location string `help:"Defines the directory Please is installed into.\nDefaults to ~/.please but you might want it to be somewhere else if you're installing via another method (e.g. the debs and install script still use /opt/please)."` 232 SelfUpdate bool `help:"Sets whether plz will attempt to update itself when the version set in the config file is different."` 233 DownloadLocation cli.URL `help:"Defines the location to download Please from when self-updating. Defaults to the Please web server, but you can point it to some location of your own if you prefer to keep traffic within your network or use home-grown versions."` 234 NumOldVersions int `help:"Number of old versions to keep from autoupdates."` 235 Autoclean bool `help:"Automatically clean stale versions without prompting"` 236 NumThreads int `help:"Number of parallel build operations to run.\nIs overridden by the equivalent command-line flag, if that's passed." example:"6"` 237 Motd []string `help:"Message of the day; is displayed once at the top during builds. If multiple are given, one is randomly chosen."` 238 } `help:"The [please] section in the config contains non-language-specific settings defining how Please should operate."` 239 Parse struct { 240 ExperimentalDir []string `help:"Directory containing experimental code. This is subject to some extra restrictions:\n - Code in the experimental dir can override normal visibility constraints\n - Code outside the experimental dir can never depend on code inside it\n - Tests are excluded from general detection." example:"experimental"` 241 BuildFileName []string `help:"Sets the names that Please uses instead of BUILD for its build files.\nFor clarity the documentation refers to them simply as BUILD files but you could reconfigure them here to be something else.\nOne case this can be particularly useful is in cases where you have a subdirectory named build on a case-insensitive file system like HFS+."` 242 BlacklistDirs []string `help:"Directories to blacklist when recursively searching for BUILD files (e.g. when using plz build ... or similar).\nThis is generally useful when you have large directories within your repo that don't need to be searched, especially things like node_modules that have come from external package managers."` 243 PreloadBuildDefs []string `help:"Files to preload by the parser before loading any BUILD files.\nSince this is done before the first package is parsed they must be files in the repository, they cannot be subinclude() paths." example:"build_defs/go_bindata.build_defs"` 244 } `help:"The [parse] section in the config contains settings specific to parsing files."` 245 Display struct { 246 UpdateTitle bool `help:"Updates the title bar of the shell window Please is running in as the build progresses. This isn't on by default because not everyone's shell is configured to reset it again after and we don't want to alter it forever."` 247 SystemStats bool `help:"Whether or not to show basic system resource usage in the interactive display. Has no effect without that configured."` 248 } `help:"Please has an animated display mode which shows the currently building targets.\nBy default it will autodetect whether it is using an interactive TTY session and choose whether to use it or not, although you can force it on or off via flags.\n\nThe display is heavily inspired by Buck's SuperConsole."` 249 Events struct { 250 Port int `help:"Port to start the streaming build event server on."` 251 } `help:"The [events] section in the config contains settings relating to the internal build event system & streaming them externally."` 252 Build struct { 253 Arch cli.Arch `help:"Architecture to compile for. Defaults to the host architecture."` 254 Timeout cli.Duration `help:"Default timeout for Dockerised tests, in seconds. Default is twenty minutes."` 255 Path []string `help:"The PATH variable that will be passed to the build processes.\nDefaults to /usr/local/bin:/usr/bin:/bin but of course can be modified if you need to get binaries from other locations." example:"/usr/local/bin:/usr/bin:/bin"` 256 Config string `help:"The build config to use when one is not chosen on the command line. Defaults to opt." example:"opt | dbg"` 257 FallbackConfig string `help:"The build config to use when one is chosen and a required target does not have one by the same name. Also defaults to opt." example:"opt | dbg"` 258 Lang string `help:"Sets the language passed to build rules when building. This can be important for some tools (although hopefully not many) - we've mostly observed it with Sass."` 259 Sandbox bool `help:"True to sandbox individual build actions, which isolates them using namespaces. Somewhat experimental, only works on Linux and requires please_sandbox to be installed separately." var:"BUILD_SANDBOX"` 260 PleaseSandboxTool string `help:"The location of the please_sandbox tool to use."` 261 Nonce string `help:"This is an arbitrary string that is added to the hash of every build target. It provides a way to force a rebuild of everything when it's changed.\nWe will bump the default of this whenever we think it's required - although it's been a pretty long time now and we hope that'll continue."` 262 PassEnv []string `help:"A list of environment variables to pass from the current environment to build rules. For example\n\nPassEnv = HTTP_PROXY\n\nwould copy your HTTP_PROXY environment variable to the build env for any rules."` 263 } 264 BuildConfig map[string]string `help:"A section of arbitrary key-value properties that are made available in the BUILD language. These are often useful for writing custom rules that need some configurable property.\n\n[buildconfig]\nandroid-tools-version = 23.0.2\n\nFor example, the above can be accessed as CONFIG.ANDROID_TOOLS_VERSION."` 265 BuildEnv map[string]string `help:"A set of extra environment variables to define for build rules. For example:\n\n[buildenv]\nsecret-passphrase = 12345\n\nThis would become SECRET_PASSPHRASE for any rules. These can be useful for passing secrets into custom rules; any variables containing SECRET or PASSWORD won't be logged.\n\nIt's also useful if you'd like internal tools to honour some external variable."` 266 Cache struct { 267 Workers int `help:"Number of workers for uploading artifacts to remote caches, which is done asynchronously."` 268 Dir string `help:"Sets the directory to use for the dir cache.\nThe default is .plz-cache, if set to the empty string the dir cache will be disabled."` 269 DirCacheCleaner string `help:"The binary to use for cleaning the directory cache.\nDefaults to cache_cleaner in the plz install directory.\nCan also be set to the empty string to disable attempting to run it - note that this will of course lead to the dir cache growing without limit which may ruin your day if it fills your disk :)"` 270 DirCacheHighWaterMark cli.ByteSize `help:"Starts cleaning the directory cache when it is over this number of bytes.\nCan also be given with human-readable suffixes like 10G, 200MB etc."` 271 DirCacheLowWaterMark cli.ByteSize `help:"When cleaning the directory cache, it's reduced to at most this size."` 272 DirClean bool `help:"Controls whether entries in the dir cache are cleaned or not. If disabled the cache will only grow."` 273 HTTPURL cli.URL `help:"Base URL of the HTTP cache.\nNot set to anything by default which means the cache will be disabled."` 274 HTTPWriteable bool `help:"If True this plz instance will write content back to the HTTP cache.\nBy default it runs in read-only mode."` 275 HTTPTimeout cli.Duration `help:"Timeout for operations contacting the HTTP cache, in seconds."` 276 RPCURL cli.URL `help:"Base URL of the RPC cache.\nNot set to anything by default which means the cache will be disabled."` 277 RPCWriteable bool `help:"If True this plz instance will write content back to the RPC cache.\nBy default it runs in read-only mode."` 278 RPCTimeout cli.Duration `help:"Timeout for operations contacting the RPC cache, in seconds."` 279 RPCPublicKey string `help:"File containing a PEM-encoded private key which is used to authenticate to the RPC cache." example:"my_key.pem"` 280 RPCPrivateKey string `help:"File containing a PEM-encoded certificate which is used to authenticate to the RPC cache." example:"my_cert.pem"` 281 RPCCACert string `help:"File containing a PEM-encoded certificate which is used to validate the RPC cache's certificate." example:"ca.pem"` 282 RPCSecure bool `help:"Forces SSL on for the RPC cache. It will be activated if any of rpcpublickey, rpcprivatekey or rpccacert are set, but this can be used if none of those are needed and SSL is still in use."` 283 RPCMaxMsgSize cli.ByteSize `help:"Maximum size of a single message that we'll send to the RPC server.\nThis should agree with the server's limit, if it's higher the artifacts will be rejected.\nThe value is given as a byte size so can be suffixed with M, GB, KiB, etc."` 284 } `help:"Please has several built-in caches that can be configured in its config file.\n\nThe simplest one is the directory cache which by default is written into the .plz-cache directory. This allows for fast retrieval of code that has been built before (for example, when swapping Git branches).\n\nThere is also a remote RPC cache which allows using a centralised server to store artifacts. A typical pattern here is to have your CI system write artifacts into it and give developers read-only access so they can reuse its work.\n\nFinally there's a HTTP cache which is very similar, but a little obsolete now since the RPC cache outperforms it and has some extra features. Otherwise the two have similar semantics and share quite a bit of implementation.\n\nPlease has server implementations for both the RPC and HTTP caches."` 285 Metrics struct { 286 PushGatewayURL cli.URL `help:"The URL of the pushgateway to send metrics to."` 287 PushFrequency cli.Duration `help:"The frequency, in milliseconds, to push statistics at." example:"400ms"` 288 PushTimeout cli.Duration `help:"Timeout on pushes to the metrics repository." example:"500ms"` 289 PerTest bool `help:"Emit per-test duration metrics. Off by default because they generate increased load on Prometheus."` 290 } `help:"A section of options relating to reporting metrics. Currently only pushing metrics to a Prometheus pushgateway is supported, which is enabled by the pushgatewayurl setting."` 291 CustomMetricLabels map[string]string `help:"Allows defining custom labels to be applied to metrics. The key is the name of the label, and the value is a command to be run, the output of which becomes the label's value. For example, to attach the current Git branch to all metrics:\n\n[custommetriclabels]\nbranch = git rev-parse --abbrev-ref HEAD\n\nBe careful when defining new labels, it is quite possible to overwhelm the metric collector by creating metric sets with too high cardinality."` 292 Test struct { 293 Timeout cli.Duration `help:"Default timeout applied to all tests. Can be overridden on a per-rule basis."` 294 DefaultContainer string `help:"Sets the default type of containerisation to use for tests that are given container = True.\nCurrently the only available option is 'docker', we expect to add support for more engines in future." options:"none,docker"` 295 Sandbox bool `help:"True to sandbox individual tests, which isolates them using namespaces. Somewhat experimental, only works on Linux and requires please_sandbox to be installed separately." var:"TEST_SANDBOX"` 296 } 297 Cover struct { 298 FileExtension []string `help:"Extensions of files to consider for coverage.\nDefaults to a reasonably obvious set for the builtin rules including .go, .py, .java, etc."` 299 ExcludeExtension []string `help:"Extensions of files to exclude from coverage.\nTypically this is for generated code; the default is to exclude protobuf extensions like .pb.go, _pb2.py, etc."` 300 } 301 Docker struct { 302 DefaultImage string `help:"The default image used for any test that doesn't specify another."` 303 AllowLocalFallback bool `help:"If True, will attempt to run the test locally if containerised running fails."` 304 Timeout cli.Duration `help:"Default timeout for containerised tests. Can be overridden on a per-rule basis."` 305 ResultsTimeout cli.Duration `help:"Timeout to wait when trying to retrieve results from inside the container. Default is 20 seconds."` 306 RemoveTimeout cli.Duration `help:"Timeout to wait when trying to remove a container after running a test. Defaults to 20 seconds."` 307 RunArgs []string `help:"Arguments passed to docker run when running a test." example:"-e LANG=en_GB"` 308 } `help:"Please supports running individual tests within Docker containers for isolation. This is useful for tests that mutate some global state (such as an embedded database, or open a server on a particular port). To do so, simply mark a test rule with container = True."` 309 Gc struct { 310 Keep []BuildLabel `help:"Marks targets that gc should always keep. Can include meta-targets such as //test/... and //docs:all."` 311 KeepLabel []string `help:"Defines a target label to be kept; for example, if you set this to go, no Go targets would ever be considered for deletion." example:"go"` 312 } `help:"Please supports a form of 'garbage collection', by which it means identifying targets that are not used for anything. By default binary targets and all their transitive dependencies are always considered non-garbage, as are any tests directly on those. The config options here allow tweaking this behaviour to retain more things.\n\nNote that it's a very good idea that your BUILD files are in the standard format when running this."` 313 Go struct { 314 GoTool string `help:"The binary to use to invoke Go & its subtools with." var:"GO_TOOL"` 315 GoRoot string `help:"If set, will set the GOROOT environment variable appropriately during build actions."` 316 TestTool string `help:"Sets the location of the please_go_test tool that is used to template the test main for go_test rules." var:"GO_TEST_TOOL"` 317 GoPath string `help:"If set, will set the GOPATH environment variable appropriately during build actions." var:"GOPATH"` 318 ImportPath string `help:"Sets the default Go import path at the root of this repository.\nFor example, in the Please repo, we might set it to github.com/thought-machine/please to allow imports from that package within the repo." var:"GO_IMPORT_PATH"` 319 CgoCCTool string `help:"Sets the location of CC while building cgo_library and cgo_test rules. Defaults to gcc" var:"CGO_CC_TOOL"` 320 FilterTool string `help:"Sets the location of the please_go_filter tool that is used to filter source files against build constraints." var:"GO_FILTER_TOOL"` 321 DefaultStatic bool `help:"Sets Go binaries to default to static linking. Note that enabling this may have negative consequences for some code, including Go's DNS lookup code in the net module." var:"GO_DEFAULT_STATIC"` 322 } `help:"Please has built-in support for compiling Go, and of course is written in Go itself.\nSee the config subfields or the Go rules themselves for more information.\n\nNote that Please is a bit more flexible than Go about directory layout - for example, it is possible to have multiple packages in a directory, but it's not a good idea to push this too far since Go's directory layout is inextricably linked with its import paths."` 323 Python struct { 324 PipTool string `help:"The tool that is invoked during pip_library rules." var:"PIP_TOOL"` 325 PipFlags string `help:"Additional flags to pass to pip invocations in pip_library rules." var:"PIP_FLAGS"` 326 PexTool string `help:"The tool that's invoked to build pexes. Defaults to please_pex in the install directory." var:"PEX_TOOL"` 327 DefaultInterpreter string `help:"The interpreter used for python_binary and python_test rules when none is specified on the rule itself. Defaults to python but you could of course set it to, say, pypy." var:"DEFAULT_PYTHON_INTERPRETER"` 328 TestRunner string `help:"The test runner used to discover & run Python tests; one of unittest or pytest." var:"PYTHON_TEST_RUNNER" options:"unittest,pytest"` 329 ModuleDir string `help:"Defines a directory containing modules from which they can be imported at the top level.\nBy default this is empty but by convention we define our pip_library rules in third_party/python and set this appropriately. Hence any of those third-party libraries that try something like import six will have it work as they expect, even though it's actually in a different location within the .pex." var:"PYTHON_MODULE_DIR"` 330 DefaultPipRepo cli.URL `help:"Defines a location for a pip repo to download wheels from.\nBy default pip_library uses PyPI (although see below on that) but you may well want to use this define another location to upload your own wheels to.\nIs overridden by the repo argument to pip_library." var:"PYTHON_DEFAULT_PIP_REPO"` 331 WheelRepo cli.URL `help:"Defines a location for a remote repo that python_wheel rules will download from. See python_wheel for more information." var:"PYTHON_WHEEL_REPO"` 332 UsePyPI bool `help:"Whether or not to use PyPI for pip_library rules or not. Defaults to true, if you disable this you will presumably want to set DefaultPipRepo to use one of your own.\nIs overridden by the use_pypi argument to pip_library." var:"USE_PYPI"` 333 WheelNameScheme string `help:"Defines a custom templatized wheel naming scheme. Templatized variables should be surrounded in curly braces, and the available options are: url_base, package_name, and version. The default search pattern is '{url_base}/{package_name}-{version}-${{OS}}-${{ARCH}}.whl' along with a few common variants." var:"PYTHON_WHEEL_NAME_SCHEME"` 334 } `help:"Please has built-in support for compiling Python.\nPlease's Python artifacts are pex files, which are essentially self-executable zip files containing all needed dependencies, bar the interpreter itself. This fits our aim of at least semi-static binaries for each language.\nSee https://github.com/pantsbuild/pex for more information.\nNote that due to differences between the environment inside a pex and outside some third-party code may not run unmodified (for example, it cannot simply open() files). It's possible to work around a lot of this, but if it all becomes too much it's possible to mark pexes as not zip-safe which typically resolves most of it at a modest speed penalty."` 335 Java struct { 336 JavacTool string `help:"Defines the tool used for the Java compiler. Defaults to javac." var:"JAVAC_TOOL"` 337 JlinkTool string `help:"Defines the tool used for the Java linker. Defaults to jlink." var:"JLINK_TOOL"` 338 JavaHome string `help:"Defines the path of the Java Home folder." var:"JAVA_HOME"` 339 JavacWorker string `help:"Defines the tool used for the Java persistent compiler. This is significantly (approx 4x) faster for large Java trees than invoking javac separately each time. Default to javac_worker in the install directory, but can be switched off to fall back to javactool and separate invocation." var:"JAVAC_WORKER"` 340 JarCatTool string `help:"Defines the tool used to concatenate .jar files which we use to build the output of java_binary, java_test and various other rules. Defaults to jarcat in the Please install directory." var:"JARCAT_TOOL"` 341 PleaseMavenTool string `help:"Defines the tool used to fetch information from Maven in maven_jars rules.\nDefaults to please_maven in the Please install directory." var:"PLEASE_MAVEN_TOOL"` 342 JUnitRunner string `help:"Defines the .jar containing the JUnit runner. This is built into all java_test rules since it's necessary to make JUnit do anything useful.\nDefaults to junit_runner.jar in the Please install directory." var:"JUNIT_RUNNER"` 343 DefaultTestPackage string `help:"The Java classpath to search for functions annotated with @Test. If not specified the compiled sources will be searched for files named *Test.java." var:"DEFAULT_TEST_PACKAGE"` 344 ReleaseLevel string `help:"The default Java release level when compiling.\nSourceLevel and TargetLevel are ignored if this is set. Bear in mind that this flag is only supported in Java version 9+." var:"JAVA_RELEASE_LEVEL"` 345 SourceLevel string `help:"The default Java source level when compiling. Defaults to 8." var:"JAVA_SOURCE_LEVEL"` 346 TargetLevel string `help:"The default Java bytecode level to target. Defaults to 8." var:"JAVA_TARGET_LEVEL"` 347 JavacFlags string `help:"Additional flags to pass to javac when compiling libraries." example:"-Xmx1200M" var:"JAVAC_FLAGS"` 348 JavacTestFlags string `help:"Additional flags to pass to javac when compiling tests." example:"-Xmx1200M" var:"JAVAC_TEST_FLAGS"` 349 DefaultMavenRepo []cli.URL `help:"Default location to load artifacts from in maven_jar rules. Can be overridden on a per-rule basis." var:"DEFAULT_MAVEN_REPO"` 350 } `help:"Please has built-in support for compiling Java.\nIt builds uber-jars for binary and test rules which contain all dependencies and can be easily deployed, and with the help of some of Please's additional tools they are deterministic as well.\n\nWe've only tested support for Java 7 and 8, although it's likely newer versions will work with little or no change."` 351 Cpp struct { 352 CCTool string `help:"The tool invoked to compile C code. Defaults to gcc but you might want to set it to clang, for example." var:"CC_TOOL"` 353 CppTool string `help:"The tool invoked to compile C++ code. Defaults to g++ but you might want to set it to clang++, for example." var:"CPP_TOOL"` 354 LdTool string `help:"The tool invoked to link object files. Defaults to ld but you could also set it to gold, for example." var:"LD_TOOL"` 355 ArTool string `help:"The tool invoked to archive static libraries. Defaults to ar." var:"AR_TOOL"` 356 AsmTool string `help:"The tool invoked as an assembler. Currently only used on OSX for cc_embed_binary rules and so defaults to nasm." var:"ASM_TOOL"` 357 LinkWithLdTool bool `help:"If true, instructs Please to use the tool set earlier in ldtool to link binaries instead of cctool.\nThis is an esoteric setting that most people don't want; a vanilla ld will not perform all steps necessary here (you'll get lots of missing symbol messages from having no libc etc). Generally best to leave this disabled unless you have very specific requirements." var:"LINK_WITH_LD_TOOL"` 358 DefaultOptCflags string `help:"Compiler flags passed to all C rules during opt builds; these are typically pretty basic things like what language standard you want to target, warning flags, etc.\nDefaults to --std=c99 -O3 -DNDEBUG -Wall -Wextra -Werror" var:"DEFAULT_OPT_CFLAGS"` 359 DefaultDbgCflags string `help:"Compiler rules passed to all C rules during dbg builds.\nDefaults to --std=c99 -g3 -DDEBUG -Wall -Wextra -Werror." var:"DEFAULT_DBG_CFLAGS"` 360 DefaultOptCppflags string `help:"Compiler flags passed to all C++ rules during opt builds; these are typically pretty basic things like what language standard you want to target, warning flags, etc.\nDefaults to --std=c++11 -O3 -DNDEBUG -Wall -Wextra -Werror" var:"DEFAULT_OPT_CPPFLAGS"` 361 DefaultDbgCppflags string `help:"Compiler rules passed to all C++ rules during dbg builds.\nDefaults to --std=c++11 -g3 -DDEBUG -Wall -Wextra -Werror." var:"DEFAULT_DBG_CPPFLAGS"` 362 DefaultLdflags string `help:"Linker flags passed to all C++ rules.\nBy default this is empty." var:"DEFAULT_LDFLAGS"` 363 DefaultNamespace string `help:"Namespace passed to all cc_embed_binary rules when not overridden by the namespace argument to that rule.\nNot set by default, if you want to use those rules you'll need to set it or pass it explicitly to each one." var:"DEFAULT_NAMESPACE"` 364 PkgConfigPath string `help:"Custom PKG_CONFIG_PATH for pkg-config.\nBy default this is empty." var:"PKG_CONFIG_PATH"` 365 Coverage bool `help:"If true (the default), coverage will be available for C and C++ build rules.\nThis is still a little experimental but should work for GCC. Right now it does not work for Clang (it likely will in Clang 4.0 which will likely support --fprofile-dir) and so this can be useful to disable it.\nIt's also useful in some cases for CI systems etc if you'd prefer to avoid the overhead, since the tests have to be compiled with extra instrumentation and without optimisation." var:"CPP_COVERAGE"` 366 } `help:"Please has built-in support for compiling C and C++ code. We don't support every possible nuance of compilation for these languages, but aim to provide something fairly straightforward.\nTypically there is little problem compiling & linking against system libraries although Please has no insight into those libraries and when they change, so cannot rebuild targets appropriately.\n\nThe C and C++ rules are very similar and simply take a different set of tools and flags to facilitate side-by-side usage."` 367 Proto struct { 368 ProtocTool string `help:"The binary invoked to compile .proto files. Defaults to protoc." var:"PROTOC_TOOL"` 369 ProtocGoPlugin string `help:"The binary passed to protoc as a plugin to generate Go code. Defaults to protoc-gen-go.\nWe've found this easier to manage with a go_get rule instead though, so you can also pass a build label here. See the Please repo for an example." var:"PROTOC_GO_PLUGIN"` 370 GrpcPythonPlugin string `help:"The plugin invoked to compile Python code for grpc_library.\nDefaults to protoc-gen-grpc-python." var:"GRPC_PYTHON_PLUGIN"` 371 GrpcJavaPlugin string `help:"The plugin invoked to compile Java code for grpc_library.\nDefaults to protoc-gen-grpc-java." var:"GRPC_JAVA_PLUGIN"` 372 GrpcCCPlugin string `help:"The plugin invoked to compile C++ code for grpc_library.\nDefaults to grpc_cpp_plugin." var:"GRPC_CC_PLUGIN"` 373 Language []string `help:"Sets the default set of languages that proto rules are built for.\nChosen from the set of {cc, java, go, py}.\nDefaults to all of them!" var:"PROTO_LANGUAGES"` 374 PythonDep string `help:"An in-repo dependency that's applied to any Python proto libraries." var:"PROTO_PYTHON_DEP"` 375 JavaDep string `help:"An in-repo dependency that's applied to any Java proto libraries." var:"PROTO_JAVA_DEP"` 376 GoDep string `help:"An in-repo dependency that's applied to any Go proto libraries." var:"PROTO_GO_DEP"` 377 JsDep string `help:"An in-repo dependency that's applied to any Javascript proto libraries." var:"PROTO_JS_DEP"` 378 PythonGrpcDep string `help:"An in-repo dependency that's applied to any Python gRPC libraries." var:"GRPC_PYTHON_DEP"` 379 JavaGrpcDep string `help:"An in-repo dependency that's applied to any Java gRPC libraries." var:"GRPC_JAVA_DEP"` 380 GoGrpcDep string `help:"An in-repo dependency that's applied to any Go gRPC libraries." var:"GRPC_GO_DEP"` 381 PythonPackage string `help:"Overrides the default package to import Python proto code from; useful to work with our typical third_party/python idiom." example:"third_party.python.google.protobuf" var:"PROTO_PYTHON_PACKAGE"` 382 } `help:"Please has built-in support for compiling protocol buffers, which are a form of codegen to define common data types which can be serialised and communicated between different languages.\nSee https://developers.google.com/protocol-buffers/ for more information.\n\nThere is also support for gRPC, which is an implementation of protobuf's RPC framework. See http://www.grpc.io/ for more information.\n\nNote that you must have the protocol buffers compiler (and gRPC plugins, if needed) installed on your machine to make use of these rules."` 383 Licences struct { 384 Accept []string `help:"Licences that are accepted in this repository.\nWhen this is empty licences are ignored. As soon as it's set any licence detected or assigned must be accepted explicitly here.\nThere's no fuzzy matching, so some package managers (especially PyPI and Maven, but shockingly not npm which rather nicely uses SPDX) will generate a lot of slightly different spellings of the same thing, which will all have to be accepted here. We'd rather that than trying to 'cleverly' match them which might result in matching the wrong thing."` 385 Reject []string `help:"Licences that are explicitly rejected in this repository.\nAn astute observer will notice that this is not very different to just not adding it to the accept section, but it does have the advantage of explicitly documenting things that the team aren't allowed to use."` 386 } `help:"Please has some limited support for declaring acceptable licences and detecting them from some libraries. You should not rely on this for complete licence compliance, but it can be a useful check to try to ensure that unacceptable licences do not slip in."` 387 Aliases map[string]string `help:"It is possible to define aliases for new commands in your .plzconfig file. These are essentially string-string replacements of the command line, for example 'deploy = run //tools:deployer --' makes 'plz deploy' run a particular tool."` 388 Bazel struct { 389 Compatibility bool `help:"Activates limited Bazel compatibility mode. When this is active several rule arguments are available under different names (e.g. compiler_flags -> copts etc), the WORKSPACE file is interpreted, Makefile-style replacements like $< and $@ are made in genrule commands, etc.\nNote that Skylark is not generally supported and many aspects of compatibility are fairly superficial; it's unlikely this will work for complex setups of either tool." var:"BAZEL_COMPATIBILITY"` 390 } `help:"Bazel is an open-sourced version of Google's internal build tool. Please draws a lot of inspiration from the original tool although the two have now diverged in various ways.\nNonetheless, if you've used Bazel, you will likely find Please familiar."` 391 392 // buildEnvStored is a cached form of BuildEnv. 393 buildEnvStored *storedBuildEnv 394 } 395 396 type storedBuildEnv struct { 397 Env []string 398 Once sync.Once 399 } 400 401 // Hash returns a hash of the parts of this configuration that affect building targets in general. 402 // Most parts are considered not to (e.g. cache settings) or affect specific targets (e.g. changing 403 // tool paths which get accounted for on the targets that use them). 404 func (config *Configuration) Hash() []byte { 405 h := sha1.New() 406 // These fields are the ones that need to be in the general hash; other things will be 407 // picked up by relevant rules (particularly tool paths etc). 408 // Note that container settings are handled separately. 409 for _, f := range config.Parse.BuildFileName { 410 h.Write([]byte(f)) 411 } 412 h.Write([]byte(config.Build.Lang)) 413 h.Write([]byte(config.Build.Nonce)) 414 for _, l := range config.Licences.Reject { 415 h.Write([]byte(l)) 416 } 417 for _, env := range config.GetBuildEnv() { 418 h.Write([]byte(env)) 419 } 420 return h.Sum(nil) 421 } 422 423 // ContainerisationHash returns the hash of the containerisation part of the config. 424 func (config *Configuration) ContainerisationHash() []byte { 425 h := sha1.New() 426 encoder := gob.NewEncoder(h) 427 if err := encoder.Encode(config.Docker); err != nil { 428 panic(err) 429 } 430 return h.Sum(nil) 431 } 432 433 // GetBuildEnv returns the build environment configured for this config object. 434 func (config *Configuration) GetBuildEnv() []string { 435 config.buildEnvStored.Once.Do(func() { 436 env := []string{ 437 // Need to know these for certain rules. 438 "ARCH=" + config.Build.Arch.Arch, 439 "OS=" + config.Build.Arch.OS, 440 // These are slightly modified forms that are more convenient for some things. 441 "XARCH=" + config.Build.Arch.XArch(), 442 "XOS=" + config.Build.Arch.XOS(), 443 // It's easier to just make these available for Go-based rules. 444 "GOARCH=" + config.Build.Arch.GoArch(), 445 "GOOS=" + config.Build.Arch.OS, 446 } 447 448 // from the BuildEnv config keyword 449 for k, v := range config.BuildEnv { 450 pair := strings.Replace(strings.ToUpper(k), "-", "_", -1) + "=" + v 451 env = append(env, pair) 452 } 453 454 // from the user's environment based on the PassEnv config keyword 455 for _, k := range config.Build.PassEnv { 456 if v, isSet := os.LookupEnv(k); isSet { 457 env = append(env, k+"="+v) 458 } 459 } 460 461 sort.Strings(env) 462 config.buildEnvStored.Env = env 463 }) 464 return config.buildEnvStored.Env 465 } 466 467 // ApplyOverrides applies a set of overrides to the config. 468 // The keys of the given map are dot notation for the config setting. 469 func (config *Configuration) ApplyOverrides(overrides map[string]string) error { 470 match := func(s1 string) func(string) bool { 471 return func(s2 string) bool { 472 return strings.ToLower(s2) == s1 473 } 474 } 475 elem := reflect.ValueOf(config).Elem() 476 for k, v := range overrides { 477 split := strings.Split(strings.ToLower(k), ".") 478 if len(split) != 2 { 479 return fmt.Errorf("Bad option format: %s", k) 480 } 481 field := elem.FieldByNameFunc(match(split[0])) 482 if !field.IsValid() { 483 return fmt.Errorf("Unknown config field: %s", split[0]) 484 } else if field.Kind() == reflect.Map { 485 field.SetMapIndex(reflect.ValueOf(split[1]), reflect.ValueOf(v)) 486 continue 487 } else if field.Kind() != reflect.Struct { 488 return fmt.Errorf("Unsettable config field: %s", split[0]) 489 } 490 subfield, ok := field.Type().FieldByNameFunc(match(split[1])) 491 if !ok { 492 return fmt.Errorf("Unknown config field: %s", split[1]) 493 } 494 field = field.FieldByNameFunc(match(split[1])) 495 switch field.Kind() { 496 case reflect.String: 497 // verify this is a legit setting for this field 498 if options := subfield.Tag.Get("options"); options != "" { 499 if !cli.ContainsString(v, strings.Split(options, ",")) { 500 return fmt.Errorf("Invalid value %s for field %s; options are %s", v, k, options) 501 } 502 } 503 if field.Type().Name() == "URL" { 504 field.Set(reflect.ValueOf(cli.URL(v))) 505 } else { 506 field.Set(reflect.ValueOf(v)) 507 } 508 case reflect.Bool: 509 v = strings.ToLower(v) 510 // Mimics the set of truthy things gcfg accepts in our config file. 511 field.SetBool(v == "true" || v == "yes" || v == "on" || v == "1") 512 case reflect.Int: 513 i, err := strconv.Atoi(v) 514 if err != nil { 515 return fmt.Errorf("Invalid value for an integer field: %s", v) 516 } 517 field.Set(reflect.ValueOf(i)) 518 case reflect.Int64: 519 var d cli.Duration 520 if err := d.UnmarshalText([]byte(v)); err != nil { 521 return fmt.Errorf("Invalid value for a duration field: %s", v) 522 } 523 field.Set(reflect.ValueOf(d)) 524 case reflect.Slice: 525 // Comma-separated values are accepted. 526 if field.Type().Elem().Kind() == reflect.Struct { 527 // Assume it must be a slice of BuildLabel. 528 l := []BuildLabel{} 529 for _, s := range strings.Split(v, ",") { 530 l = append(l, ParseBuildLabel(s, "")) 531 } 532 field.Set(reflect.ValueOf(l)) 533 } else if field.Type().Elem().Name() == "URL" { 534 urls := []cli.URL{} 535 for _, s := range strings.Split(v, ",") { 536 urls = append(urls, cli.URL(s)) 537 } 538 field.Set(reflect.ValueOf(urls)) 539 } else { 540 field.Set(reflect.ValueOf(strings.Split(v, ","))) 541 } 542 default: 543 return fmt.Errorf("Can't override config field %s (is %s)", k, field.Kind()) 544 } 545 } 546 return nil 547 } 548 549 // Completions returns a list of possible completions for the given option prefix. 550 func (config *Configuration) Completions(prefix string) []flags.Completion { 551 ret := []flags.Completion{} 552 t := reflect.TypeOf(config).Elem() 553 for i := 0; i < t.NumField(); i++ { 554 if field := t.Field(i); field.Type.Kind() == reflect.Struct { 555 for j := 0; j < field.Type.NumField(); j++ { 556 subfield := field.Type.Field(j) 557 if name := strings.ToLower(field.Name + "." + subfield.Name); strings.HasPrefix(name, prefix) { 558 help := subfield.Tag.Get("help") 559 if options := subfield.Tag.Get("options"); options != "" { 560 for _, option := range strings.Split(options, ",") { 561 ret = append(ret, flags.Completion{Item: name + ":" + option, Description: help}) 562 } 563 } else { 564 ret = append(ret, flags.Completion{Item: name + ":", Description: help}) 565 } 566 } 567 } 568 } 569 } 570 return ret 571 }