From fec0f55739f5ed6b7607706f1c04088eef5781cd Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Wed, 5 Aug 2026 23:39:01 +0900 Subject: [PATCH 1/6] wip --- examples/BUILD.bazel | 1 + examples/bzlmod/requirements_lock_3_9.txt | 24 +++++------ python/uv/private/lock.bzl | 52 ++++++++++++++++++++--- script.sh | 18 ++++++++ 4 files changed, 77 insertions(+), 18 deletions(-) create mode 100755 script.sh diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index a08e5e64ae..ffb2e50049 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -21,6 +21,7 @@ lock( name = "bzlmod_requirements_3_9", srcs = ["bzlmod/requirements.in"], out = "bzlmod/requirements_lock_3_9.txt", + directory = "examples/bzlmod", args = [ "--emit-index-url", "--universal", diff --git a/examples/bzlmod/requirements_lock_3_9.txt b/examples/bzlmod/requirements_lock_3_9.txt index 8a6d41441a..938f6ad7b6 100644 --- a/examples/bzlmod/requirements_lock_3_9.txt +++ b/examples/bzlmod/requirements_lock_3_9.txt @@ -27,7 +27,7 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via - # -r examples/bzlmod/requirements.in + # -r requirements.in # pylint # sphinx dill==0.3.6 \ @@ -183,17 +183,17 @@ pylint==2.15.9 \ --hash=sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4 \ --hash=sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb # via - # -r examples/bzlmod/requirements.in + # -r requirements.in # pylint-print pylint-print==1.0.1 \ --hash=sha256:30aa207e9718ebf4ceb47fb87012092e6d8743aab932aa07aa14a73e750ad3d0 \ --hash=sha256:a2b2599e7887b93e551db2624c523c1e6e9e58c3be8416cd98d41e4427e2669b - # via -r examples/bzlmod/requirements.in + # via -r requirements.in python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 # via - # -r examples/bzlmod/requirements.in + # -r requirements.in # s3cmd python-magic==0.4.27 \ --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ @@ -256,12 +256,12 @@ requests==2.25.1 \ --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e # via - # -r examples/bzlmod/requirements.in + # -r requirements.in # sphinx s3cmd==2.1.0 \ --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa \ --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03 - # via -r examples/bzlmod/requirements.in + # via -r requirements.in setuptools==78.1.1 \ --hash=sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561 \ --hash=sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d @@ -280,7 +280,7 @@ sphinx==7.2.6 \ --hash=sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560 \ --hash=sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5 # via - # -r examples/bzlmod/requirements.in + # -r requirements.in # sphinxcontrib-applehelp # sphinxcontrib-devhelp # sphinxcontrib-htmlhelp @@ -310,12 +310,12 @@ sphinxcontrib-serializinghtml==1.1.9 \ --hash=sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54 \ --hash=sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1 # via - # -r examples/bzlmod/requirements.in + # -r requirements.in # sphinx tabulate==0.9.0 \ --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \ --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f - # via -r examples/bzlmod/requirements.in + # via -r requirements.in tomli==2.0.1 ; python_full_version < '3.11' \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f @@ -405,11 +405,11 @@ websockets==11.0.3 \ --hash=sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74 \ --hash=sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0 \ --hash=sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564 - # via -r examples/bzlmod/requirements.in + # via -r requirements.in wheel==0.40.0 \ --hash=sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873 \ --hash=sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247 - # via -r examples/bzlmod/requirements.in + # via -r requirements.in wrapt==1.14.1 \ --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \ --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \ @@ -479,7 +479,7 @@ wrapt==1.14.1 \ yamllint==1.28.0 \ --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \ --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b - # via -r examples/bzlmod/requirements.in + # via -r requirements.in zipp==3.20.0 ; python_full_version < '3.10' \ --hash=sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31 \ --hash=sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index 695591e924..f1bb9ca8cf 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -71,6 +71,30 @@ def _args(ctx): add_all = _add_all, ) +def _reroot(x, directory): + if not directory: + return x + + if hasattr(x, "short_path"): + x = x.short_path + + return x[len(directory)+1:] or "." + +def _reroot_all(xs, directory): + return [ + _reroot(x, directory) + for x in xs + ] + +def _up(x, directory): + if not directory: + return x + + return "{}/{}".format( + "/".join([".."] * (len(directory.split("/"))+0)), + getattr(x, "short_path", x), + ) + def _common_lock(ctx, locker): fname = "{}.out".format(ctx.label.name) @@ -117,11 +141,12 @@ def _common_lock(ctx, locker): # select the shortest match project = src.dirname + directory = getattr(ctx.attr, "directory", None) if project == None: project = ctx.label.package if project: - args.add_all([project], before_each = "--project") + args.add_all([_reroot(project, directory)], before_each = "--project") args.add_all(ctx.attr.args) @@ -129,7 +154,7 @@ def _common_lock(ctx, locker): runtime = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime python = runtime.interpreter or runtime.interpreter_path python_files = runtime.files or depset() - args.add("--python", python) + args.add("--python", _up(python, directory)) # These arguments does not change behaviour, but it reduces the output from # the command, which is especially verbose in stderr. @@ -252,14 +277,20 @@ def _pip_compile_impl(ctx): if not ctx.attr.strip_extras: args.add("--no-strip-extras") - args.add_all(ctx.files.build_constraints, before_each = "--build-constraints") - args.add_all(ctx.files.constraints, before_each = "--constraints") + directory = ctx.attr.directory + + if directory: + args.add_all([directory], before_each = "--directory") + + # TODO @aignas 2026-08-04: fix the --directory handling + args.add_all(_reroot_all(ctx.files.build_constraints, directory), before_each = "--build-constraints") + args.add_all(_reroot_all(ctx.files.constraints, directory), before_each = "--constraints") - args.run_shell.add("--output-file", output) + args.run_shell.add("--output-file", _reroot(output, directory)) mnemonic = "PyRequirementsLockUv" progress_message = "Creating a requirements.txt with uv: %{label}" - args.add_all(ctx.files.srcs) + args.add_all(_reroot_all(ctx.files.srcs, directory)) srcs = ctx.files.srcs + ctx.files.build_constraints + ctx.files.constraints return srcs, None, mnemonic, progress_message @@ -342,6 +373,12 @@ Added the {attr}`project` to configure the project setting if autodetection fail allow_files = True, doc = "Public, see the docs in the macro.", ), + "directory": attr.string( + doc = """\ +Sets the --directory flag if provided. Will fail if at least one of the files +does not start with the given prefix of the directory. +""", + ), "generate_hashes": attr.bool( doc = "Public, see the docs in the macro.", default = True, @@ -535,6 +572,7 @@ def lock( env = None, generate_hashes = True, python_version = None, + directory = None, project = None, strip_extras = False, **kwargs): @@ -636,6 +674,8 @@ def lock( lock_target_kwargs["build_constraints"] = build_constraints if constraints: lock_target_kwargs["constraints"] = constraints + if directory: + lock_target_kwargs["directory"] = directory if out.endswith(".lock"): _lock(name = name, **lock_target_kwargs) diff --git a/script.sh b/script.sh new file mode 100755 index 0000000000..9163fde5cf --- /dev/null +++ b/script.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +args=( + uv + pip + compile + "--generate-hashes" + "--no-strip-extras" +) + +"${args[@]}" \ + --directory examples/bzlmod \ + requirements.in \ + --python-version=3.9 \ + --output-file requirements_lock_3_9.txt \ + --universal \ + --custom-compile-command="bazel run //examples:bzlmod_requirements_3_9.update" \ + --emit-index-url From dfe095ed7831b26165e978d796a6761608273c15 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:51:16 +0900 Subject: [PATCH 2/6] get the directory attr work --- dev/BUILD.bazel | 1 + dev/requirements.txt | 54 ++++++++++---------- python/uv/private/lock.bzl | 32 ++++++++---- python/uv/private/template/uv_lock.sh | 2 +- python/uv/private/template/uv_pip_compile.sh | 13 +++++ 5 files changed, 65 insertions(+), 37 deletions(-) diff --git a/dev/BUILD.bazel b/dev/BUILD.bazel index 91b2a9f566..423d4c84d3 100644 --- a/dev/BUILD.bazel +++ b/dev/BUILD.bazel @@ -12,6 +12,7 @@ lock( "--universal", "--upgrade", ], + directory = "dev", # NOTE @aignas 2025-08-17: here we select the lowest actively supported version so that the # requirements file is generated to be compatible with Python version 3.9 or greater. python_version = "3.9", diff --git a/dev/requirements.txt b/dev/requirements.txt index 943fce2dcf..d0a8984d51 100644 --- a/dev/requirements.txt +++ b/dev/requirements.txt @@ -5,11 +5,11 @@ absl-py==2.3.1 ; python_full_version < '3.10' \ --hash=sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9 \ --hash=sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) absl-py==2.5.0 ; python_full_version >= '3.10' \ --hash=sha256:0c996f25c0490700fadabe6351630f6111534fa0ae252cc6d2014ea3b141135f \ --hash=sha256:0f17b89f2a4eaaedc4f28c622998aa690564b3012a396a4ffad0821007fe03ba - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) alabaster==0.7.16 ; python_full_version < '3.10' \ --hash=sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65 \ --hash=sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92 @@ -187,7 +187,7 @@ jinja2==3.1.6 \ macholib==1.16.4 \ --hash=sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea \ --hash=sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362 - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) markdown-it-py==3.0.0 ; python_full_version < '3.11' \ --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb @@ -291,7 +291,7 @@ markupsafe==3.0.3 \ --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # jinja2 mdit-py-plugins==0.4.2 ; python_full_version < '3.10' \ --hash=sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636 \ @@ -308,18 +308,18 @@ mdurl==0.1.2 \ myst-parser==3.0.1 ; python_full_version < '3.10' \ --hash=sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1 \ --hash=sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87 - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) myst-parser==4.0.1 ; python_full_version == '3.10.*' \ --hash=sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4 \ --hash=sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) myst-parser==5.1.0 ; python_full_version >= '3.11' \ --hash=sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a \ --hash=sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02 - # via rules-python-dev (dev/pyproject.toml) -packaging==26.2 \ - --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ - --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 + # via rules-python-dev (pyproject.toml) +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c # via # pytest # readthedocs-sphinx-ext @@ -327,7 +327,7 @@ packaging==26.2 \ pefile==2024.8.26 \ --hash=sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632 \ --hash=sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 @@ -335,11 +335,11 @@ pluggy==1.6.0 \ pyelftools==0.32 ; python_full_version < '3.10' \ --hash=sha256:013df952a006db5e138b1edf6d8a68ecc50630adbd0d83a2d41e7f846163d738 \ --hash=sha256:6de90ee7b8263e740c8715a925382d4099b354f29ac48ea40d840cf7aa14ace5 - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) pyelftools==0.33 ; python_full_version >= '3.10' \ --hash=sha256:660d82dcbeb8e83d1702bd97f223f761625da06111c0cc988eac6b8ab0c1b61f \ --hash=sha256:f215ad5f47d3f1373a21496a6c9e0707c622840d0622f23ff7ce08678b020036 - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) pygments==2.20.0 \ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 @@ -350,23 +350,23 @@ pytest==8.4.2 ; python_full_version < '3.10' \ --hash=sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01 \ --hash=sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79 # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # pytest-bazel # pytest-mock pytest==9.1.1 ; python_full_version >= '3.10' \ --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # pytest-bazel # pytest-mock pytest-bazel==0.1.6 \ --hash=sha256:a29e80e1d67c3db801bdd4d0b6b742f2bfb48cd6841caa33401458e5c4e29c21 - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) pytest-mock==3.15.1 \ --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d \ --hash=sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) pyyaml==6.0.3 \ --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ @@ -445,7 +445,7 @@ pyyaml==6.0.3 \ readthedocs-sphinx-ext==2.2.5 \ --hash=sha256:ee5fd5b99db9f0c180b2396cbce528aa36671951b9526bb0272dbfce5517bd27 \ --hash=sha256:f8c56184ea011c972dd45a90122568587cc85b0127bc9cf064d17c68bc809daa - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) requests==2.32.5 ; python_full_version < '3.10' \ --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf @@ -470,7 +470,7 @@ sphinx==7.4.7 ; python_full_version < '3.10' \ --hash=sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe \ --hash=sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239 # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # myst-parser # sphinx-reredirects # sphinx-rtd-theme @@ -479,7 +479,7 @@ sphinx==8.1.3 ; python_full_version == '3.10.*' \ --hash=sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2 \ --hash=sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927 # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # myst-parser # sphinx-reredirects # sphinx-rtd-theme @@ -488,7 +488,7 @@ sphinx==9.0.4 ; python_full_version == '3.11.*' \ --hash=sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3 \ --hash=sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # myst-parser # sphinx-reredirects # sphinx-rtd-theme @@ -497,7 +497,7 @@ sphinx==9.1.0 ; python_full_version >= '3.12' \ --hash=sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb \ --hash=sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978 # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # myst-parser # sphinx-reredirects # sphinx-rtd-theme @@ -505,19 +505,19 @@ sphinx==9.1.0 ; python_full_version >= '3.12' \ sphinx-autodoc2==0.5.0 \ --hash=sha256:7d76044aa81d6af74447080182b6868c7eb066874edc835e8ddf810735b6565a \ --hash=sha256:e867013b1512f9d6d7e6f6799f8b537d6884462acd118ef361f3f619a60b5c9e - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) sphinx-reredirects==0.1.6 ; python_full_version < '3.11' \ --hash=sha256:c491cba545f67be9697508727818d8626626366245ae64456fe29f37e9bbea64 \ --hash=sha256:efd50c766fbc5bf40cd5148e10c00f2c00d143027de5c5e48beece93cc40eeea - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) sphinx-reredirects==1.1.0 ; python_full_version >= '3.11' \ --hash=sha256:4b5692273c72cd2d4d917f4c6f87d5919e4d6114a752d4be033f7f5f6310efd9 \ --hash=sha256:fb9b195335ab14b43f8273287d0c7eeb637ba6c56c66581c11b47202f6718b29 - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) sphinx-rtd-theme==3.1.0 \ --hash=sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89 \ --hash=sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c - # via rules-python-dev (dev/pyproject.toml) + # via rules-python-dev (pyproject.toml) sphinxcontrib-applehelp==2.0.0 \ --hash=sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1 \ --hash=sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5 @@ -602,7 +602,7 @@ typing-extensions==4.16.0 \ --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 # via - # rules-python-dev (dev/pyproject.toml) + # rules-python-dev (pyproject.toml) # astroid # exceptiongroup # sphinx-autodoc2 diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index f1bb9ca8cf..0ec5d21d9c 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -78,7 +78,7 @@ def _reroot(x, directory): if hasattr(x, "short_path"): x = x.short_path - return x[len(directory)+1:] or "." + return x[len(directory) + 1:] or "." def _reroot_all(xs, directory): return [ @@ -86,14 +86,16 @@ def _reroot_all(xs, directory): for x in xs ] -def _up(x, directory): +def _up(x, directory, short_path = False): if not directory: return x - return "{}/{}".format( - "/".join([".."] * (len(directory.split("/"))+0)), - getattr(x, "short_path", x), - ) + prefix = "/".join([".."] * (len(directory.split("/")))) + + if short_path: + return "{}/{}".format(prefix, x.short_path) + else: + return "{}/{}".format(prefix, x.path) def _common_lock(ctx, locker): fname = "{}.out".format(ctx.label.name) @@ -126,6 +128,8 @@ def _common_lock(ctx, locker): "--no-python-downloads", "--no-cache", ]) + rootdir = "{}/{}".format(ctx.label.package, ctx.label.name) + workdir = rootdir project = None if ctx.attr.project: @@ -142,6 +146,9 @@ def _common_lock(ctx, locker): project = src.dirname directory = getattr(ctx.attr, "directory", None) + if directory: + workdir = "{}/{}".format(workdir, directory) + if project == None: project = ctx.label.package @@ -154,7 +161,10 @@ def _common_lock(ctx, locker): runtime = exec_tools.exec_interpreter[platform_common.ToolchainInfo].py3_runtime python = runtime.interpreter or runtime.interpreter_path python_files = runtime.files or depset() - args.add("--python", _up(python, directory)) + + # Handle python + args.run_shell.add("--python", _up(python, workdir)) + args.run_info.extend(["--python", _up(python, directory, short_path = True)]) # These arguments does not change behaviour, but it reduces the output from # the command, which is especially verbose in stderr. @@ -220,8 +230,10 @@ def _common_lock(ctx, locker): ctx.actions.expand_template( template = ctx.files._template[0], substitutions = { + '"$SRCS"': " ".join([s.path for s in srcs]), '"{{args}}"': windows_args, "{{out}}": output_path, + "{{rootdir}}": rootdir, "{{src_out}}": src_out_path, }, output = script, @@ -278,9 +290,9 @@ def _pip_compile_impl(ctx): args.add("--no-strip-extras") directory = ctx.attr.directory - if directory: - args.add_all([directory], before_each = "--directory") + args.run_shell.add("--directory={}/{}/{}".format(ctx.label.package, ctx.label.name, directory)) + args.run_info.append("--directory={}".format(directory)) # TODO @aignas 2026-08-04: fix the --directory handling args.add_all(_reroot_all(ctx.files.build_constraints, directory), before_each = "--build-constraints") @@ -612,6 +624,8 @@ def lock( is passed as is and the environment variables are not expanded. build_constraints: {type}`list[Label]` The list of build constraints to use. constraints: {type}`list[Label]` The list of constraints files to use. + directory: {type}`str` The directory into which we should cd when running + the command. Only supported for requirements.txt generation. generate_hashes: {type}`bool` Generate hashes for all of the requirements. Only meaningful for `requirements.txt` style output. Defaults to `True`. diff --git a/python/uv/private/template/uv_lock.sh b/python/uv/private/template/uv_lock.sh index a6dd6d3c39..2385c0f87a 100755 --- a/python/uv/private/template/uv_lock.sh +++ b/python/uv/private/template/uv_lock.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail +set -euxo pipefail if [[ -n "${BUILD_WORKSPACE_DIRECTORY:-}" ]]; then exec "{{args}}" "$@" diff --git a/python/uv/private/template/uv_pip_compile.sh b/python/uv/private/template/uv_pip_compile.sh index ba379ed0f9..983e296d48 100755 --- a/python/uv/private/template/uv_pip_compile.sh +++ b/python/uv/private/template/uv_pip_compile.sh @@ -12,4 +12,17 @@ readonly out="{{out}}" if [[ -f "{{src_out}}" ]]; then cp "{{src_out}}" "$out" fi +cp_srcs=( + "$SRCS" +) +# Copy all of the sources under a new directory, so that `directory` arg is working +# as expected and we can reroot the sources if needed. This also is a starting point +# to get the workspaces working properly in the sandbox - with this copying of sources +# we can remap the paths if needed, but that would require another attribute. +for src in "${cp_srcs[@]}"; do + # First create a dir if it does not exist + mkdir -p "$(dirname "{{rootdir}}/$src")" + # Then copy the source to the dir + cp -v "$src" "{{rootdir}}/$src" +done exec "$@" From aa1aa747192a98c89641549c01745461d34e93b0 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 10 Aug 2026 00:03:20 +0900 Subject: [PATCH 3/6] add versionadded --- python/uv/private/lock.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index 0ec5d21d9c..08aaf0841e 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -626,6 +626,7 @@ def lock( constraints: {type}`list[Label]` The list of constraints files to use. directory: {type}`str` The directory into which we should cd when running the command. Only supported for requirements.txt generation. + {versionadded} VERSION_NEXT_MINOR generate_hashes: {type}`bool` Generate hashes for all of the requirements. Only meaningful for `requirements.txt` style output. Defaults to `True`. From cf92b3a91b947f4b19e2eafc178aab582fd14b09 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 10 Aug 2026 00:35:06 +0900 Subject: [PATCH 4/6] Add directory support to uv.lock generation --- dev/BUILD.bazel | 1 + python/uv/private/lock.bzl | 21 ++++++++++----------- python/uv/private/template/uv_lock.sh | 18 +++++++++++++++--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/dev/BUILD.bazel b/dev/BUILD.bazel index 423d4c84d3..6fc70f3eb5 100644 --- a/dev/BUILD.bazel +++ b/dev/BUILD.bazel @@ -24,6 +24,7 @@ lock( name = "uv_lock", srcs = ["pyproject.toml"], out = "uv.lock", + directory = "dev", python_version = "3.9", visibility = ["//:__subpackages__"], ) diff --git a/python/uv/private/lock.bzl b/python/uv/private/lock.bzl index 08aaf0841e..8359cbe370 100644 --- a/python/uv/private/lock.bzl +++ b/python/uv/private/lock.bzl @@ -145,9 +145,11 @@ def _common_lock(ctx, locker): # select the shortest match project = src.dirname - directory = getattr(ctx.attr, "directory", None) + directory = ctx.attr.directory if directory: workdir = "{}/{}".format(workdir, directory) + args.run_shell.add("--directory={}".format(workdir)) + args.run_info.append("--directory={}".format(directory)) if project == None: project = ctx.label.package @@ -290,9 +292,6 @@ def _pip_compile_impl(ctx): args.add("--no-strip-extras") directory = ctx.attr.directory - if directory: - args.run_shell.add("--directory={}/{}/{}".format(ctx.label.package, ctx.label.name, directory)) - args.run_info.append("--directory={}".format(directory)) # TODO @aignas 2026-08-04: fix the --directory handling args.add_all(_reroot_all(ctx.files.build_constraints, directory), before_each = "--build-constraints") @@ -327,6 +326,12 @@ _common_attrs = { "args": attr.string_list( doc = "Public, see the docs in the macro.", ), + "directory": attr.string( + doc = """\ +Sets the --directory flag if provided. Will fail if at least one of the files +does not start with the given prefix of the directory. +""", + ), "env": attr.string_dict( doc = "Public, see the docs in the macro.", ), @@ -385,12 +390,6 @@ Added the {attr}`project` to configure the project setting if autodetection fail allow_files = True, doc = "Public, see the docs in the macro.", ), - "directory": attr.string( - doc = """\ -Sets the --directory flag if provided. Will fail if at least one of the files -does not start with the given prefix of the directory. -""", - ), "generate_hashes": attr.bool( doc = "Public, see the docs in the macro.", default = True, @@ -625,7 +624,7 @@ def lock( build_constraints: {type}`list[Label]` The list of build constraints to use. constraints: {type}`list[Label]` The list of constraints files to use. directory: {type}`str` The directory into which we should cd when running - the command. Only supported for requirements.txt generation. + the command. {versionadded} VERSION_NEXT_MINOR generate_hashes: {type}`bool` Generate hashes for all of the requirements. Only meaningful for `requirements.txt` style output. diff --git a/python/uv/private/template/uv_lock.sh b/python/uv/private/template/uv_lock.sh index 2385c0f87a..11d6bacdf7 100755 --- a/python/uv/private/template/uv_lock.sh +++ b/python/uv/private/template/uv_lock.sh @@ -21,12 +21,24 @@ fi # 1. If the current uv.lock does not exist yet # 1. Run uv # 2. Copy the contents to out. + +# Copy all of the sources under a new directory, so that `directory` arg is working +# as expected and we can reroot the sources if needed. This also is a starting point +# to get the workspaces working properly in the sandbox - with this copying of sources +# we can remap the paths if needed, but that would require another attribute. +for src in "${cp_srcs[@]}"; do + # First create a dir if it does not exist + mkdir -p "$(dirname "{{rootdir}}/$src")" + # Then copy the source to the dir + cp -v "$src" "{{rootdir}}/$src" +done readonly out="{{out}}" if [[ -f "{{src_out}}" ]]; then cp "{{src_out}}" "$out" - rm "{{src_out}}" - ln -s "$(pwd)"/"$out" "{{src_out}}" + mkdir -p "$(dirname "{{rootdir}}/{{src_out}}")" + ln -s "$(pwd)"/"$out" "{{rootdir}}/{{src_out}}" else - ln -s "$(pwd)"/"$out" "{{src_out}}" + mkdir -p "$(dirname "{{rootdir}}/{{src_out}}")" + ln -s "$(pwd)"/"$out" "{{rootdir}}/{{src_out}}" fi exec "$@" From 3a6697f9a6ebe7a8a5088b54329462e5eb6cfc47 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 10 Aug 2026 00:35:43 +0900 Subject: [PATCH 5/6] wip uv pip compile directory support for windows --- python/uv/private/template/uv_pip_compile.bat | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/python/uv/private/template/uv_pip_compile.bat b/python/uv/private/template/uv_pip_compile.bat index 2ea1b5a44a..0b9ad59628 100755 --- a/python/uv/private/template/uv_pip_compile.bat +++ b/python/uv/private/template/uv_pip_compile.bat @@ -1,8 +1,19 @@ @echo off -if not defined BUILD_WORKSPACE_DIRECTORY goto :else +if not defined BUILD_WORKSPACE_DIRECTORY goto :build + set "out=%BUILD_WORKSPACE_DIRECTORY%\{{src_out}}" "{{args}}" --output-file "%out%" %* exit /b %ERRORLEVEL% -:else -"{{args}}" %* +:build +set "out={{out}}" +if exist "{{src_out}}" copy /Y "{{src_out}}" "%out%" >nul +set "__srcs="$SRCS"" +if defined __srcs ( + for %%s in (%__srcs%) do ( + for %%d in ("{{rootdir}}\%%s") do mkdir "%%~dpd" >nul 2>&1 + copy /Y "%%s" "{{rootdir}}\%%s" >nul + ) +) +"{{args}}" +exit /b %ERRORLEVEL% \ No newline at end of file From f190c9a655658f34d6d849b404ff74435ebc6158 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:12:56 +0900 Subject: [PATCH 6/6] remove the script --- script.sh | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100755 script.sh diff --git a/script.sh b/script.sh deleted file mode 100755 index 9163fde5cf..0000000000 --- a/script.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -args=( - uv - pip - compile - "--generate-hashes" - "--no-strip-extras" -) - -"${args[@]}" \ - --directory examples/bzlmod \ - requirements.in \ - --python-version=3.9 \ - --output-file requirements_lock_3_9.txt \ - --universal \ - --custom-compile-command="bazel run //examples:bzlmod_requirements_3_9.update" \ - --emit-index-url