diff --git a/.circleci/config.yml b/.circleci/config.yml index 6b9b39eb2..27068008b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -125,7 +125,7 @@ jobs: working_directory: ~/please macos: xcode: "16.4.0" - resource_class: macos.m1.medium.gen1 + resource_class: m4pro.medium steps: - checkout - attach_workspace: @@ -206,7 +206,7 @@ jobs: build-darwin: macos: xcode: "16.4.0" - resource_class: macos.m1.medium.gen1 + resource_class: m4pro.medium environment: PLZ_ARGS: "--profile ci --exclude pip --exclude embed" steps: diff --git a/.cirrus.yml b/.cirrus.yml index 4129e2efb..e7146207b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,5 +1,5 @@ freebsd_instance: - image_family: freebsd-14-2 + image_family: freebsd-14-3 env: GOPROXY: https://proxy.golang.org diff --git a/ChangeLog b/ChangeLog index e892077bd..f1f233b62 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +Version 17.27.0 +--------------- + * Traverse `data` and `debug_data` for run-time dependencies (#3469) + +Version 17.26.0 +--------------- + * Optionally traverse `srcs` and `deps` for run-time dependencies (#3466) + +Version 17.25.1 +--------------- + * Bump arcat to v1.3.1, fixing a bug relating to symbol table stripping on Darwin and FreeBSD (#3462) + +Version 17.25.0 +--------------- + * Add `maxdepth` parameter to `get_labels` built-in, allowing for recursive dependency searches to be depth-limited (#3460) + +Version 17.24.2 +--------------- + * Improve platform detection logic in `plz init`'s `pleasew` script (#3458) + +Version 17.24.1 +--------------- + * Download run-time dependencies when running a target remotely (#3453) + +Version 17.24.0 +--------------- + * Define `PLZ_ENV` environment variable in build environments (#3444) + * Strip symbols from please_sandbox release binaries (#3450) + * Implement run-time dependencies for binary targets via the `runtime_deps` parameter (#3451) + +Version 17.23.0 +--------------- + * Bump arcat to v1.3.0, adding the `--include` option to the `zip` command (#3441) + Version 17.22.0 --------------- * Add `--audit_log_dir` option for logging of Please invocations, build commands and remote file fetching (#3425) diff --git a/VERSION b/VERSION index 721d4f737..b1799abc7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -17.22.0 +17.27.0 diff --git a/docs/BUILD b/docs/BUILD index 8cf72d2c1..5e1937f50 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -60,7 +60,7 @@ plugins = { "python": "v1.14.0", "java": "v0.4.5", "go": "v1.26.0", - "cc": "v0.5.2", + "cc": "v0.7.1", "shell": "v0.2.0", "go-proto": "v0.3.0", "python-proto": "v0.1.0", diff --git a/docs/codelabs/python_intro.md b/docs/codelabs/python_intro.md index b438e33c2..bb1d1882b 100644 --- a/docs/codelabs/python_intro.md +++ b/docs/codelabs/python_intro.md @@ -349,7 +349,3 @@ determine files changes since master, watch rules and build them automatically a `plz help`, and explore this rich set of commands! Otherwise, why not try one of the other codelabs! -, watch rules and build them automatically as things change and much more! Use -`plz help`, and explore this rich set of commands! - -Otherwise, why not try one of the other codelabs! diff --git a/docs/lexicon.html b/docs/lexicon.html index 49ee4c266..7a2ecf710 100644 --- a/docs/lexicon.html +++ b/docs/lexicon.html @@ -509,6 +509,15 @@

- returns a copy of this string converted to lowercase. +
  • + + matches(pattern) + - returns true if the string matches the regular expression given by pattern. + +
  • diff --git a/go.mod b/go.mod index 126ef7859..98c3c534d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/thought-machine/please -go 1.23.0 +go 1.24.0 require ( cloud.google.com/go/longrunning v0.5.5 @@ -47,11 +47,11 @@ require ( github.com/zeebo/blake3 v0.2.3 go.uber.org/automaxprocs v1.5.3 golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - golang.org/x/net v0.38.0 - golang.org/x/sync v0.12.0 - golang.org/x/sys v0.31.0 - golang.org/x/term v0.30.0 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d + golang.org/x/net v0.47.0 + golang.org/x/sync v0.18.0 + golang.org/x/sys v0.38.0 + golang.org/x/term v0.37.0 + golang.org/x/tools v0.38.0 google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304212257-790db918fca8 google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 google.golang.org/grpc v1.62.1 @@ -103,9 +103,9 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/sdk v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect - golang.org/x/crypto v0.36.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/text v0.23.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/api v0.168.0 // indirect google.golang.org/genproto v0.0.0-20240304212257-790db918fca8 // indirect diff --git a/go.sum b/go.sum index 8e50c9d4f..285e02e64 100644 --- a/go.sum +++ b/go.sum @@ -305,8 +305,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= @@ -334,8 +334,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= @@ -346,8 +346,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -375,21 +375,21 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -405,8 +405,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pleasew b/pleasew index 06c1fadf4..bf21ab79b 100755 --- a/pleasew +++ b/pleasew @@ -17,24 +17,44 @@ else RESET='' fi +OS="" +ARCH="" + +case "$(uname)" in + Linux) + OS=linux + case "$(uname -m)" in + x86_64) ARCH=amd64 ;; + aarch64*|armv8b|armv8l) ARCH=arm64 ;; + esac + ;; + Darwin) + OS=darwin + case "$(uname -m)" in + x86_64) ARCH=amd64 ;; + arm64) ARCH=arm64 ;; + esac + ;; + FreeBSD) + OS=freebsd + case "$(uname -m)" in + amd64) ARCH=amd64 ;; + esac + ;; + *) + printf >&2 '%bPlease does not support the %s operating system.%b\n' \ + "${RED}" "$(uname)" "${RESET}" + exit 1 + ;; +esac -DEFAULT_URL_BASE='https://get.please.build' - -OS="$(uname)" - -if [ "${OS}" = 'Darwin' ]; then - # switch between mac amd64/arm64 - ARCH="$(uname -m)" -else - # default to amd64 on other operating systems - # because we only build intel binaries - ARCH='amd64' +if [ -z "$ARCH" ]; then + printf >&2 '%bPlease does not support the %s architecture on %s.%b\n' \ + "${RED}" "$(uname -m)" "$(uname)" "${RESET}" + exit 1 fi -case "${ARCH}" in - aarch64_be|aarch64|armv8b|armv8l) ARCH='arm64' ;; - x86_64) ARCH='amd64' ;; -esac +DEFAULT_URL_BASE='https://get.please.build' has_command () { command -v "${1}" > /dev/null 2>&1 @@ -54,12 +74,24 @@ get_profile () { # Check `PLZ_CONFIG_PROFILE` or fall back to arguments for a profile. PROFILE="${PLZ_CONFIG_PROFILE:-$(get_profile "${@}")}" +# Find repo root by traversing up until we find .plzconfig +find_repo_root() { + dir="$PWD" + while true; do + [ -f "$dir/.plzconfig" ] && echo "$dir" && return 0 + [ "$dir" = "/" ] && return 0 + dir="$(dirname "$dir")" + done +} + +REPO_ROOT="$(find_repo_root)" + # Config files on order of precedence high to low. CONFIGS="$(cat <<- EOS - .plzconfig.local - ${PROFILE:+.plzconfig.${PROFILE}} - .plzconfig_${OS}_${ARCH} - .plzconfig + ${REPO_ROOT:+${REPO_ROOT}/.plzconfig.local} + ${REPO_ROOT:+${PROFILE:+${REPO_ROOT}/.plzconfig.${PROFILE}}} + ${REPO_ROOT:+${REPO_ROOT}/.plzconfig_${OS}_${ARCH}} + ${REPO_ROOT:+${REPO_ROOT}/.plzconfig} ${HOME}/.config/please/plzconfig /etc/please/plzconfig EOS @@ -141,20 +173,7 @@ if [ "${VERSION:+x}" != 'x' ]; then VERSION=$(${TRANSFER_TOOL} ${TRANSFER_SILENT_OPTS} "${URL_BASE}"/latest_version) fi -# Find the os / arch to download. You can do this quite nicely with go env -# but we use this script on machines that don't necessarily have Go itself. -if [ "${OS}" = 'Linux' ]; then - GOOS='linux' -elif [ "${OS}" = 'Darwin' ]; then - GOOS='darwin' -elif [ "${OS}" = 'FreeBSD' ]; then - GOOS='freebsd' -else - printf >&2 '%bUnknown operating system %s%b\n' "${RED}" "${OS}" "${RESET}" - exit 1 -fi - -PLEASE_URL="${URL_BASE}/${GOOS}_${ARCH}/${VERSION}/please_${VERSION}.tar.xz" +PLEASE_URL="${URL_BASE}/${OS}_${ARCH}/${VERSION}/please_${VERSION}.tar.xz" DIR="${LOCATION}/${VERSION}" # Potentially we could reuse this but it's easier not to really. diff --git a/plugins/BUILD b/plugins/BUILD index 258ba758d..1222d0787 100644 --- a/plugins/BUILD +++ b/plugins/BUILD @@ -7,7 +7,7 @@ plugin_repo( plugin_repo( name = "cc", plugin = "cc-rules", - revision = "v0.5.2", + revision = "v0.7.1", ) plugin_repo( diff --git a/rules/builtins.build_defs b/rules/builtins.build_defs index 5edca910d..1f62089b0 100644 --- a/rules/builtins.build_defs +++ b/rules/builtins.build_defs @@ -2,18 +2,64 @@ # Do not change the order of arguments to this function without updating the iota in targets.go to match it. -def build_rule(name:str, cmd:str|dict='', test_cmd:str|dict='', debug_cmd:str='', srcs:list|dict=None, data:list|dict=None, - debug_data:list|dict=None, outs:list|dict=None, deps:list=None, exported_deps:list=None, secrets:list|dict=None, - tools:str|list|dict=None, test_tools:str|list|dict=None, debug_tools:str|list|dict=None, labels:list=None, - visibility:list=CONFIG.DEFAULT_VISIBILITY, hashes:list=None, binary:bool=False, test:bool=False, - test_only:bool=CONFIG.DEFAULT_TESTONLY, building_description:str=None, needs_transitive_deps:bool=False, - output_is_complete:bool=False, sandbox:bool=CONFIG.BUILD_SANDBOX, test_sandbox:bool=CONFIG.TEST_SANDBOX, - no_test_output:bool=False, flaky:bool|int=0, build_timeout:int|str=0, test_timeout:int|str=0, pre_build:function=None, - post_build:function=None, requires:list=None, provides:dict=None, licences:list=CONFIG.DEFAULT_LICENCES, - test_outputs:list=None, system_srcs:list=None, stamp:bool=False, tag:str='', optional_outs:list=None, progress:bool=False, - size:str=None, _urls:list=None, internal_deps:list=None, pass_env:list=None, local:bool=False, output_dirs:list=[], - exit_on_error:bool=CONFIG.EXIT_ON_ERROR, entry_points:dict={}, env:dict={}, _file_content:str=None, - _subrepo:bool=False, no_test_coverage:bool=False, src_list_files:bool=False): +def build_rule( + name:str, + cmd:str|dict="", + test_cmd:str|dict="", + debug_cmd:str="", + srcs:list|dict=None, + data:list|dict=None, + debug_data:list|dict=None, + outs:list|dict=None, + deps:list=None, + exported_deps:list=None, + runtime_deps:list=None, + runtime_deps_from_srcs:bool=False, + runtime_deps_from_deps:bool=False, + secrets:list|dict=None, + tools:str|list|dict=None, + test_tools:str|list|dict=None, + debug_tools:str|list|dict=None, + labels:list=None, + visibility:list=CONFIG.DEFAULT_VISIBILITY, + hashes:list=None, + binary:bool=False, + test:bool=False, + test_only:bool=CONFIG.DEFAULT_TESTONLY, + building_description:str=None, + needs_transitive_deps:bool=False, + output_is_complete:bool=False, + sandbox:bool=CONFIG.BUILD_SANDBOX, + test_sandbox:bool=CONFIG.TEST_SANDBOX, + no_test_output:bool=False, + flaky:bool|int=0, + build_timeout:int|str=0, + test_timeout:int|str=0, + pre_build:function=None, + post_build:function=None, + requires:list=None, + provides:dict=None, + licences:list=CONFIG.DEFAULT_LICENCES, + test_outputs:list=None, + system_srcs:list=None, + stamp:bool=False, + tag:str="", + optional_outs:list=None, + progress:bool=False, + size:str=None, + _urls:list=None, + internal_deps:list=None, + pass_env:list=None, + local:bool=False, + output_dirs:list=[], + exit_on_error:bool=CONFIG.EXIT_ON_ERROR, + entry_points:dict={}, + env:dict={}, + _file_content:str=None, + _subrepo:bool=False, + no_test_coverage:bool=False, + src_list_files:bool=False, +): pass def chr(i:int) -> str: @@ -63,6 +109,8 @@ def upper(self:str) -> str: pass def lower(self:str) -> str: pass +def matches(self:str, pattern: str) -> bool: + pass def fail(msg:str): pass @@ -243,16 +291,18 @@ def dirname(p:str) -> str: # Post-build callback functions. -def get_labels(name:str, prefix:str, all:bool=False, transitive=True) -> list: +def get_labels(name:str, prefix:str, all:bool=False, transitive:bool=None, maxdepth:int=-1) -> list: pass def has_label(name:str, prefix:str, all:bool=False) -> bool: return len(get_labels(name, prefix, all)) > 0 def add_label(name:str, label:str): pass -def add_dep(target:str, dep:str, exported:bool=False): +def add_dep(target:str, dep:str, exported:bool=False, runtime:bool=False): pass def add_exported_dep(target:str, dep:str): - add_dep(target, dep, True) + add_dep(target, dep, exported=True) +def add_runtime_dep(target:str, dep:str): + add_dep(target, dep, runtime=True) def add_data(target:str, datum:str|list|dict): pass def add_out(target:str, name:str, out:str=''): diff --git a/rules/misc_rules.build_defs b/rules/misc_rules.build_defs index 3d9a260eb..0aaf7e751 100644 --- a/rules/misc_rules.build_defs +++ b/rules/misc_rules.build_defs @@ -1,14 +1,42 @@ """Miscellaneous rules that aren't language-specific.""" -def genrule(name:str, cmd:str|list|dict, srcs:list|dict=None, out:str=None, outs:list|dict=None, deps:list=None, - exported_deps:list=None, labels:list&features&tags=None, visibility:list=None, - building_description:str='Building...', data:list|dict=None, hashes:list=None, timeout:int=0, binary:bool=False, - sandbox:bool=None, needs_transitive_deps:bool=False, output_is_complete:bool=True, - test_only:bool&testonly=False, secrets:list|dict=None, requires:list=None, provides:dict=None, - pre_build:function=None, post_build:function=None, tools:str|list|dict=None, pass_env:list=None, - local:bool=False, output_dirs:list=[], exit_on_error:bool=CONFIG.EXIT_ON_ERROR, entry_points:dict={}, - env:dict={}, optional_outs:list=[]): +def genrule( + name:str, + cmd:str|list|dict, + srcs:list|dict=None, + out:str=None, + outs:list|dict=None, + deps:list=None, + exported_deps:list=None, + runtime_deps:list=None, + runtime_deps_from_srcs:bool=False, + runtime_deps_from_deps:bool=False, + labels:list&features&tags=None, + visibility:list=None, + building_description:str="Building...", + data:list|dict=None, + hashes:list=None, + timeout:int=0, + binary:bool=False, + sandbox:bool=None, + needs_transitive_deps:bool=False, + output_is_complete:bool=True, + test_only:bool&testonly=False, + secrets:list|dict=None, + requires:list=None, + provides:dict=None, + pre_build:function=None, + post_build:function=None, + tools:str|list|dict=None, + pass_env:list=None, + local:bool=False, + output_dirs:list=[], + exit_on_error:bool=CONFIG.EXIT_ON_ERROR, + entry_points:dict={}, + env:dict={}, + optional_outs:list=[], +): """A general build rule which allows the user to specify a command. Args: @@ -34,6 +62,21 @@ def genrule(name:str, cmd:str|list|dict, srcs:list|dict=None, out:str=None, outs names to lists, with similar semantics to those of srcs. deps (list): Dependencies of this rule. exported_deps (list): Dependencies that will become visible to any rules that depend on this rule. + runtime_deps (list): Run-time dependencies of this rule. If this rule is run (i.e. with 'plz run'), + rules in this list, as well as those rules' transitive run-time dependencies, + are guaranteed to be built before this rule runs. If this rule is declared as + a dependency of another rule, the outputs of rules in this list, as well as + the outputs of those rules' transitive run-time dependencies, will exist in + the dependent rule's build environment. Requires the rule to produce a runnable + output (i.e. binary = True). + runtime_deps_from_srcs (bool): If true, additionally collect run-time dependencies from this target's + sources. This is useful if the target's output simply collects its + sources in some way without eliminating their own run-time dependencies. + runtime_deps_from_deps (bool): If true, additionally collect run-time dependencies from this target's + build-time dependencies (and those targets' exported dependencies). + This is useful if the target's output includes its dependencies without + eliminating their own run-time dependencies, e.g. for targets generated + by the shell-rules plugin's sh_binary rule. tools (str | list | dict): Tools used to build this rule; similar to srcs but are not copied to the temporary build directory. Should be accessed via $(exe //path/to:tool) or similar. @@ -119,6 +162,9 @@ def genrule(name:str, cmd:str|list|dict, srcs:list|dict=None, out:str=None, outs cmd = ' && '.join(cmd) if isinstance(cmd, list) else cmd, deps = deps, exported_deps = exported_deps, + runtime_deps = runtime_deps, + runtime_deps_from_srcs = runtime_deps_from_srcs, + runtime_deps_from_deps = runtime_deps_from_deps, data = data, tools = tools, secrets = secrets, @@ -146,12 +192,38 @@ def genrule(name:str, cmd:str|list|dict, srcs:list|dict=None, out:str=None, outs ) -def gentest(name:str, test_cmd:str|list|dict, labels:list&features&tags=None, cmd:str|list|dict=None, srcs:list|dict=None, - outs:list=None, deps:list=None, exported_deps:list=None, tools:str|list|dict=None, test_tools:str|list|dict=None, - data:list|dict=None, visibility:list=None, timeout:int=0, needs_transitive_deps:bool=False, - flaky:bool|int=0, secrets:list|dict=None, no_test_output:bool=False, test_outputs:list=None, - output_is_complete:bool=True, requires:list=None, sandbox:bool=None, size:str=None, local:bool=False, - pass_env:list=None, env:dict=None, exit_on_error:bool=CONFIG.EXIT_ON_ERROR, no_test_coverage:bool=False): +def gentest( + name:str, + test_cmd:str|list|dict, + labels:list&features&tags=None, + cmd:str|list|dict=None, + srcs:list|dict=None, + outs:list=None, + deps:list=None, + exported_deps:list=None, + runtime_deps:list=None, + runtime_deps_from_srcs:bool=False, + runtime_deps_from_deps:bool=False, + tools:str|list|dict=None, + test_tools:str|list|dict=None, + data:list|dict=None, + visibility:list=None, + timeout:int=0, + needs_transitive_deps:bool=False, + flaky:bool|int=0, + secrets:list|dict=None, + no_test_output:bool=False, + test_outputs:list=None, + output_is_complete:bool=True, + requires:list=None, + sandbox:bool=None, + size:str=None, + local:bool=False, + pass_env:list=None, + env:dict=None, + exit_on_error:bool=CONFIG.EXIT_ON_ERROR, + no_test_coverage:bool=False, +): """A rule which creates a test with an arbitrary command. The command must return zero on success and nonzero on failure. Test results are written @@ -172,6 +244,18 @@ def gentest(name:str, test_cmd:str|list|dict, labels:list&features&tags=None, cm outs (list): Output files of this rule. deps (list): Dependencies of this rule. exported_deps (list): Dependencies that will become visible to any rules that depend on this rule. + runtime_deps (list): Run-time dependencies of this rule. When the test command runs, rules in this + list, as well as those rules' transitive run-time dependencies, will exist in + the test environment. Requires the rule to produce a runnable output (i.e. + binary = True). + runtime_deps_from_srcs (bool): If true, additionally collect run-time dependencies from this target's + sources. This is useful if the target's output simply collects its + sources in some way without eliminating their own run-time dependencies. + runtime_deps_from_deps (bool): If true, additionally collect run-time dependencies from this target's + build-time dependencies (and those targets' exported dependencies). + This is useful if the target's output includes its dependencies without + eliminating their own run-time dependencies, e.g. for targets generated + by the shell-rules plugin's sh_binary rule. tools (str | list | dict): Tools used to build this rule; similar to srcs but are not copied to the temporary build directory. test_tools (str | list | dict): Like tools but available to test_cmd instead. @@ -210,6 +294,9 @@ def gentest(name:str, test_cmd:str|list|dict, labels:list&features&tags=None, cm outs = outs, deps = deps, exported_deps = exported_deps, + runtime_deps = runtime_deps, + runtime_deps_from_srcs = runtime_deps_from_srcs, + runtime_deps_from_deps = runtime_deps_from_deps, data = data, tools = tools, test_tools = test_tools, @@ -373,7 +460,7 @@ def system_library(name:str, srcs:list, deps:list=None, hashes:list=None, def remote_file(name:str, url:str|list, hashes:list=None, out:str=None, binary:bool=False, visibility:list=None, licences:list=None, test_only:bool&testonly=False, - labels:list=[], deps:list=None, exported_deps:list=None, + labels:list=[], deps:list=None, exported_deps:list=None, runtime_deps:list=None, extract:bool=False, strip_prefix:str='', _tag:str='',exported_files=[], entry_points:dict={}, username:str=None, password_file:str=None, headers:dict={}, secret_headers:dict={}, pass_env:list=[]): @@ -392,6 +479,8 @@ def remote_file(name:str, url:str|list, hashes:list=None, out:str=None, binary:b labels (list): Labels to apply to this rule. deps (list): List of extra dependencies for this rule. exported_deps (list): Dependencies that will become visible to any rules that depend on this rule. + runtime_deps (list): Run-time dependencies of this rule. Requires the rule to produce a runnable + output (i.e. binary = True). extract (bool): Extracts the contents of the downloaded file. It must be either zip or tar format. strip_prefix (str): When extracting, strip this prefix from the extracted files. @@ -454,6 +543,7 @@ def remote_file(name:str, url:str|list, hashes:list=None, out:str=None, binary:b building_description = 'Extracting...', deps = deps, exported_deps = exported_deps, + runtime_deps = runtime_deps, entry_points = entry_points, ) @@ -484,6 +574,7 @@ def remote_file(name:str, url:str|list, hashes:list=None, out:str=None, binary:b building_description = 'Fetching...', deps = deps, exported_deps = exported_deps, + runtime_deps = runtime_deps, test_only = test_only, labels = labels, sandbox = False, diff --git a/src/build/incrementality_test.go b/src/build/incrementality_test.go index a2ce75cdf..02da05869 100644 --- a/src/build/incrementality_test.go +++ b/src/build/incrementality_test.go @@ -81,9 +81,12 @@ var KnownFields = map[string]bool{ "Debug.namedTools": true, // These only contribute to the runtime hash, not at build time. - "Data": true, - "NamedData": true, - "ContainerSettings": true, + "runtimeDependencies": true, + "RuntimeDependenciesFromSources": true, + "RuntimeDependenciesFromDependencies": true, + "Data": true, + "NamedData": true, + "ContainerSettings": true, // These would ideally not contribute to the hash, but we need that at present // because we don't have a good way to force a recheck of its reverse dependencies. diff --git a/src/core/build_env.go b/src/core/build_env.go index 732b71b9f..b3c0bcfbb 100644 --- a/src/core/build_env.go +++ b/src/core/build_env.go @@ -22,6 +22,7 @@ type BuildEnv map[string]string // on any specific target etc. func GeneralBuildEnvironment(state *BuildState) BuildEnv { env := BuildEnv{ + "PLZ_ENV": "1", // Need this for certain tools, for example sass "LANG": state.Config.Build.Lang, // Need to know these for certain rules. diff --git a/src/core/build_env_test.go b/src/core/build_env_test.go index 0375e4483..ca2b22210 100644 --- a/src/core/build_env_test.go +++ b/src/core/build_env_test.go @@ -65,6 +65,7 @@ func TestExecEnvironment(t *testing.T) { env := ExecEnvironment(NewDefaultBuildState(), target, "/path/to/runtime/dir") + assert.Equal(t, env["PLZ_ENV"], "1") assert.Equal(t, env["DATA"], "pkg/data_file1") assert.Equal(t, env["TMP_DIR"], "/path/to/runtime/dir") assert.Equal(t, env["TMPDIR"], "/path/to/runtime/dir") @@ -104,6 +105,7 @@ func TestExecEnvironmentTestTarget(t *testing.T) { env := ExecEnvironment(state, testTarget, "/path/to/runtime/dir") + assert.Equal(t, env["PLZ_ENV"], "1") assert.Equal(t, env["DATA"], "pkg/data_file1 pkg/data_file2") assert.Equal(t, env["DATA_FILE2"], "pkg/data_file2") assert.Equal(t, env["TOOLS"], "plz-out/bin/tool1 plz-out/bin/tool2") @@ -138,6 +140,7 @@ func TestExecEnvironmentDebugTarget(t *testing.T) { env := ExecEnvironment(state, target, "/path/to/runtime/dir") + assert.Equal(t, env["PLZ_ENV"], "1") assert.Equal(t, env["DEBUG_DATA"], "pkg/data_file1") assert.Equal(t, env["DEBUG_TOOLS"], "plz-out/bin/tool1") assert.Equal(t, env["DEBUG_TOOLS_TOOL1"], "plz-out/bin/tool1") @@ -173,6 +176,7 @@ func TestExecEnvironmentDebugTestTarget(t *testing.T) { env := ExecEnvironment(state, testTarget, "/path/to/runtime/dir") + assert.Equal(t, env["PLZ_ENV"], "1") assert.Equal(t, env["DEBUG_DATA"], "pkg/data_file1") assert.Equal(t, env["DEBUG_TOOLS"], "plz-out/bin/tool1") assert.Equal(t, env["DEBUG_TOOLS_TOOL1"], "plz-out/bin/tool1") diff --git a/src/core/build_target.go b/src/core/build_target.go index 18dc8ed2f..d15a827b5 100644 --- a/src/core/build_target.go +++ b/src/core/build_target.go @@ -115,6 +115,15 @@ type BuildTarget struct { // Maps the original declaration to whatever dependencies actually got attached, // which may be more than one in some cases. Also contains info about exporting etc. dependencies []depInfo `name:"deps"` + // The run-time dependencies of this target. + runtimeDependencies []BuildLabel `name:"runtime_deps"` + // Whether to consider the run-time dependencies of this target's sources to be additional + // run-time dependencies of this target. + RuntimeDependenciesFromSources bool `name:"runtime_deps_from_srcs"` + // Whether to consider the run-time dependencies of this target's build-time dependencies + // (and exported dependencies of those targets) to be additional run-time dependencies of + // this target. + RuntimeDependenciesFromDependencies bool `name:"runtime_deps_from_deps"` // List of build target patterns that can use this build target. Visibility []BuildLabel // Source files of this rule. Can refer to build rules themselves. @@ -307,6 +316,7 @@ type depInfo struct { resolved bool // has the graph resolved it exported bool // is it an exported dependency internal bool // is it an internal dependency (that is not picked up implicitly by transitive searches) + runtime bool // is it a run-time (and therefore implicitly transitive) dependency source bool // is it implicit because it's a source (not true if it's a dependency too) data bool // is it a data item for a test } @@ -632,7 +642,7 @@ func (target *BuildTarget) DeclaredDependenciesStrict() []BuildLabel { defer target.mutex.RUnlock() ret := make(BuildLabels, 0, len(target.dependencies)) for _, dep := range target.dependencies { - if !dep.exported && !dep.source && !target.IsTool(*dep.declared) { + if !dep.runtime && !dep.exported && !dep.source && !target.IsTool(*dep.declared) { ret = append(ret, *dep.declared) } } @@ -672,13 +682,13 @@ func (target *BuildTarget) ExternalDependencies() []*BuildTarget { return ret } -// BuildDependencies returns the build-time dependencies of this target (i.e. not data, internal nor source). +// BuildDependencies returns the build-time dependencies of this target (i.e. not run-time dependencies, data, internal nor source). func (target *BuildTarget) BuildDependencies() []*BuildTarget { target.mutex.RLock() defer target.mutex.RUnlock() ret := make(BuildTargets, 0, len(target.dependencies)) for _, deps := range target.dependencies { - if !deps.data && !deps.internal && !deps.source { + if !deps.runtime && !deps.data && !deps.internal && !deps.source { for _, dep := range deps.deps { ret = append(ret, dep) } @@ -701,6 +711,110 @@ func (target *BuildTarget) ExportedDependencies() []BuildLabel { return ret } +// RuntimeDependencies returns any run-time dependencies of this target. +// +// Although run-time dependencies are transitive, RuntimeDependencies only returns this target's direct run-time +// dependencies. Use IterAllRuntimeDependencies to iterate over the target's run-time dependencies transitively. +func (target *BuildTarget) RuntimeDependencies() []BuildLabel { + target.mutex.RLock() + defer target.mutex.RUnlock() + ret := make(BuildLabels, 0, len(target.dependencies)) + for _, deps := range target.dependencies { + if deps.runtime { + ret = append(ret, *deps.declared) + } + } + return ret +} + +// IterAllRuntimeDependencies returns an iterator over the transitive run-time dependencies of this target. +// Require/provide relationships between pairs of targets are resolved as they are with build-time dependencies. +func (target *BuildTarget) IterAllRuntimeDependencies(graph *BuildGraph) iter.Seq[BuildLabel] { + var ( + push func(*BuildTarget, func(BuildLabel) bool) bool + done = make(map[string]bool) + ) + push = func(t *BuildTarget, yield func(BuildLabel) bool) bool { + if done[t.String()] { + return true + } + done[t.String()] = true + for _, dep := range t.runtimeDependencies { + depLabel, _ := dep.Label() + for _, providedDep := range graph.TargetOrDie(depLabel).ProvideFor(t) { + if !yield(providedDep) { + return false + } + if !push(graph.TargetOrDie(providedDep), yield) { + return false + } + } + } + // Include the run-time dependencies of data targets, but not the data targets themselves. (We needn't worry + // about data files here - they can't have run-time dependencies of their own.) + for _, data := range t.AllData() { + dataLabel, ok := data.Label() + if !ok { + continue + } + for _, providedDep := range graph.TargetOrDie(dataLabel).ProvideFor(t) { + if !push(graph.TargetOrDie(providedDep), yield) { + return false + } + } + } + if t.Debug != nil { + for _, data := range t.AllDebugData() { + dataLabel, ok := data.Label() + if !ok { + continue + } + for _, providedDep := range graph.TargetOrDie(dataLabel).ProvideFor(t) { + if !push(graph.TargetOrDie(providedDep), yield) { + return false + } + } + } + } + if t.RuntimeDependenciesFromSources || t.RuntimeDependenciesFromDependencies { + for _, dep := range t.dependencies { + // If required, include the run-time dependencies of sources, but not the sources themselves. + if t.RuntimeDependenciesFromSources && dep.source { + depLabel, _ := dep.declared.Label() + for _, providedDep := range graph.TargetOrDie(depLabel).ProvideFor(t) { + if !push(graph.TargetOrDie(providedDep), yield) { + return false + } + } + } + // If required, include the run-time dependencies of dependencies, but not the dependencies themselves. + if t.RuntimeDependenciesFromDependencies && !dep.exported && !dep.source && !dep.internal && !dep.runtime { + depLabel, _ := dep.declared.Label() + depTarget := graph.TargetOrDie(depLabel) + for _, providedDep := range depTarget.ProvideFor(t) { + if !push(graph.TargetOrDie(providedDep), yield) { + return false + } + } + // Also include the run-time dependencies of the target's exported dependencies, but not the + // exported dependencies themselves. + for _, exportedDep := range depTarget.ExportedDependencies() { + for _, providedDep := range graph.TargetOrDie(exportedDep).ProvideFor(t) { + if !push(graph.TargetOrDie(providedDep), yield) { + return false + } + } + } + } + } + } + return true + } + return func(yield func(BuildLabel) bool) { + push(target, yield) + } +} + // DependenciesFor returns the dependencies that relate to a given label. func (target *BuildTarget) DependenciesFor(label BuildLabel) []*BuildTarget { target.mutex.RLock() @@ -1287,7 +1401,7 @@ func (target *BuildTarget) addSource(sources []BuildInput, source BuildInput) [] } // Add a dependency if this is not just a file. if label, ok := source.Label(); ok { - target.AddMaybeExportedDependency(label, false, true, false) + target.AddMaybeExportedDependency(label, false, true, false, false) } return append(sources, source) } @@ -1653,7 +1767,7 @@ func (target *BuildTarget) AllNamedTools() map[string][]BuildInput { // AddDependency adds a dependency to this target. It deduplicates against any existing deps. func (target *BuildTarget) AddDependency(dep BuildLabel) { - target.AddMaybeExportedDependency(dep, false, false, false) + target.AddMaybeExportedDependency(dep, false, false, false, false) } // HintDependencies allocates space for at least the given number of dependencies without reallocating. @@ -1662,17 +1776,30 @@ func (target *BuildTarget) HintDependencies(n int) { } // AddMaybeExportedDependency adds a dependency to this target which may be exported. It deduplicates against any existing deps. -func (target *BuildTarget) AddMaybeExportedDependency(dep BuildLabel, exported, source, internal bool) { +func (target *BuildTarget) AddMaybeExportedDependency(dep BuildLabel, exported, source, internal, runtime bool) { if dep == target.Label { log.Fatalf("Attempted to add %s as a dependency of itself.\n", dep) } + if runtime { + if !target.IsBinary { + log.Fatalf("%s: output must be marked as binary to have run-time dependencies", target.String()) + } + target.runtimeDependencies = append(target.runtimeDependencies, dep) + } info := target.dependencyInfo(dep) if info == nil { - target.dependencies = append(target.dependencies, depInfo{declared: &dep, exported: exported, source: source, internal: internal}) + target.dependencies = append(target.dependencies, depInfo{ + declared: &dep, + exported: exported, + source: source, + internal: internal, + runtime: runtime, + }) } else { info.exported = info.exported || exported info.source = info.source && source info.internal = info.internal && internal + info.runtime = info.runtime && runtime info.data = false // It's not *only* data any more. } } diff --git a/src/core/build_target_test.go b/src/core/build_target_test.go index d9a6cea38..4360be131 100644 --- a/src/core/build_target_test.go +++ b/src/core/build_target_test.go @@ -4,6 +4,7 @@ package core import ( "fmt" "os" + "slices" "testing" "github.com/stretchr/testify/assert" @@ -263,7 +264,7 @@ func TestAddDatum(t *testing.T) { assert.Equal(t, target1.Data, []BuildInput{target2.Label}) assert.True(t, target1.dependencies[0].data) // Now we add it as a dependency too, which unsets the data label - target1.AddMaybeExportedDependency(target2.Label, false, false, false) + target1.AddMaybeExportedDependency(target2.Label, false, false, false, false) assert.False(t, target1.dependencies[0].data) } @@ -427,20 +428,64 @@ func TestBuildDependencies(t *testing.T) { target1 := makeTarget1("//src/core:target1", "") target2 := makeTarget1("//src/core:target2", "", target1) target3 := makeTarget1("//src/core:target3", "", target2) + target4 := makeTarget1("//src/core:target4", "") + target5 := makeTarget1("//src/core:target5", "") target3.AddDatum(target1.Label) + // BuildDependencies shouldn't return run-time dependencies: + target5.IsBinary = true + target5.AddMaybeExportedDependency(target4.Label, false, false, false, true) // runtime assert.Equal(t, []*BuildTarget{}, target1.BuildDependencies()) assert.Equal(t, []*BuildTarget{target1}, target2.BuildDependencies()) assert.Equal(t, []*BuildTarget{target2}, target3.BuildDependencies()) + assert.Equal(t, []*BuildTarget{}, target5.BuildDependencies()) } func TestDeclaredDependenciesStrict(t *testing.T) { target1 := makeTarget1("//src/core:target1", "") target2 := makeTarget1("//src/core:target2", "", target1) target3 := makeTarget1("//src/core:target3", "", target2) - target3.AddMaybeExportedDependency(target1.Label, true, false, false) + target4 := makeTarget1("//src/core:target4", "") + target5 := makeTarget1("//src/core:target5", "") + target3.AddMaybeExportedDependency(target1.Label, true, false, false, false) + // DeclaredDependenciesStrict shouldn't return run-time dependencies: + target5.IsBinary = true + target5.AddMaybeExportedDependency(target4.Label, false, false, false, true) // runtime assert.Equal(t, []BuildLabel{}, target1.DeclaredDependenciesStrict()) assert.Equal(t, []BuildLabel{target1.Label}, target2.DeclaredDependenciesStrict()) assert.Equal(t, []BuildLabel{target2.Label}, target3.DeclaredDependenciesStrict()) + assert.Equal(t, []*BuildTarget{}, target5.BuildDependencies()) +} + +func TestRuntimeDependencies(t *testing.T) { + target1 := makeTarget1("//src/core:target1", "") + target2 := makeTarget1("//src/core:target2", "") + target3 := makeTarget1("//src/core:target3", "") + target2.IsBinary = true + target2.AddMaybeExportedDependency(target1.Label, false, false, false, true) // runtime + target3.IsBinary = true + target3.AddMaybeExportedDependency(target2.Label, false, false, false, true) // runtime + // RuntimeDependencies shouldn't return transitive run-time dependencies. + assert.Equal(t, []BuildLabel{}, target1.RuntimeDependencies()) + assert.Equal(t, []BuildLabel{target1.Label}, target2.RuntimeDependencies()) + assert.Equal(t, []BuildLabel{target2.Label}, target3.RuntimeDependencies()) +} + +func TestIterAllRuntimeDependencies(t *testing.T) { + target1 := makeTarget1("//src/core:target1", "") + target2 := makeTarget1("//src/core:target2", "") + target3 := makeTarget1("//src/core:target3", "") + target2.IsBinary = true + target2.AddMaybeExportedDependency(target1.Label, false, false, false, true) // runtime + target3.IsBinary = true + target3.AddMaybeExportedDependency(target2.Label, false, false, false, true) // runtime + graph := NewGraph() + graph.AddTarget(target1) + graph.AddTarget(target2) + graph.AddTarget(target3) + // IterAllRuntimeDependencies should yield transitive run-time dependencies. + assert.Nil(t, slices.Collect(target1.IterAllRuntimeDependencies(graph))) + assert.ElementsMatch(t, []BuildLabel{target1.Label}, slices.Collect(target2.IterAllRuntimeDependencies(graph))) + assert.ElementsMatch(t, []BuildLabel{target1.Label, target2.Label}, slices.Collect(target3.IterAllRuntimeDependencies(graph))) } func TestAddDependency(t *testing.T) { @@ -451,7 +496,7 @@ func TestAddDependency(t *testing.T) { target2.AddDependency(target1.Label) assert.Equal(t, []BuildLabel{target1.Label}, target2.DeclaredDependencies()) assert.Equal(t, []BuildLabel{}, target2.ExportedDependencies()) - target2.AddMaybeExportedDependency(target1.Label, true, false, false) + target2.AddMaybeExportedDependency(target1.Label, true, false, false, false) assert.Equal(t, []BuildLabel{target1.Label}, target2.DeclaredDependencies()) assert.Equal(t, []BuildLabel{target1.Label}, target2.ExportedDependencies()) assert.Equal(t, []*BuildTarget{}, target2.Dependencies()) @@ -459,13 +504,25 @@ func TestAddDependency(t *testing.T) { assert.Equal(t, []*BuildTarget{target1}, target2.Dependencies()) } +func TestAddRuntimeDependency(t *testing.T) { + target1 := makeTarget1("//src/core:target1", "PUBLIC") + target2 := makeTarget1("//src/core:target2", "PUBLIC") + target1.IsBinary = true + target1.AddMaybeExportedDependency(target2.Label, false, false, false, true) // runtime + assert.Equal(t, target1.runtimeDependencies, []BuildLabel{target2.Label}) + assert.True(t, target1.dependencies[0].runtime) + // Now we add it as a build-time dependency too, which should unset the runtime flag. + target1.AddMaybeExportedDependency(target2.Label, false, false, false, false) + assert.False(t, target1.dependencies[0].runtime) +} + func TestAddDependencySource(t *testing.T) { target1 := makeTarget1("//src/core:target1", "") target2 := makeTarget1("//src/core:target2", "") - target2.AddMaybeExportedDependency(target1.Label, true, true, false) + target2.AddMaybeExportedDependency(target1.Label, true, true, false, false) assert.True(t, target2.IsSourceOnlyDep(target1.Label)) // N.B. It's important that calling this again cancels the source flag. - target2.AddMaybeExportedDependency(target1.Label, true, false, false) + target2.AddMaybeExportedDependency(target1.Label, true, false, false, false) assert.False(t, target2.IsSourceOnlyDep(target1.Label)) } diff --git a/src/core/state.go b/src/core/state.go index 3596885b5..18a03d70a 100644 --- a/src/core/state.go +++ b/src/core/state.go @@ -1353,18 +1353,44 @@ func (state *BuildState) IterInputs(target *BuildTarget, test bool) iter.Seq[Bui return IterInputs(state, state.Graph, target, true, target.IsFilegroup) } return func(yield func(BuildInput) bool) { + // The target itself, plus its transitive run-time dependencies (since we're about to run the target): if !yield(target.Label) { return } + for runDep := range target.IterAllRuntimeDependencies(state.Graph) { + if !yield(runDep) { + return + } + } + // The target's data, plus the transitive run-time dependencies for data that are also targets: for _, datum := range target.AllData() { if !yield(datum) { return } + label, ok := datum.Label() + if !ok { + continue + } + for runDep := range state.Graph.TargetOrDie(label).IterAllRuntimeDependencies(state.Graph) { + if !yield(runDep) { + return + } + } } + // The target's test tools, plus the transitive run-time dependencies for test tools that are also targets: for _, tool := range target.AllTestTools() { if !yield(tool) { return } + label, ok := tool.Label() + if !ok { + continue + } + for runDep := range state.Graph.TargetOrDie(label).IterAllRuntimeDependencies(state.Graph) { + if !yield(runDep) { + return + } + } } } } diff --git a/src/core/utils.go b/src/core/utils.go index abc7998cf..3c63f5351 100644 --- a/src/core/utils.go +++ b/src/core/utils.go @@ -146,6 +146,11 @@ func IterInputs(state *BuildState, graph *BuildGraph, target *BuildTarget, inclu if !yield(p) { return false } + for runDep := range graph.TargetOrDie(p).IterAllRuntimeDependencies(graph) { + if !yield(runDep) { + return false + } + } } return true } @@ -157,6 +162,11 @@ func IterInputs(state *BuildState, graph *BuildGraph, target *BuildTarget, inclu if !yield(dependency.Label) { return false } + for runDep := range graph.TargetOrDie(dependency.Label).IterAllRuntimeDependencies(graph) { + if !yield(runDep) { + return false + } + } } done[dependency.Label] = true @@ -257,6 +267,15 @@ func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool, } } + for runDep := range target.IterAllRuntimeDependencies(graph) { + fullPaths := runDep.FullPaths(graph) + for i, depPath := range runDep.Paths(graph) { + if !pushOut(fullPaths[i], depPath) { + return + } + } + } + for _, data := range target.AllData() { fullPaths := data.FullPaths(graph) for i, dataPath := range data.Paths(graph) { @@ -264,6 +283,18 @@ func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool, return } } + label, ok := data.Label() + if !ok { + continue + } + for runDep := range graph.TargetOrDie(label).IterAllRuntimeDependencies(graph) { + fullPaths := runDep.FullPaths(graph) + for i, depPath := range runDep.Paths(graph) { + if !pushOut(fullPaths[i], depPath) { + return + } + } + } } if target.Test != nil { @@ -274,6 +305,18 @@ func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool, return } } + label, ok := tool.Label() + if !ok { + continue + } + for runDep := range graph.TargetOrDie(label).IterAllRuntimeDependencies(graph) { + fullPaths := runDep.FullPaths(graph) + for i, depPath := range runDep.Paths(graph) { + if !pushOut(fullPaths[i], depPath) { + return + } + } + } } } @@ -285,6 +328,18 @@ func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool, return } } + label, ok := data.Label() + if !ok { + continue + } + for runDep := range graph.TargetOrDie(label).IterAllRuntimeDependencies(graph) { + fullPaths := runDep.FullPaths(graph) + for i, depPath := range runDep.Paths(graph) { + if !pushOut(fullPaths[i], depPath) { + return + } + } + } } for _, tool := range target.AllDebugTools() { fullPaths := tool.FullPaths(graph) @@ -293,6 +348,18 @@ func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool, return } } + label, ok := tool.Label() + if !ok { + continue + } + for runDep := range graph.TargetOrDie(label).IterAllRuntimeDependencies(graph) { + fullPaths := runDep.FullPaths(graph) + for i, depPath := range runDep.Paths(graph) { + if !pushOut(fullPaths[i], depPath) { + return + } + } + } } } } diff --git a/src/export/export.go b/src/export/export.go index 905be937a..2863dfef0 100644 --- a/src/export/export.go +++ b/src/export/export.go @@ -37,6 +37,10 @@ func ToDir(state *core.BuildState, dir string, noTrim bool, targets []core.Build exportedTargets: map[core.BuildLabel]bool{}, } + if err := os.MkdirAll(dir, fs.DirPermissions); err != nil { + log.Fatalf("failed to create export directory %s: %v", dir, err) + } + e.exportPlzConf() for _, target := range state.Config.Parse.PreloadSubincludes { for _, includeLabel := range append(state.Graph.TransitiveSubincludes(target), target) { diff --git a/src/parse/asp/builtins.go b/src/parse/asp/builtins.go index c34479f30..353478b07 100644 --- a/src/parse/asp/builtins.go +++ b/src/parse/asp/builtins.go @@ -7,6 +7,7 @@ import ( "io" "path/filepath" "reflect" + "regexp" "slices" "sort" "strconv" @@ -47,8 +48,8 @@ func registerBuiltins(s *scope) { setNativeCode(s, "zip", zip, varargs) setNativeCode(s, "any", anyFunc) setNativeCode(s, "all", allFunc) - setNativeCode(s, "min", min) - setNativeCode(s, "max", max) + setNativeCode(s, "min", minFunc) + setNativeCode(s, "max", maxFunc) setNativeCode(s, "chr", chr) setNativeCode(s, "ord", ord) setNativeCode(s, "len", lenFunc) @@ -98,6 +99,7 @@ func registerBuiltins(s *scope) { "count": setNativeCode(s, "count", strCount), "upper": setNativeCode(s, "upper", strUpper), "lower": setNativeCode(s, "lower", strLower), + "matches": setNativeCode(s, "matches", strMatches), } s.interpreter.stringMethods["format"].kwargs = true s.interpreter.dictMethods = map[string]*pyFunc{ @@ -645,6 +647,20 @@ func strLower(s *scope, args []pyObject) pyObject { return pyString(strings.ToLower(self)) } +func strMatches(s *scope, args []pyObject) pyObject { + self := string(args[0].(pyString)) + pattern := string(args[1].(pyString)) + compiledRegex := s.interpreter.regexCache.Get(pattern) + if compiledRegex == nil { + compiled, err := regexp.Compile(pattern) + s.Assert(err == nil, "%s", err) + // We don't need to check if another task inserted the regex first, as it will be an identical result. + s.interpreter.regexCache.Add(pattern, compiled) + compiledRegex = compiled + } + return newPyBool(compiledRegex.MatchString(self)) +} + func boolType(s *scope, args []pyObject) pyObject { return newPyBool(args[0].IsTruthy()) } @@ -1030,11 +1046,11 @@ func allFunc(s *scope, args []pyObject) pyObject { return True } -func min(s *scope, args []pyObject) pyObject { +func minFunc(s *scope, args []pyObject) pyObject { return extreme(s, args, LessThan) } -func max(s *scope, args []pyObject) pyObject { +func maxFunc(s *scope, args []pyObject) pyObject { return extreme(s, args, GreaterThan) } @@ -1093,13 +1109,20 @@ func getLabels(s *scope, args []pyObject) pyObject { name := string(args[0].(pyString)) prefix := string(args[1].(pyString)) all := args[2].IsTruthy() - transitive := args[3].IsTruthy() + transitive := args[3] + maxDepth := int(args[4].(pyInt)) + if transitiveBool, ok := transitive.(pyBool); ok { + s.Assert(maxDepth == -1, "get_labels: only one of transitive and maxdepth may be specified, not both") + if !transitiveBool.IsTruthy() { + maxDepth = 0 + } + } if core.LooksLikeABuildLabel(name) { label := core.ParseBuildLabel(name, s.pkg.Name) - return getLabelsInternal(s.state.Graph.TargetOrDie(label), prefix, core.Built, all, transitive) + return getLabelsInternal(s.state.Graph.TargetOrDie(label), prefix, core.Built, all, maxDepth) } target := getTargetPost(s, name) - return getLabelsInternal(target, prefix, core.Building, all, transitive) + return getLabelsInternal(target, prefix, core.Building, all, maxDepth) } // addLabel adds a set of labels to the named rule @@ -1119,35 +1142,35 @@ func addLabel(s *scope, args []pyObject) pyObject { return None } -func getLabelsInternal(target *core.BuildTarget, prefix string, minState core.BuildTargetState, all, transitive bool) pyObject { +func getLabelsInternal(target *core.BuildTarget, prefix string, minState core.BuildTargetState, all bool, maxDepth int) pyObject { if target.State() < minState { log.Fatalf("get_labels called on a target that is not yet built: %s", target.Label) } - if all && !transitive { - log.Fatalf("get_labels can't be called with all set to true when transitive is set to False") + if all && maxDepth != -1 { + log.Fatalf("get_labels: if all is True, transitive must be True or maxdepth must be -1") } labels := map[string]bool{} done := map[*core.BuildTarget]bool{} - var getLabels func(*core.BuildTarget) - getLabels = func(t *core.BuildTarget) { + var getLabels func(*core.BuildTarget, int) + getLabels = func(t *core.BuildTarget, depth int) { for _, label := range t.Labels { if strings.HasPrefix(label, prefix) { labels[strings.TrimSpace(strings.TrimPrefix(label, prefix))] = true } } - if !transitive { + done[t] = true + if depth == 0 { return } - done[t] = true if !t.OutputIsComplete || t == target || all { for _, dep := range t.Dependencies() { if !done[dep] { - getLabels(dep) + getLabels(dep, max(depth-1, -1)) } } } } - getLabels(target) + getLabels(target, maxDepth) ret := make([]string, len(labels)) i := 0 for label := range labels { @@ -1176,7 +1199,8 @@ func addDep(s *scope, args []pyObject) pyObject { target := getTargetPost(s, string(args[0].(pyString))) dep := s.parseLabelInPackage(string(args[1].(pyString)), s.pkg) exported := args[2].IsTruthy() - target.AddMaybeExportedDependency(dep, exported, false, false) + runtime := args[3].IsTruthy() + target.AddMaybeExportedDependency(dep, exported, false, false, runtime) // Queue this dependency if it'll be needed. if target.State() > core.Inactive { err := s.state.QueueTarget(dep, target.Label, false, core.ParseModeNormal) diff --git a/src/parse/asp/builtins_test.go b/src/parse/asp/builtins_test.go index dbe3764f6..02e622b0c 100644 --- a/src/parse/asp/builtins_test.go +++ b/src/parse/asp/builtins_test.go @@ -21,28 +21,43 @@ func TestPackageName(t *testing.T) { func TestGetLabels(t *testing.T) { state := core.NewBuildState(core.DefaultConfiguration()) - foo := core.NewBuildTarget(core.NewBuildLabel("pkg", "foo")) - foo.AddLabel("cc:ld:-ldl") - foo.SetState(core.Built) - bar := core.NewBuildTarget(core.NewBuildLabel("pkg", "bar")) - bar.AddDependency(foo.Label) - bar.AddLabel("cc:ld:-pthread") - bar.SetState(core.Built) + bottom := core.NewBuildTarget(core.NewBuildLabel("pkg", "bottom")) + bottom.AddLabel("target:bottom") + bottom.SetState(core.Built) - state.Graph.AddTarget(foo) - state.Graph.AddTarget(bar) + middle := core.NewBuildTarget(core.NewBuildLabel("pkg", "middle")) + middle.AddDependency(bottom.Label) + middle.AddLabel("target:middle") + middle.SetState(core.Built) - err := bar.ResolveDependencies(state.Graph) + top := core.NewBuildTarget(core.NewBuildLabel("pkg", "top")) + top.AddDependency(middle.Label) + top.AddLabel("target:top") + top.SetState(core.Built) + + state.Graph.AddTarget(bottom) + state.Graph.AddTarget(middle) + state.Graph.AddTarget(top) + + err := middle.ResolveDependencies(state.Graph) + require.NoError(t, err) + err = top.ResolveDependencies(state.Graph) require.NoError(t, err) s := &scope{state: state, pkg: core.NewPackage("pkg")} - ls := getLabels(s, []pyObject{pyString(":bar"), pyString("cc:ld:"), False, True}).(pyList) - assert.Len(t, ls, 2) - - ls = getLabels(s, []pyObject{pyString(":bar"), pyString("cc:ld:"), False, False}).(pyList) - assert.Len(t, ls, 1) - assert.Equal(t, pyString("-pthread"), ls[0]) + ls := getLabels(s, []pyObject{pyString(":top"), pyString("target:"), False, True, pyInt(-1)}).(pyList) // transitive=True + assert.Equal(t, pyList{pyString("bottom"), pyString("middle"), pyString("top")}, ls) + ls = getLabels(s, []pyObject{pyString(":top"), pyString("target:"), False, None, pyInt(-1)}).(pyList) // maxdepth=-1 (equivalent to above) + assert.Equal(t, pyList{pyString("bottom"), pyString("middle"), pyString("top")}, ls) + + ls = getLabels(s, []pyObject{pyString(":top"), pyString("target:"), False, False, pyInt(-1)}).(pyList) // transitive=False + assert.Equal(t, pyList{pyString("top")}, ls) + ls = getLabels(s, []pyObject{pyString(":top"), pyString("target:"), False, None, pyInt(0)}).(pyList) // maxdepth=0 (equivalent to above) + assert.Equal(t, pyList{pyString("top")}, ls) + + ls = getLabels(s, []pyObject{pyString(":top"), pyString("target:"), False, None, pyInt(1)}).(pyList) // maxdepth=1 + assert.Equal(t, pyList{pyString("middle"), pyString("top")}, ls) } func TestTag(t *testing.T) { diff --git a/src/parse/asp/interpreter.go b/src/parse/asp/interpreter.go index 4d8199439..93e4ca0a3 100644 --- a/src/parse/asp/interpreter.go +++ b/src/parse/asp/interpreter.go @@ -6,6 +6,7 @@ import ( "iter" "path/filepath" "reflect" + "regexp" "runtime/debug" "runtime/pprof" "strings" @@ -31,6 +32,8 @@ type interpreter struct { limiter semaphore stringMethods, dictMethods, configMethods map[string]*pyFunc + + regexCache *cmap.Map[string, *regexp.Regexp] } // newInterpreter creates and returns a new interpreter instance. @@ -42,10 +45,11 @@ func newInterpreter(state *core.BuildState, p *Parser) *interpreter { locals: map[string]pyObject{}, } i := &interpreter{ - scope: s, - parser: p, - configs: map[*core.BuildState]*pyConfig{}, - limiter: make(semaphore, state.Config.Parse.NumThreads), + scope: s, + parser: p, + configs: map[*core.BuildState]*pyConfig{}, + limiter: make(semaphore, state.Config.Parse.NumThreads), + regexCache: cmap.New[string, *regexp.Regexp](cmap.SmallShardCount, cmap.XXHash), } // If we're creating an interpreter for a subrepo, we should share the subinclude cache. if p.interpreter != nil { diff --git a/src/parse/asp/targets.go b/src/parse/asp/targets.go index 94c656830..48404b792 100644 --- a/src/parse/asp/targets.go +++ b/src/parse/asp/targets.go @@ -30,6 +30,9 @@ const ( outsBuildRuleArgIdx depsBuildRuleArgIdx exportedDepsBuildRuleArgIdx + runtimeDepsBuildRuleArgIdx + runtimeDepsFromSrcsBuildRuleArgIdx + runtimeDepsFromDepsBuildRuleArgIdx secretsBuildRuleArgIdx toolsBuildRuleArgIdx testToolsBuildRuleArgIdx @@ -141,6 +144,11 @@ func createTarget(s *scope, args []pyObject) *core.BuildTarget { if target.IsRemoteFile { target.AddLabel("remote") } + // filegroups don't really produce any outputs of their own - they're just the filegroup's own sources. + // The run-time dependencies of a filegroup's sources should therefore be treated as the filegroup's own + // run-time dependencies. + target.RuntimeDependenciesFromSources = target.IsFilegroup || isTruthy(runtimeDepsFromSrcsBuildRuleArgIdx) + target.RuntimeDependenciesFromDependencies = isTruthy(runtimeDepsFromDepsBuildRuleArgIdx) target.Command, target.Commands = decodeCommands(s, args[cmdBuildRuleArgIdx]) if test { target.Test = new(core.TestFields) @@ -270,9 +278,10 @@ func populateTarget(s *scope, t *core.BuildTarget, args []pyObject) { addMaybeNamedOutput(s, "outs", args[outsBuildRuleArgIdx], t.AddOutput, t.AddNamedOutput, t, false) addMaybeNamedOutput(s, "optional_outs", args[optionalOutsBuildRuleArgIdx], t.AddOptionalOutput, nil, t, true) t.HintDependencies(depLen(args[depsBuildRuleArgIdx]) + depLen(args[exportedDepsBuildRuleArgIdx]) + depLen(args[internalDepsBuildRuleArgIdx])) - addDependencies(s, "deps", args[depsBuildRuleArgIdx], t, false, false) - addDependencies(s, "exported_deps", args[exportedDepsBuildRuleArgIdx], t, true, false) - addDependencies(s, "internal_deps", args[internalDepsBuildRuleArgIdx], t, false, true) + addDependencies(s, "deps", args[depsBuildRuleArgIdx], t, false, false, false) + addDependencies(s, "exported_deps", args[exportedDepsBuildRuleArgIdx], t, true, false, false) + addDependencies(s, "internal_deps", args[internalDepsBuildRuleArgIdx], t, false, true, false) + addDependencies(s, "runtime_deps", args[runtimeDepsBuildRuleArgIdx], t, false, false, true) addStrings(s, "labels", args[labelsBuildRuleArgIdx], t.AddLabel) addStrings(s, "hashes", args[hashesBuildRuleArgIdx], t.AddHash) addStrings(s, "licences", args[licencesBuildRuleArgIdx], t.AddLicence) @@ -472,13 +481,13 @@ func addMaybeNamedSecret(s *scope, name string, obj pyObject, anon func(string), } // addDependencies adds dependencies to a target, which may or may not be exported. -func addDependencies(s *scope, name string, obj pyObject, target *core.BuildTarget, exported, internal bool) { +func addDependencies(s *scope, name string, obj pyObject, target *core.BuildTarget, exported, internal, runtime bool) { addStrings(s, name, obj, func(str string) { if s.state.Config.Bazel.Compatibility && !core.LooksLikeABuildLabel(str) && !strings.HasPrefix(str, "@") { // *sigh*... Bazel seems to allow an implicit : on the start of dependencies str = ":" + str } - target.AddMaybeExportedDependency(assertNotPseudoLabel(s, s.parseLabelInPackage(str, s.pkg)), exported, false, internal) + target.AddMaybeExportedDependency(assertNotPseudoLabel(s, s.parseLabelInPackage(str, s.pkg)), exported, false, internal, runtime) }) } diff --git a/src/parse/internal.tmpl b/src/parse/internal.tmpl index be7a29888..90cfe6b67 100644 --- a/src/parse/internal.tmpl +++ b/src/parse/internal.tmpl @@ -1,14 +1,10 @@ remote_file( name = "arcat", - url = f"https://github.com/please-build/arcat/releases/download/v1.2.1/arcat-1.2.1-{CONFIG.HOSTOS}_{CONFIG.HOSTARCH}", + url = f"https://github.com/please-build/arcat/releases/download/v1.3.1/arcat-1.3.1-{CONFIG.HOSTOS}_{CONFIG.HOSTARCH}", out = "arcat", binary = True, hashes = [ - "507958f2e44e5de7529cb85fa9137ddcca2293daf6ef30dbc1a1cfa22e86ee96", # darwin_amd64 - "717cb15f1237010740be50df3561027a976ac5bd2a5d8f5c30d2ea57ccbcad82", # darwin_arm64 - "190fbf9cdcbcf53a82b886076c45a07323eeb6b4f460485c645cf2f306927c0c", # freebsd_amd64 - "e5b23a127c093939f21bf2f8cb66635b7f0b88cf8a3d1fca1bc19a114f5c5a0c", # linux_amd64 - "019ee52b534a3c48028e9a7229990055432af24a23892619e1a0d28eb3265245", # linux_arm64 + "{{ .ArcatHash }}", # defined in internal_package.go ], visibility = ["PUBLIC"], ) @@ -29,3 +25,4 @@ genrule( binary = True, ) {{ end }} + diff --git a/src/parse/internal_package.go b/src/parse/internal_package.go index c2d77bd13..b867eca57 100644 --- a/src/parse/internal_package.go +++ b/src/parse/internal_package.go @@ -27,15 +27,33 @@ func GetInternalPackage(config *core.Configuration) (string, error) { url = fmt.Sprintf("%s/%s_%s/%s/please_tools_%s.tar.xz", config.Please.DownloadLocation, runtime.GOOS, runtime.GOARCH, version.PleaseVersion, version.PleaseVersion) } + var arcatHash string + switch fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH) { + case "darwin_amd64": + arcatHash = "6af2cf108592535701aa9395f3a5deeb48a5dfbe8174a8ebe3d56bb93de2c255" + case "darwin_arm64": + arcatHash = "5070ef05d14c66a85d438f400c6ff734a23833929775d6824b69207b704034bf" + case "freebsd_amd64": + arcatHash = "05ad6ac45be3a4ca1238bb1bd09207a596f8ff5f885415f8df4ff2dc849fa04e" + case "linux_amd64": + arcatHash = "aec85425355291e515cd10ac0addec3a5bc9e05c9d07af01aca8c34aaf0f1222" + case "linux_arm64": + arcatHash = "8266cb95cc84b23642bca6567f8b4bd18de399c887cb5845ab6a901d0dba54d2" + default: + return "", fmt.Errorf("arcat tool not supported for platform: %s_%s", runtime.GOOS, runtime.GOARCH) + } + data := struct { - ToolsURL string - Tools []string + ToolsURL string + Tools []string + ArcatHash string }{ ToolsURL: url, Tools: []string{ "build_langserver", "please_sandbox", }, + ArcatHash: arcatHash, } var buf bytes.Buffer diff --git a/src/query/print.go b/src/query/print.go index 9d5524bd3..0c551055d 100644 --- a/src/query/print.go +++ b/src/query/print.go @@ -137,6 +137,9 @@ func specialFields() specialFieldsMap { "exported_deps": func(target *core.BuildTarget) interface{} { return target.ExportedDependencies() }, + "runtime_deps": func(target *core.BuildTarget) interface{} { + return target.RuntimeDependencies() + }, "visibility": func(target *core.BuildTarget) interface{} { if len(target.Visibility) == 1 && target.Visibility[0] == core.WholeGraph[0] { return []string{"PUBLIC"} diff --git a/src/remote/remote.go b/src/remote/remote.go index e7fe68fe2..ee7ec220d 100644 --- a/src/remote/remote.go +++ b/src/remote/remote.go @@ -351,10 +351,13 @@ func (c *Client) Build(target *core.BuildTarget) (*core.BuildMetadata, error) { if err := c.Download(target); err != nil { return metadata, err } - // TODO(peterebden): Should this not just be part of Download()? + // TODO(peterebden): Should these not just be part of Download()? if err := c.downloadData(target); err != nil { return metadata, err } + if err := c.downloadRuntimeDependencies(target); err != nil { + return metadata, err + } } return metadata, nil } @@ -376,6 +379,22 @@ func (c *Client) downloadData(target *core.BuildTarget) error { return g.Wait() } +// downloadRuntimeDependencies downloads all the runtime dependencies for a target. +func (c *Client) downloadRuntimeDependencies(target *core.BuildTarget) error { + var g errgroup.Group + for runDep := range target.IterAllRuntimeDependencies(c.state.Graph) { + l, _ := runDep.Label() + t := c.state.Graph.TargetOrDie(l) + g.Go(func() error { + if err := c.Download(t); err != nil { + return err + } + return nil + }) + } + return g.Wait() +} + // Run runs a target on the remote executors. func (c *Client) Run(target *core.BuildTarget) error { if err := c.CheckInitialised(); err != nil { diff --git a/test/builtins/BUILD b/test/builtins/BUILD new file mode 100644 index 000000000..ccb21ec42 --- /dev/null +++ b/test/builtins/BUILD @@ -0,0 +1,7 @@ +subinclude("//test/build_defs") + +please_repo_e2e_test( + name = "strings_test", + plz_command = "plz build", + repo = "strings", +) diff --git a/test/builtins/strings/.plzconfig b/test/builtins/strings/.plzconfig new file mode 100644 index 000000000..ea85e4f73 --- /dev/null +++ b/test/builtins/strings/.plzconfig @@ -0,0 +1,2 @@ +[parse] +BuildFileName = BUILD_FILE diff --git a/test/builtins/strings/BUILD_FILE b/test/builtins/strings/BUILD_FILE new file mode 100644 index 000000000..8d7275107 --- /dev/null +++ b/test/builtins/strings/BUILD_FILE @@ -0,0 +1,45 @@ + +assert ",".join(["a","b","c"]) == "a,b,c" + +assert "a,b,c".split(",") == ["a", "b", "c"] + +assert "abc".replace("bc", "ab") == "aab" + +pre, sep, post = "a,b,c".partition(",") +assert pre == "a" and sep == "," and post == "b,c" + +pre, sep, post = "a,b,c".rpartition(",") +assert pre == "a,b" and sep == "," and post == "c" + +assert "abc".startswith("ab") == True + +assert "abc".endswith("bc") == True + +assert "a{var1},b{var2},c{var3}".format(var1="a", var2=2, var3=[3]) == "aa,b2,c[3]" + +assert "abcba".lstrip("a") == "bcba" + +assert "abcba".rstrip("a") == "abcb" + +assert "abcba".strip("a") == "bcb" + +assert "abc".removeprefix("ab") == "c" + +assert "abc".removesuffix("bc") == "a" + +assert "abcba".find("b") == 1 + +assert "abcba".rfind("b") == 3 + +assert "abcba".count("b") == 2 + +assert "abc".upper() == "ABC" + +assert "ABC".lower() == "abc" + + +assert "abc".matches("a.c") + +assert "abbbbbbc".matches("a.*c") + +assert not "abc".matches("$b") diff --git a/test/get_labels/BUILD b/test/get_labels/BUILD new file mode 100644 index 000000000..e7c03693b --- /dev/null +++ b/test/get_labels/BUILD @@ -0,0 +1,109 @@ +subinclude("//test/build_defs") + +def maxdepth_test(name:str, deps:list=None, maxdepth:int, expected:list): + test_case = genrule( + name = name, + outs = [f"{name}.sh"], + cmd = { + "opt": "echo '#!/bin/sh' > $OUTS", + }, + binary = True, + labels = [ + f"name:{name}", + "manual", + ], + output_is_complete = False, + pre_build = echo_name_labels_up_to(maxdepth), + deps = deps, + ) + expected = text_file( + name = tag(name, "expected"), + content = "\n".join(expected) + "\n", + ) + plz_e2e_test( + name = f"{name}_test", + cmd = f"plz run //test/get_labels:{name}", + expected_output = expected, + ) + +def dep(name:str, deps:list=None): + return genrule( + name = name, + outs = [name], + cmd = "touch $OUTS", + labels = [ + f"name:{name}", + "manual", + ], + output_is_complete = False, + deps = deps, + ) + +def echo_name_labels_up_to(maxdepth:int): + def echo(name:str): + labels = get_labels(name, "name:", maxdepth=maxdepth) + set_command(name, "opt", " && ".join([get_command(name, "opt")] + [f"echo 'echo {l}' >> $OUTS" for l in labels])) + return echo + +dep(name = "dep1", deps = [":dep3"]) +dep(name = "dep2") +dep(name = "dep3", deps = [":dep4", ":dep5"]) +dep(name = "dep4") +dep(name = "dep5") + +maxdepth_test( + name = "target_only", + deps = [ + ":dep1", + ":dep2", + ], + maxdepth = 0, + expected = ["target_only"], +) + +maxdepth_test( + name = "direct_deps", + deps = [ + ":dep1", + ":dep2", + ], + maxdepth = 1, + expected = [ + "dep1", + "dep2", + "direct_deps", + ], +) + +maxdepth_test( + name = "second_level_deps", + deps = [ + ":dep1", + ":dep2", + ":dep3", + ], + maxdepth = 2, + expected = [ + "dep1", + "dep2", + "dep3", + "second_level_deps", + ], +) + +maxdepth_test( + name = "all_deps", + deps = [ + ":dep1", + ":dep2", + ], + maxdepth = -1, + expected = [ + "all_deps", + "dep1", + "dep2", + "dep3", + "dep4", + "dep5", + ], +) diff --git a/test/runtime_deps/BUILD b/test/runtime_deps/BUILD new file mode 100644 index 000000000..8f07efff6 --- /dev/null +++ b/test/runtime_deps/BUILD @@ -0,0 +1,101 @@ +subinclude("//test/build_defs") + +please_repo_e2e_test( + name = "runtime_deps_test", + plz_command = " && ".join([ + "plz test //test:runtime_deps_test_case", + ]), + repo = "repo", +) + +# Ensure that the runtime_deps field is correctly printed for those that have one (and that it is omitted for those that +# don't). Importantly, ensure that run-time dependencies are not printed transitively - `plz query print` should only +# print the target's direct run-time dependencies. +please_repo_e2e_test( + name = "query_print_test", + plz_command = " && ".join([ + "plz query print -f runtime_deps //test:runtime_deps_test_case > runtime_deps_test_case", + "plz query print -f runtime_deps //test:target_with_no_runtime_deps > target_with_no_runtime_deps", + ]), + expected_output = { + "runtime_deps_test_case": "//test:target_with_runtime_deps", + "target_with_no_runtime_deps": "", + }, + repo = "repo", +) + +# Ensure that run-time dependencies are in fact considered dependencies by `plz query deps`. Run-time dependencies added +# by a call to the add_runtime_dep function in a post-build function should not appear here. +_expected_deps = """\ +//test:dep_with_runtime_dep + //test:dep_runtime_dep +//test:src_with_runtime_dep + //test:src_dep_with_runtime_dep + //test:src_dep_runtime_dep + //test:src_runtime_dep +//test:target_with_runtime_deps + //test:runtime_dep + //test:target_requiring_kittens + //test:target_with_provides_runtime_dep + //test:provides_runtime_dep + //test:target_with_another_runtime_dep + //test:another_runtime_dep + //test:target_with_build_and_runtime_deps + //test:build_and_runtime_dep + //test:target_with_post_build_runtime_dep +//test:test_data_with_runtime_dep + //test:test_data_runtime_dep +//test:test_tool_with_runtime_dep + //test:test_tool_runtime_dep""" + +please_repo_e2e_test( + name = "query_deps_test", + plz_command = "plz query deps //test:runtime_deps_test_case > deps", + expected_output = { + "deps": _expected_deps, + }, + repo = "repo", +) + +# Ensure that run-time dependencies are in fact considered dependencies by `plz query revdeps`. +please_repo_e2e_test( + name = "query_revdeps_test", + plz_command = "plz query revdeps //test:another_runtime_dep > revdeps", + expected_output = { + "revdeps": "//test:target_with_another_runtime_dep", + }, + repo = "repo", +) + +please_repo_e2e_test( + name = "runtime_deps_from_srcs_test", + plz_command = "plz test //from_srcs_test:runtime_deps_from_srcs_test_case", + repo = "repo", +) + +please_repo_e2e_test( + name = "runtime_deps_from_data_test", + plz_command = " && ".join([ + "plz test //from_data_test:runtime_deps_from_data_test_case", + "plz -c dbg test //from_data_test:runtime_deps_from_data_test_case", + ]), + repo = "repo", +) + +please_repo_e2e_test( + name = "runtime_deps_from_deps_test", + plz_command = "plz test //from_deps_test:runtime_deps_from_deps_test_case", + repo = "repo", +) + +please_repo_e2e_test( + name = "runtime_deps_from_exported_deps_test", + plz_command = "plz test //from_exported_deps_test:runtime_deps_from_exported_deps_test_case", + repo = "repo", +) + +please_repo_e2e_test( + name = "filegroup_test", + plz_command = "plz test //filegroup_test:filegroup_test_case", + repo = "repo", +) diff --git a/test/runtime_deps/repo/.plzconfig b/test/runtime_deps/repo/.plzconfig new file mode 100644 index 000000000..e69de29bb diff --git a/test/runtime_deps/repo/build_defs/BUILD_FILE b/test/runtime_deps/repo/build_defs/BUILD_FILE new file mode 100644 index 000000000..d8f94687e --- /dev/null +++ b/test/runtime_deps/repo/build_defs/BUILD_FILE @@ -0,0 +1,5 @@ +export_file( + name = "test", + src = "test.build_defs", + visibility = ["PUBLIC"], +) diff --git a/test/runtime_deps/repo/build_defs/test.build_defs b/test/runtime_deps/repo/build_defs/test.build_defs new file mode 100644 index 000000000..2f9e725fd --- /dev/null +++ b/test/runtime_deps/repo/build_defs/test.build_defs @@ -0,0 +1,45 @@ +def exists(file:str): + path = join_path(package_name(), file) + return f"echo -n '{path} exists: '; if [ -f '{path}' ]; then echo OK; else echo FAIL; exit 1; fi" + +def not_exists(file:str): + path = join_path(package_name(), file) + return f"echo -n '{path} does not exist: '; if [ -f '{path}' ]; then echo FAIL; exit 1; else echo OK; fi" + +def target(name:str, srcs:list=[], build_tests:list=[], data:list=[], debug_data:list=[], post_build:function=None, + requires:list=None, provides:dict=None, runtime_deps:list=[], runtime_deps_from_srcs:bool=False, + runtime_deps_from_deps:bool=False, exported_deps:list=[], deps:list=[]): + # Ensure that run-time dependencies are not present at build time, unless they are also + # explicitly given as build-time dependencies. (The lstrips here turn build target names + # into the names of files they output - they're slightly grungy, but they allow us to + # re-use exists and not_exists outside of this function; they work because targets + # generated by runtime_dep output a single file who name is identical to that of the + # target's.) + cmd = [not_exists(r.lstrip(":")) for r in runtime_deps if r not in deps] + cmd += [exists(d.lstrip(":")) for d in deps] + return build_rule( + name = name, + srcs = srcs, + outs = [name], + # Assume that if a post-build function is defined, it dynamically adds a run-time + # dependency, which requires this target to be marked as binary. + binary = len(runtime_deps) > 0 or post_build is not None, + cmd = " && ".join(cmd + build_tests + ["touch $OUTS"]), + data = data, + debug_data = debug_data, + post_build = post_build, + requires = requires, + provides = provides, + runtime_deps = runtime_deps, + runtime_deps_from_srcs = runtime_deps_from_srcs, + runtime_deps_from_deps = runtime_deps_from_deps, + exported_deps = exported_deps, + deps = deps, + ) + +def runtime_dep(name:str): + return genrule( + name = name, + outs = [name], + cmd = "touch $OUTS", + ) diff --git a/test/runtime_deps/repo/filegroup_test/BUILD_FILE b/test/runtime_deps/repo/filegroup_test/BUILD_FILE new file mode 100644 index 000000000..19083d918 --- /dev/null +++ b/test/runtime_deps/repo/filegroup_test/BUILD_FILE @@ -0,0 +1,27 @@ +subinclude("//build_defs:test") + +filegroup( + name = "filegroup", + srcs = [":filegroup_src"], +) + +target( + name = "filegroup_src", + runtime_deps = [":filegroup_src_runtime_dep"], +) + +runtime_dep("filegroup_src_runtime_dep") + +gentest( + name = "filegroup_test_case", + outs = ["filegroup_test_case"], + cmd = [ + not_exists("filegroup_src_runtime_dep"), + "touch $OUTS", + ], + data = [":filegroup"], + no_test_output = True, + test_cmd = [ + exists("filegroup_src_runtime_dep"), + ], +) diff --git a/test/runtime_deps/repo/from_data_test/BUILD_FILE b/test/runtime_deps/repo/from_data_test/BUILD_FILE new file mode 100644 index 000000000..27eaaa871 --- /dev/null +++ b/test/runtime_deps/repo/from_data_test/BUILD_FILE @@ -0,0 +1,42 @@ +subinclude("//build_defs:test") + +target( + name = "runtime_deps_from_data", + data = [":data_with_runtime_dep"], + debug_data = [":debug_data_with_runtime_dep"], +) + +target( + name = "data_with_runtime_dep", + runtime_deps = [":data_runtime_dep"], +) + +target( + name = "debug_data_with_runtime_dep", + runtime_deps = [":debug_data_runtime_dep"], +) + +runtime_dep("data_runtime_dep") +runtime_dep("debug_data_runtime_dep") + +gentest( + name = "runtime_deps_from_data_test_case", + outs = ["runtime_deps_from_data_test_case"], + cmd = [ + not_exists("data_runtime_dep"), + not_exists("debug_data_runtime_dep"), + "touch $OUTS", + ], + data = [":runtime_deps_from_data"], + no_test_output = True, + test_cmd = { + "opt": " && ".join([ + exists("data_runtime_dep"), + not_exists("debug_data_runtime_dep"), + ]), + "dbg": " && ".join([ + exists("data_runtime_dep"), + exists("debug_data_runtime_dep"), + ]), + }, +) diff --git a/test/runtime_deps/repo/from_deps_test/BUILD_FILE b/test/runtime_deps/repo/from_deps_test/BUILD_FILE new file mode 100644 index 000000000..6c2e3fe4a --- /dev/null +++ b/test/runtime_deps/repo/from_deps_test/BUILD_FILE @@ -0,0 +1,28 @@ +subinclude("//build_defs:test") + +target( + name = "runtime_deps_from_deps", + runtime_deps_from_deps = True, + deps = [":dep"], +) + +target( + name = "dep", + runtime_deps = [":dep_runtime_dep"], +) + +runtime_dep("dep_runtime_dep") + +gentest( + name = "runtime_deps_from_deps_test_case", + outs = ["runtime_deps_from_deps_test_case"], + cmd = [ + not_exists("dep_runtime_dep"), + "touch $OUTS", + ], + data = [":runtime_deps_from_deps"], + no_test_output = True, + test_cmd = [ + exists("dep_runtime_dep"), + ], +) diff --git a/test/runtime_deps/repo/from_exported_deps_test/BUILD_FILE b/test/runtime_deps/repo/from_exported_deps_test/BUILD_FILE new file mode 100644 index 000000000..bd6121a4b --- /dev/null +++ b/test/runtime_deps/repo/from_exported_deps_test/BUILD_FILE @@ -0,0 +1,45 @@ +subinclude("//build_defs:test") + +target( + name = "runtime_deps_from_deps", + runtime_deps_from_deps = True, + deps = [":dep_with_exported_dep"], +) + +target( + name = "dep_with_exported_dep", + exported_deps = [":exported_dep"], + deps = [":dep"], +) + +target( + name = "exported_dep", + runtime_deps = [":exported_dep_runtime_dep"], +) + +target( + name = "dep", + runtime_deps = [":dep_runtime_dep"], +) + +runtime_dep("exported_dep_runtime_dep") +runtime_dep("dep_runtime_dep") + +gentest( + name = "runtime_deps_from_exported_deps_test_case", + outs = ["runtime_deps_from_exported_deps_test_case"], + cmd = [ + not_exists("dep_runtime_dep"), + not_exists("exported_dep_runtime_dep"), + "touch $OUTS", + ], + data = [":runtime_deps_from_deps"], + no_test_output = True, + test_cmd = [ + # This still shouldn't exist - it's a build-time dependency of a build-time dependency, not a run-time + # dependency of a build-time dependency. + not_exists("dep_runtime_dep"), + # This one should exist - it's a run-time dependency of an (exported) build-time dependency. + exists("exported_dep_runtime_dep"), + ], +) diff --git a/test/runtime_deps/repo/from_srcs_test/BUILD_FILE b/test/runtime_deps/repo/from_srcs_test/BUILD_FILE new file mode 100644 index 000000000..5e525f651 --- /dev/null +++ b/test/runtime_deps/repo/from_srcs_test/BUILD_FILE @@ -0,0 +1,28 @@ +subinclude("//build_defs:test") + +target( + name = "runtime_deps_from_srcs", + srcs = [":src"], + runtime_deps_from_srcs = True, +) + +target( + name = "src", + runtime_deps = [":src_runtime_dep"], +) + +runtime_dep("src_runtime_dep") + +gentest( + name = "runtime_deps_from_srcs_test_case", + outs = ["runtime_deps_from_srcs_test_case"], + cmd = [ + not_exists("src_runtime_dep"), + "touch $OUTS", + ], + data = [":runtime_deps_from_srcs"], + no_test_output = True, + test_cmd = [ + exists("src_runtime_dep"), + ], +) diff --git a/test/runtime_deps/repo/test/BUILD_FILE b/test/runtime_deps/repo/test/BUILD_FILE new file mode 100644 index 000000000..c33f48cb6 --- /dev/null +++ b/test/runtime_deps/repo/test/BUILD_FILE @@ -0,0 +1,159 @@ +subinclude("//build_defs:test") + +target( + name = "target_with_no_runtime_deps", +) + +target( + name = "target_with_runtime_deps", + build_tests = [ + # Ensure that the run-time dependencies :target_with_post_build_runtime_dep and + # :target_requiring_kittens are not present at build time (these ones are tricky + # to identify statically, hence explicitly listing them here). + not_exists("post_build_runtime_dep"), + not_exists("provides_runtime_dep"), + ], + runtime_deps = [ + ":runtime_dep", + ":target_with_another_runtime_dep", + ":target_with_build_and_runtime_deps", + ":target_with_post_build_runtime_dep", + ":target_requiring_kittens", + ], +) + +target( + name = "target_with_another_runtime_dep", + runtime_deps = [":another_runtime_dep"], +) + +target( + name = "target_with_build_and_runtime_deps", + runtime_deps = [":build_and_runtime_dep"], + deps = [":build_and_runtime_dep"], +) + +target( + name = "target_with_post_build_runtime_dep", + build_tests = [ + # Ensure that the run-time dependency added by the post-build function is not present at + # build time (this one can't be identified statically, hence explicitly listing it here). + not_exists("post_build_runtime_dep"), + ], + post_build = lambda name, _: add_runtime_dep(name, ":post_build_runtime_dep"), +) + +target( + name = "src_with_runtime_dep", + runtime_deps = [":src_runtime_dep"], + deps = [":src_dep_with_runtime_dep"], +) + +target( + name = "src_dep_with_runtime_dep", + runtime_deps = [":src_dep_runtime_dep"], +) + +target( + name = "dep_with_runtime_dep", + runtime_deps = [":dep_runtime_dep"], +) + +target( + name = "target_requiring_kittens", + build_tests = [ + # Ensure that neither of the run-time dependencies that could possibly be provided by + # :runtime_dep_providing_kittens or :target_with_provides_runtime_dep are present at + # build time (only the latter *should* be provided, but it isn't possible to generate + # both of these tests statically, hence explicitly listing them here). + not_exists("runtime_dep_providing_kittens_runtime_dep"), + not_exists("provides_runtime_dep"), + ], + requires = ["kittens"], + runtime_deps = [":runtime_dep_providing_kittens"], +) + +target( + name = "runtime_dep_providing_kittens", + provides = { + "kittens": ":target_with_provides_runtime_dep", + }, + runtime_deps = [":runtime_dep_providing_kittens_runtime_dep"], +) + +target( + name = "target_with_provides_runtime_dep", + runtime_deps = [":provides_runtime_dep"], +) + +target( + name = "test_data_with_runtime_dep", + runtime_deps = [":test_data_runtime_dep"], +) + +target( + name = "test_tool_with_runtime_dep", + runtime_deps = [":test_tool_runtime_dep"], +) + +runtime_dep("runtime_dep") +runtime_dep("another_runtime_dep") +runtime_dep("build_and_runtime_dep") +runtime_dep("post_build_runtime_dep") +runtime_dep("src_runtime_dep") +runtime_dep("src_dep_runtime_dep") +runtime_dep("dep_runtime_dep") +runtime_dep("test_data_runtime_dep") +runtime_dep("test_tool_runtime_dep") +runtime_dep("provides_runtime_dep") +runtime_dep("runtime_dep_providing_kittens_runtime_dep") + +gentest( + name = "runtime_deps_test_case", + srcs = { + "src_with_runtime_dep": [":src_with_runtime_dep"], + }, + outs = ["runtime_deps_test_case"], + cmd = [ + # Ensure this target's sources' and dependencies' run-time dependencies are present at build time... + exists("src_runtime_dep"), + exists("dep_runtime_dep"), + # ...but that those targets' (build-time) dependencies' run-time dependencies are not... + not_exists("src_dep_runtime_dep"), + # ...and that this target's run-time dependencies are not... + not_exists("runtime_dep"), + not_exists("another_runtime_dep"), + not_exists("build_and_runtime_dep"), + not_exists("post_build_runtime_dep"), + not_exists("provides_runtime_dep"), + not_exists("runtime_dep_providing_kittens_runtime_dep"), + # ...and that this target's test-time dependencies are not. + not_exists("test_data_runtime_dep"), + not_exists("test_tool_runtime_dep"), + "touch $OUTS", + ], + data = [":test_data_with_runtime_dep"], + no_test_output = True, + runtime_deps = [":target_with_runtime_deps"], + test_cmd = [ + # Ensure this target's sources' and dependencies' (transitive) run-time dependencies are not present at test time... + not_exists("src_runtime_dep"), + not_exists("dep_runtime_dep"), + not_exists("src_dep_runtime_dep"), + # ...but that this target's run-time dependencies are... + exists("runtime_dep"), + exists("another_runtime_dep"), + exists("build_and_runtime_dep"), + exists("post_build_runtime_dep"), + exists("provides_runtime_dep"), + # ...and that this target's test-time dependencies are... + exists("test_data_runtime_dep"), + exists("test_tool_runtime_dep"), + # ...and that the requires/provides in the run-time dependency tree were correctly resolved. + not_exists("runtime_dep_providing_kittens_runtime_dep"), + ], + test_tools = [":test_tool_with_runtime_dep"], + deps = [ + ":dep_with_runtime_dep", + ], +) diff --git a/third_party/go/BUILD b/third_party/go/BUILD index f42164f98..e4a905761 100644 --- a/third_party/go/BUILD +++ b/third_party/go/BUILD @@ -5,14 +5,14 @@ package(default_visibility = ["PUBLIC"]) go_toolchain( name = "toolchain", hashes = [ - "addbfce2056744962e2d7436313ab93486660cf7a2e066d171b9d6f2da7c7abe", # go1.24.1.darwin-amd64.tar.gz - "295581b5619acc92f5106e5bcb05c51869337eb19742fdfa6c8346c18e78ff88", # go1.24.1.darwin-arm64.tar.gz - "47d7de8bb64d5c3ee7b6723aa62d5ecb11e3568ef2249bbe1d4bbd432d37c00c", # go1.24.1.freebsd-amd64.tar.gz - "cb2396bae64183cdccf81a9a6df0aea3bce9511fc21469fb89a0c00470088073", # go1.24.1.linux-amd64.tar.gz - "8df5750ffc0281017fb6070fba450f5d22b600a02081dceef47966ffaf36a3af", # go1.24.1.linux-arm64.tar.gz + "fde05d84f7f64c8d01564f299ea1897fe94457d20d8d9054200ac1f8ae1c2bc3", # go1.24.10.darwin-amd64.tar.gz + "71c70841bcdadf4b5d2f7c0f099952907969f25235663622a47d6f2233ad39aa", # go1.24.10.darwin-arm64.tar.gz + "cb917b64aa4a407ed3310b397cc4dca10f0a3e2b0dd184ed74164ceaeab2625e", # go1.24.10.freebsd-amd64.tar.gz + "dd52b974e3d9c5a7bbfb222c685806def6be5d6f7efd10f9caa9ca1fa2f47955", # go1.24.10.linux-amd64.tar.gz + "94a99dae43dab8a3fe337485bbb89214b524285ec53ea02040514b0c2a9c3f94", # go1.24.10.linux-arm64.tar.gz ], install_std = False, - version = "1.24.1", + version = "1.24.10", ) go_stdlib( @@ -35,12 +35,12 @@ go_repo( go_repo( module = "golang.org/x/text", - version = "v0.23.0", + version = "v0.31.0", ) go_repo( module = "golang.org/x/tools", - version = "v0.21.1-0.20240508182429-e35e4ccd0d2d", + version = "v0.38.0", ) go_repo( @@ -116,7 +116,7 @@ go_repo( go_repo( module = "golang.org/x/net", - version = "v0.38.0", + version = "v0.47.0", ) go_repo( @@ -211,7 +211,7 @@ go_repo( go_repo( module = "golang.org/x/term", - version = "v0.30.0", + version = "v0.37.0", ) go_repo( @@ -271,7 +271,7 @@ go_repo( go_repo( module = "golang.org/x/sync", - version = "v0.12.0", + version = "v0.18.0", ) go_repo( @@ -331,7 +331,7 @@ go_repo( go_repo( module = "golang.org/x/sys", - version = "v0.31.0", + version = "v0.38.0", ) go_repo( @@ -401,7 +401,7 @@ go_repo( go_repo( module = "golang.org/x/crypto", - version = "v0.36.0", + version = "v0.45.0", ) go_repo( diff --git a/tools/build_langserver/lsp/definition_test.go b/tools/build_langserver/lsp/definition_test.go index ff05ee0af..a728a8ca7 100644 --- a/tools/build_langserver/lsp/definition_test.go +++ b/tools/build_langserver/lsp/definition_test.go @@ -25,7 +25,7 @@ func TestDefinition(t *testing.T) { assert.Equal(t, []lsp.Location{ { URI: lsp.DocumentURI("file://" + filepath.Join(cacheDir, "please/misc_rules.build_defs")), - Range: xrng(3, 0, 145, 5), + Range: xrng(3, 0, 191, 5), }, }, locs) } @@ -45,7 +45,7 @@ func TestDefinitionStatement(t *testing.T) { assert.Equal(t, []lsp.Location{ { URI: lsp.DocumentURI("file://" + filepath.Join(cacheDir, "please/misc_rules.build_defs")), - Range: xrng(3, 0, 145, 5), + Range: xrng(3, 0, 191, 5), }, }, locs) } @@ -65,7 +65,7 @@ func TestDefinitionBuiltin(t *testing.T) { assert.Equal(t, []lsp.Location{ { URI: lsp.DocumentURI("file://" + filepath.Join(cacheDir, "please/misc_rules.build_defs")), - Range: xrng(3, 0, 145, 5), + Range: xrng(3, 0, 191, 5), }, }, locs) }