From c4740c19c907af8baa45df248add696c14d830cc Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Wed, 18 Dec 2024 15:52:18 +0000 Subject: [PATCH 01/28] Initial try at bazelizing xacro Signed-off-by: Michael Carroll --- .bazelrc | 0 .bazelversion | 1 + .gitignore | 2 ++ BUILD.bazel | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ MODULE.bazel | 14 ++++++++++ build_defs.bzl | 0 6 files changed, 88 insertions(+) create mode 100644 .bazelrc create mode 100644 .bazelversion create mode 100644 BUILD.bazel create mode 100644 MODULE.bazel create mode 100644 build_defs.bzl diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 00000000..e69de29b diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 00000000..815da58b --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +7.4.1 diff --git a/.gitignore b/.gitignore index 27ffc2f1..7c1f968a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.pyc build +bazel-* +MODULE.bazel.lock diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 00000000..8cdbe327 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,71 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_test", "py_library") +load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("@rules_license//rules:license.bzl", "license") + +package( + default_applicable_licenses = [":license"], +) + +licenses(["notice"]) + +license( + name = "license", + license_kinds = [ + "@rules_license//licenses/spdx:BSD-3-Clause", + ], + license_text = "LICENSE", +) + +write_file( + name = "write_xacro_main", + # This is the same as scripts/xacro from upstream, except that we lose the + # unused shebang line and we use a filename that is not subject to import + # path conflicts. + content = ["import xacro; xacro.main()"], + out = "xacro_main.py" +) + +py_library( + name = "xacro_lib", + srcs = glob([ + "xacro/**/*.py" + ], allow_empty = False), + imports = ["."], + visibility = ["//visibility:public"], +) + + +py_binary( + name = "xacro_main", + srcs = ["xacro_main.py"], + main = "xacro_main.py", + deps = [":xacro_lib"], +) + +alias( + name = "xacro", + actual = ":xacro_main", + visibility = ["//visibility:public"], +) + + +TEST_RESOURCES = glob([ + "test/*.xacro", + "test/*.xml", + "test/*.yaml", + "test/subdir/**", + "test/robots/**", +]) + +filegroup( + name = "test_data", + srcs = TEST_RESOURCES, + data = TEST_RESOURCES, +) + +bzl_library( + name = "build_defs_bzl", + srcs = ["build_defs.bzl"], + parse_tests = False, + visibility = ["//visibility:public"], +) diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000..260bb899 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,14 @@ +# Bazel extensions for pybind11 +module( + name = "xacro", + version = "2.0.11", +) + +bazel_dep(name = "rules_license", version = "1.0.0") +bazel_dep(name = "bazel_skylib", version = "1.7.1") +bazel_dep(name = "rules_python", version = "0.40.0") + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + python_version = "3.11", +) diff --git a/build_defs.bzl b/build_defs.bzl new file mode 100644 index 00000000..e69de29b From 40ad44799f222a67c984ce9b6d10bb1599e1ade0 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Thu, 19 Dec 2024 18:17:16 +0000 Subject: [PATCH 02/28] Add bazel test support Signed-off-by: Michael Carroll --- BUILD.bazel | 30 +++++++++++++++---- MODULE.bazel | 9 +++++- requirements.in | 1 + requirements_lock.txt | 61 ++++++++++++++++++++++++++++++++++++++ test/test_xacro.py | 22 ++++++++++++-- xacro/substitution_args.py | 8 ++++- 6 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 requirements.in create mode 100644 requirements_lock.txt diff --git a/BUILD.bazel b/BUILD.bazel index 8cdbe327..ae3379a1 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,5 +1,7 @@ load("@rules_python//python:defs.bzl", "py_binary", "py_test", "py_library") +load("@rules_python//python:pip.bzl", "compile_pip_requirements") load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@rules_license//rules:license.bzl", "license") package( @@ -16,6 +18,13 @@ license( license_text = "LICENSE", ) +# This rule adds a convenient way to update the requirements file. +compile_pip_requirements( + name = "requirements", + src = "requirements.in", + requirements_txt = "requirements_lock.txt", +) + write_file( name = "write_xacro_main", # This is the same as scripts/xacro from upstream, except that we lose the @@ -32,9 +41,11 @@ py_library( ], allow_empty = False), imports = ["."], visibility = ["//visibility:public"], + deps = [ + "@pypi//pyyaml:pkg", + ] ) - py_binary( name = "xacro_main", srcs = ["xacro_main.py"], @@ -48,7 +59,6 @@ alias( visibility = ["//visibility:public"], ) - TEST_RESOURCES = glob([ "test/*.xacro", "test/*.xml", @@ -63,9 +73,19 @@ filegroup( data = TEST_RESOURCES, ) +py_test( + name = "test_xacro", + srcs = ["test/test_xacro.py"], + main = "test/test_xacro.py", + data = [":test_data"], + deps = [ + ":xacro_main", + "@rules_python//python/runfiles", + ] +) + bzl_library( - name = "build_defs_bzl", - srcs = ["build_defs.bzl"], - parse_tests = False, + name = "build_defs_bzl", + srcs = ["build_defs.bzl"], visibility = ["//visibility:public"], ) diff --git a/MODULE.bazel b/MODULE.bazel index 260bb899..a9a744d9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,4 +1,3 @@ -# Bazel extensions for pybind11 module( name = "xacro", version = "2.0.11", @@ -12,3 +11,11 @@ python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain( python_version = "3.11", ) + +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.parse( + hub_name = "pypi", + python_version = "3.11", + requirements_lock = "//:requirements_lock.txt" +) +use_repo(pip, "pypi") diff --git a/requirements.in b/requirements.in new file mode 100644 index 00000000..c3726e8b --- /dev/null +++ b/requirements.in @@ -0,0 +1 @@ +pyyaml diff --git a/requirements_lock.txt b/requirements_lock.txt new file mode 100644 index 00000000..40dc20e3 --- /dev/null +++ b/requirements_lock.txt @@ -0,0 +1,61 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //:requirements.update +# +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via -r requirements.in diff --git a/test/test_xacro.py b/test/test_xacro.py index 60087e26..6c84557b 100644 --- a/test/test_xacro.py +++ b/test/test_xacro.py @@ -57,6 +57,16 @@ def subTest(msg): yield None +# Determine if we are running the test under bazel and switch directory +try: + from python.runfiles import runfiles + data_path = runfiles.Create().Rlocation("_main/test") + os.chdir(data_path) + XACRO_EXECUTABLE = runfiles.Create().Rlocation("_main/xacro_main") + BAZEL_TEST = True +except ImportError: + XACRO_EXECUTABLE = 'xacro' + # regex to match whitespace whitespace = re.compile(r'\s+') @@ -383,7 +393,7 @@ def quick_xacro(self, xml, cli=None, **kwargs): def run_xacro(self, input_path, *args): args = list(args) - subprocess.call(['xacro', input_path] + args) + subprocess.call([XACRO_EXECUTABLE, input_path] + args) # class to match XML docs while ignoring any comments @@ -393,14 +403,18 @@ def __init__(self, *args, **kwargs): self.ignore_nodes = [xml.dom.Node.COMMENT_NODE] def test_pr2(self): + if BAZEL_TEST: + # This has an unspecified test dependency on ament_index_python + return + # run xacro on the pr2 tree snapshot test_dir = os.path.abspath(os.path.dirname(__file__)) pr2_xacro_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2.urdf.xacro') pr2_golden_parse_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml') self.assert_matches( xml.dom.minidom.parse(pr2_golden_parse_path), - self.quick_xacro(open(pr2_xacro_path))) + self.quick_xacro(open(pr2_xacro_path))) # standard test class (including the test from TestXacroCommentsIgnored) class TestXacro(TestXacroCommentsIgnored): @@ -552,6 +566,10 @@ def test_math_ignores_spaces(self): self.assert_matches(self.quick_xacro(src), '''''') def test_substitution_args_find(self): + if BAZEL_TEST: + # Bazel implementation does not have $(find) + return + resolved = self.quick_xacro('''$(find xacro)/test/test_xacro.py''').firstChild.firstChild.data self.assertEqual(os.path.realpath(resolved), os.path.realpath(__file__)) diff --git a/xacro/substitution_args.py b/xacro/substitution_args.py index 789f312a..1d5d1717 100644 --- a/xacro/substitution_args.py +++ b/xacro/substitution_args.py @@ -137,7 +137,13 @@ def _dirname(resolved, a, args, context): def _eval_find(pkg): - from ament_index_python.packages import get_package_share_directory + try: + from ament_index_python.packages import get_package_share_directory + except ModuleNotFoundError: + raise SubstitutionException( + '$(find pkg) requires ament_index_python, but it was not found' + ) + return get_package_share_directory(pkg) From 2bc9bc2fd76b3ecfa38fdc2c54c4a5cd16a5dda6 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Thu, 19 Dec 2024 18:51:37 +0000 Subject: [PATCH 03/28] Fix test Signed-off-by: Michael Carroll --- test/test_xacro.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/test_xacro.py b/test/test_xacro.py index 6c84557b..f2ac7dde 100644 --- a/test/test_xacro.py +++ b/test/test_xacro.py @@ -57,20 +57,23 @@ def subTest(msg): yield None -# Determine if we are running the test under bazel and switch directory try: + # Determine if we are running the test under bazel and switch to runfiles directory from python.runfiles import runfiles + data_path = runfiles.Create().Rlocation("_main/test") os.chdir(data_path) + + # Set the executable path XACRO_EXECUTABLE = runfiles.Create().Rlocation("_main/xacro_main") BAZEL_TEST = True except ImportError: XACRO_EXECUTABLE = 'xacro' + BAZEL_TEST = False # regex to match whitespace whitespace = re.compile(r'\s+') - def text_values_match(a, b): # generic comparison if whitespace.sub(' ', a).strip() == whitespace.sub(' ', b).strip(): From f6ee8d12b9d64a1d2beeb05e150979af53b9248f Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Thu, 19 Dec 2024 18:49:36 +0000 Subject: [PATCH 04/28] Add bazel CI workflow Signed-off-by: Michael Carroll --- .bazelignore | 1 + .github/workflows/bazel.yml | 24 ++++++++++++++++++++++++ .github/workflows/ci.bazelrc | 18 ++++++++++++++++++ test/bazel/BUILD.bazel | 1 + test/bazel/MODULE.bazel | 5 +++++ 5 files changed, 49 insertions(+) create mode 100644 .bazelignore create mode 100644 .github/workflows/bazel.yml create mode 100644 .github/workflows/ci.bazelrc create mode 100644 test/bazel/BUILD.bazel create mode 100644 test/bazel/MODULE.bazel diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 00000000..8ceb8b48 --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +test/bazel diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml new file mode 100644 index 00000000..27ea49a6 --- /dev/null +++ b/.github/workflows/bazel.yml @@ -0,0 +1,24 @@ +name: Bazel CI +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +jobs: + test: + uses: bazel-contrib/.github/.github/workflows/bazel.yaml@v7 + with: + folders: | + [ + ".", + "test/bazel", + ] + exclude: | + [ + {"folder": ".", "bzlmodEnabled": false}, + {"folder": "test/bazel", "bzlmodEnabled": false}, + ] diff --git a/.github/workflows/ci.bazelrc b/.github/workflows/ci.bazelrc new file mode 100644 index 00000000..cb2a9b31 --- /dev/null +++ b/.github/workflows/ci.bazelrc @@ -0,0 +1,18 @@ +# This file contains Bazel settings to apply on CI only. +# It is referenced with a --bazelrc option in the call to bazel in ci.yaml + +# Debug where options came from +build --announce_rc + +# This directory is configured in GitHub actions to be persisted between runs. +# We do not enable the repository cache to cache downloaded external artifacts +# as these are generally faster to download again than to fetch them from the +# GitHub actions cache. +build --disk_cache=~/.cache/bazel + +# Don't rely on test logs being easily accessible from the test runner, +# though it makes the log noisier. +test --test_output=errors + +# Allows tests to run bazelisk-in-bazel, since this is the cache folder used +test --test_env=XDG_CACHE_HOME diff --git a/test/bazel/BUILD.bazel b/test/bazel/BUILD.bazel new file mode 100644 index 00000000..a586d848 --- /dev/null +++ b/test/bazel/BUILD.bazel @@ -0,0 +1 @@ +load("@xacro//:build_defs.bzl", "xacro") diff --git a/test/bazel/MODULE.bazel b/test/bazel/MODULE.bazel new file mode 100644 index 00000000..0646ffcd --- /dev/null +++ b/test/bazel/MODULE.bazel @@ -0,0 +1,5 @@ +bazel_dep(name = "xacro") +local_path_override( + module_name = "xacro", + path = "../..", +) From 3aea25b37bc8d3d3d48422bc039a96b65ce6a7b2 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Thu, 19 Dec 2024 18:54:21 +0000 Subject: [PATCH 05/28] Run on all PRs Signed-off-by: Michael Carroll --- .github/workflows/bazel.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 27ea49a6..3a20da63 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -1,13 +1,10 @@ name: Bazel CI -on: - push: - branches: [main] - pull_request: - branches: [main] +on: [push, pull_request, workflow_dispatch] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true + jobs: test: uses: bazel-contrib/.github/.github/workflows/bazel.yaml@v7 From 0b95e2a97a2f8b2360a827e78c8c2f5dd5c302cc Mon Sep 17 00:00:00 2001 From: Robert Haschke Date: Fri, 20 Dec 2024 09:58:31 +0100 Subject: [PATCH 06/28] Cleanup --- test/test_xacro.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/test_xacro.py b/test/test_xacro.py index f2ac7dde..2a7c89c2 100644 --- a/test/test_xacro.py +++ b/test/test_xacro.py @@ -74,6 +74,7 @@ def subTest(msg): # regex to match whitespace whitespace = re.compile(r'\s+') + def text_values_match(a, b): # generic comparison if whitespace.sub(' ', a).strip() == whitespace.sub(' ', b).strip(): @@ -405,20 +406,17 @@ def __init__(self, *args, **kwargs): super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs) self.ignore_nodes = [xml.dom.Node.COMMENT_NODE] + @unittest.skipIf(BAZEL_TEST, "Bazel build does not support $(find pkg)") def test_pr2(self): - if BAZEL_TEST: - # This has an unspecified test dependency on ament_index_python - return - # run xacro on the pr2 tree snapshot test_dir = os.path.abspath(os.path.dirname(__file__)) pr2_xacro_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2.urdf.xacro') pr2_golden_parse_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml') self.assert_matches( xml.dom.minidom.parse(pr2_golden_parse_path), - self.quick_xacro(open(pr2_xacro_path))) + # standard test class (including the test from TestXacroCommentsIgnored) class TestXacro(TestXacroCommentsIgnored): def __init__(self, *args, **kwargs): @@ -568,11 +566,8 @@ def test_math_ignores_spaces(self): src = '''''' self.assert_matches(self.quick_xacro(src), '''''') + @unittest.skipIf(BAZEL_TEST, "Bazel build does not support $(find pkg)") def test_substitution_args_find(self): - if BAZEL_TEST: - # Bazel implementation does not have $(find) - return - resolved = self.quick_xacro('''$(find xacro)/test/test_xacro.py''').firstChild.firstChild.data self.assertEqual(os.path.realpath(resolved), os.path.realpath(__file__)) From 71dbbaf4fdb1a60ca8e9426d36d3b305bc8d6934 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 15:23:41 +0000 Subject: [PATCH 07/28] Move most bazel accessory files into bazel/ Signed-off-by: Michael Carroll --- .bazelignore | 1 - BUILD.bazel | 14 -------------- MODULE.bazel | 2 +- bazel/.bazelignore | 1 + bazel/BUILD.bazel | 17 +++++++++++++++++ bazel/build_defs.bzl | 6 ++++++ requirements.in => bazel/requirements.in | 0 .../requirements_lock.txt | 4 ++-- build_defs.bzl | 0 test/bazel/BUILD.bazel | 1 - test/bazel/MODULE.bazel | 5 ----- 11 files changed, 27 insertions(+), 24 deletions(-) delete mode 100644 .bazelignore create mode 100644 bazel/.bazelignore create mode 100644 bazel/BUILD.bazel create mode 100644 bazel/build_defs.bzl rename requirements.in => bazel/requirements.in (100%) rename requirements_lock.txt => bazel/requirements_lock.txt (98%) delete mode 100644 build_defs.bzl delete mode 100644 test/bazel/BUILD.bazel delete mode 100644 test/bazel/MODULE.bazel diff --git a/.bazelignore b/.bazelignore deleted file mode 100644 index 8ceb8b48..00000000 --- a/.bazelignore +++ /dev/null @@ -1 +0,0 @@ -test/bazel diff --git a/BUILD.bazel b/BUILD.bazel index ae3379a1..78297eb2 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,7 +1,5 @@ load("@rules_python//python:defs.bzl", "py_binary", "py_test", "py_library") -load("@rules_python//python:pip.bzl", "compile_pip_requirements") load("@bazel_skylib//rules:write_file.bzl", "write_file") -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@rules_license//rules:license.bzl", "license") package( @@ -18,12 +16,6 @@ license( license_text = "LICENSE", ) -# This rule adds a convenient way to update the requirements file. -compile_pip_requirements( - name = "requirements", - src = "requirements.in", - requirements_txt = "requirements_lock.txt", -) write_file( name = "write_xacro_main", @@ -83,9 +75,3 @@ py_test( "@rules_python//python/runfiles", ] ) - -bzl_library( - name = "build_defs_bzl", - srcs = ["build_defs.bzl"], - visibility = ["//visibility:public"], -) diff --git a/MODULE.bazel b/MODULE.bazel index a9a744d9..bb393057 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,6 +16,6 @@ pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") pip.parse( hub_name = "pypi", python_version = "3.11", - requirements_lock = "//:requirements_lock.txt" + requirements_lock = "//bazel:requirements_lock.txt" ) use_repo(pip, "pypi") diff --git a/bazel/.bazelignore b/bazel/.bazelignore new file mode 100644 index 00000000..806e5f4f --- /dev/null +++ b/bazel/.bazelignore @@ -0,0 +1 @@ +integration_test diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel new file mode 100644 index 00000000..7f78597e --- /dev/null +++ b/bazel/BUILD.bazel @@ -0,0 +1,17 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("@rules_python//python:pip.bzl", "compile_pip_requirements") + +# This rule adds a convenient way to update the requirements file. +compile_pip_requirements( + name = "requirements", + src = "requirements.in", + requirements_txt = "requirements_lock.txt", +) + +bzl_library( + name = "build_defs_bzl", + srcs = ["build_defs.bzl"], + visibility = ["//visibility:public"], +) + +exports_files(["requirements.in", "requirements_lock.txt"]) diff --git a/bazel/build_defs.bzl b/bazel/build_defs.bzl new file mode 100644 index 00000000..5be25e45 --- /dev/null +++ b/bazel/build_defs.bzl @@ -0,0 +1,6 @@ +"""Provider and rules for generating xacro output at build time.""" + +XacroInfo = provider( + "Results of the xacro generation step" + fields = ["result", "data"] +) diff --git a/requirements.in b/bazel/requirements.in similarity index 100% rename from requirements.in rename to bazel/requirements.in diff --git a/requirements_lock.txt b/bazel/requirements_lock.txt similarity index 98% rename from requirements_lock.txt rename to bazel/requirements_lock.txt index 40dc20e3..ebc01d2e 100644 --- a/requirements_lock.txt +++ b/bazel/requirements_lock.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# bazel run //:requirements.update +# bazel run //bazel:requirements.update # pyyaml==6.0.2 \ --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ @@ -58,4 +58,4 @@ pyyaml==6.0.2 \ --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 - # via -r requirements.in + # via -r bazel/requirements.in diff --git a/build_defs.bzl b/build_defs.bzl deleted file mode 100644 index e69de29b..00000000 diff --git a/test/bazel/BUILD.bazel b/test/bazel/BUILD.bazel deleted file mode 100644 index a586d848..00000000 --- a/test/bazel/BUILD.bazel +++ /dev/null @@ -1 +0,0 @@ -load("@xacro//:build_defs.bzl", "xacro") diff --git a/test/bazel/MODULE.bazel b/test/bazel/MODULE.bazel deleted file mode 100644 index 0646ffcd..00000000 --- a/test/bazel/MODULE.bazel +++ /dev/null @@ -1,5 +0,0 @@ -bazel_dep(name = "xacro") -local_path_override( - module_name = "xacro", - path = "../..", -) From 9388e1bed921409b2a085774d40dea14020c838c Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 15:30:14 +0000 Subject: [PATCH 08/28] Update test location and drop CI specific bazelrc Signed-off-by: Michael Carroll --- .github/workflows/bazel.yml | 4 ++-- .github/workflows/ci.bazelrc | 18 ------------------ 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 .github/workflows/ci.bazelrc diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 3a20da63..386440a2 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -12,10 +12,10 @@ jobs: folders: | [ ".", - "test/bazel", + "bazel/integration_test", ] exclude: | [ {"folder": ".", "bzlmodEnabled": false}, - {"folder": "test/bazel", "bzlmodEnabled": false}, + {"folder": "bazel/integration_test", "bzlmodEnabled": false}, ] diff --git a/.github/workflows/ci.bazelrc b/.github/workflows/ci.bazelrc deleted file mode 100644 index cb2a9b31..00000000 --- a/.github/workflows/ci.bazelrc +++ /dev/null @@ -1,18 +0,0 @@ -# This file contains Bazel settings to apply on CI only. -# It is referenced with a --bazelrc option in the call to bazel in ci.yaml - -# Debug where options came from -build --announce_rc - -# This directory is configured in GitHub actions to be persisted between runs. -# We do not enable the repository cache to cache downloaded external artifacts -# as these are generally faster to download again than to fetch them from the -# GitHub actions cache. -build --disk_cache=~/.cache/bazel - -# Don't rely on test logs being easily accessible from the test runner, -# though it makes the log noisier. -test --test_output=errors - -# Allows tests to run bazelisk-in-bazel, since this is the cache folder used -test --test_env=XDG_CACHE_HOME From d48c5c6022835a5025e21c34902b47262156eb16 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 15:31:12 +0000 Subject: [PATCH 09/28] Remove root bazelrc Signed-off-by: Michael Carroll --- .bazelrc | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .bazelrc diff --git a/.bazelrc b/.bazelrc deleted file mode 100644 index e69de29b..00000000 From ad38049bc4f570b7872ac1679dca82d30d29d8bc Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 16:59:57 +0000 Subject: [PATCH 10/28] Add bazel genrule test Signed-off-by: Michael Carroll --- BUILD.bazel | 2 +- bazel/BUILD.bazel | 4 +- bazel/README.md | 52 ++++++++++ bazel/build_defs.bzl | 6 -- bazel/defs.bzl | 97 +++++++++++++++++++ bazel/integration_test/.bazelversion | 1 + bazel/integration_test/BUILD.bazel | 64 ++++++++++++ bazel/integration_test/MODULE.bazel | 7 ++ bazel/integration_test/box.xml | 4 + bazel/integration_test/complex.xml.xacro | 8 ++ bazel/integration_test/conditional.xml.xacro | 7 ++ .../expected/conditional_default.xml | 8 ++ .../expected/conditional_false.xml | 7 ++ .../expected/my_complex_model.xml | 10 ++ bazel/integration_test/expected/sample1.xml | 8 ++ bazel/integration_test/expected/sample2.xml | 9 ++ bazel/integration_test/sample1.xml.xacro | 5 + bazel/integration_test/sample2.xml.xacro | 6 ++ xacro/__init__.py | 17 +++- xacro/bazel_support.py | 46 +++++++++ 20 files changed, 356 insertions(+), 12 deletions(-) create mode 100644 bazel/README.md delete mode 100644 bazel/build_defs.bzl create mode 100644 bazel/defs.bzl create mode 120000 bazel/integration_test/.bazelversion create mode 100644 bazel/integration_test/BUILD.bazel create mode 100644 bazel/integration_test/MODULE.bazel create mode 100644 bazel/integration_test/box.xml create mode 100644 bazel/integration_test/complex.xml.xacro create mode 100644 bazel/integration_test/conditional.xml.xacro create mode 100644 bazel/integration_test/expected/conditional_default.xml create mode 100644 bazel/integration_test/expected/conditional_false.xml create mode 100644 bazel/integration_test/expected/my_complex_model.xml create mode 100644 bazel/integration_test/expected/sample1.xml create mode 100644 bazel/integration_test/expected/sample2.xml create mode 100644 bazel/integration_test/sample1.xml.xacro create mode 100644 bazel/integration_test/sample2.xml.xacro create mode 100644 xacro/bazel_support.py diff --git a/BUILD.bazel b/BUILD.bazel index 78297eb2..e6079a6a 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -16,7 +16,6 @@ license( license_text = "LICENSE", ) - write_file( name = "write_xacro_main", # This is the same as scripts/xacro from upstream, except that we lose the @@ -35,6 +34,7 @@ py_library( visibility = ["//visibility:public"], deps = [ "@pypi//pyyaml:pkg", + "@rules_python//python/runfiles", ] ) diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index 7f78597e..31def0f3 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -9,8 +9,8 @@ compile_pip_requirements( ) bzl_library( - name = "build_defs_bzl", - srcs = ["build_defs.bzl"], + name = "defs", + srcs = ["defs.bzl"], visibility = ["//visibility:public"], ) diff --git a/bazel/README.md b/bazel/README.md new file mode 100644 index 00000000..9faf5948 --- /dev/null +++ b/bazel/README.md @@ -0,0 +1,52 @@ +# Bazel + +This directory contains support files and tests for the bazel build system. + +In addition to supporting building xacro with bazel, this also introduces two rules for build-time generation of xacro files. + +## xacro_file + +Allows you to transform a single xacro file into a generated output + +A simple example: + +``` +load("@xacro//bazel:defs.bzl", "xacro_file") + +# By default, will transform input filename with .xacro removed +xacro_file( + name = "sample1", + src = "sample1.xml.xacro", +) +``` + + +A more complex example: + +``` +load("@xacro//bazel:defs.bzl", "xacro_file") + +# By default, will transform input filename with .xacro removed +xacro_file( + name = "complex_example", + src = "complex.xml.xacro", + # Override the default output file name + out = "my_complex_model.xml", + # Depend on the XML file that we generated in the previous step + deps = [":sample1"], + # Set extra substitution args via the command line + extra_args = ["special_argument:=foo"] +) +``` + +Note in the case of the more complex example, you can use bazel-specified filenames if they are specified in the `deps` field: + +``` + + + + + +``` + +## xacro_filegroup diff --git a/bazel/build_defs.bzl b/bazel/build_defs.bzl deleted file mode 100644 index 5be25e45..00000000 --- a/bazel/build_defs.bzl +++ /dev/null @@ -1,6 +0,0 @@ -"""Provider and rules for generating xacro output at build time.""" - -XacroInfo = provider( - "Results of the xacro generation step" - fields = ["result", "data"] -) diff --git a/bazel/defs.bzl b/bazel/defs.bzl new file mode 100644 index 00000000..180bcfb4 --- /dev/null +++ b/bazel/defs.bzl @@ -0,0 +1,97 @@ +"""Provider and rules for generating xacro output at build time.""" + +XacroInfo = provider( + "Provider holding the result of a xacro generation step.", + fields = ["result", "data"], +) + +def _xacro_impl(ctx): + if ctx.outputs.out: + out = ctx.outputs.out + else: + src = ctx.file.src.basename + if not src.endswith(".xacro"): + fail("xacro_file src should be named *.xacro not {}".format(src)) + out = ctx.actions.declare_file(src[:-6]) + + + # The list of arguments we pass to the script. + args = [ctx.file.src.path, "-o", out.path] + ctx.attr.extra_args + + # Action to call the script. + all_inputs = [ctx.file.src] + ctx.files.data + [dep[XacroInfo].result for dep in ctx.attr.deps] + ctx.actions.run( + inputs = all_inputs, + outputs = [out], + arguments = args, + env = {"XACRO_INPUTS": "\n".join([file.path for file in all_inputs])}, + progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path), + executable = ctx.executable._xacro, + ) + + xacro_data = depset( + direct = [out] + ctx.files.data + [d[XacroInfo].result for d in ctx.attr.deps], + transitive = [d[XacroInfo].data for d in ctx.attr.deps], + ) + + runfiles = ctx.runfiles(files = xacro_data.to_list()) + + return [ + XacroInfo(result = out, data = xacro_data), + DefaultInfo( + files = depset([out]), + data_runfiles = ctx.runfiles(files = [out]), + ) + ] + +_xacro_rule = rule( + attrs = { + "src": attr.label( + mandatory = True, + allow_single_file = True, + ), + "out": attr.output(), + "data": attr.label_list( + allow_files = True, + ), + "extra_args": attr.string_list(), + "deps": attr.label_list(providers = [XacroInfo]), + "_xacro": attr.label( + default = "@xacro//:xacro", + cfg = "host", + executable = True, + ), + }, + implementation = _xacro_impl, +) + +def xacro_file( + name, + src = None, + out = None, + data = [], + tags = [], + deps = [], + extra_args = [], + visibility = None): + """Runs xacro on a single input file, creating a single output file. + + Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro. + + Args: + name: The xml output file of this rule. + src: The single xacro input file of this rule. + out: Optional output file name + data: Optional supplemental files required by the src file. + extra_args: Optional arguments to be interpreted by xacro + """ + _xacro_rule( + name = name, + src = src, + out = out, + data = data, + tags = tags, + deps = deps, + extra_args = extra_args, + visibility = visibility, + ) diff --git a/bazel/integration_test/.bazelversion b/bazel/integration_test/.bazelversion new file mode 120000 index 00000000..96cf9496 --- /dev/null +++ b/bazel/integration_test/.bazelversion @@ -0,0 +1 @@ +../../.bazelversion \ No newline at end of file diff --git a/bazel/integration_test/BUILD.bazel b/bazel/integration_test/BUILD.bazel new file mode 100644 index 00000000..568d4647 --- /dev/null +++ b/bazel/integration_test/BUILD.bazel @@ -0,0 +1,64 @@ +load("@xacro//bazel:defs.bzl", "xacro_file") +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") + +xacro_file( + name = "sample1", + src = "sample1.xml.xacro", +) + +xacro_file( + name = "sample2", + src = "sample2.xml.xacro", + data = ["box.xml"] +) + +xacro_file( + name = "complex_example", + src = "complex.xml.xacro", + out = "my_complex_model.xml", + deps = [":sample1"], + extra_args = ["special_argument:=foo"] +) + +xacro_file( + name = "conditional_default", + src = "conditional.xml.xacro", + out = "conditional_default.xml" +) + +xacro_file( + name = "conditional_false", + src = "conditional.xml.xacro", + out = "conditional_false.xml", + extra_args = ["myarg:=false"] +) + +diff_test( + name = "sample1_test", + file1 = ":sample1", + file2 = "expected/sample1.xml" +) + +diff_test( + name = "sample2_test", + file1 = ":sample2", + file2 = "expected/sample2.xml" +) + +diff_test( + name = "complex_example_test", + file1 = ":complex_example", + file2 = "expected/my_complex_model.xml" +) + +diff_test( + name = "conditional_default_test", + file1 = ":conditional_default", + file2 = "expected/conditional_default.xml" +) + +diff_test( + name = "conditional_false_test", + file1 = ":conditional_false", + file2 = "expected/conditional_false.xml" +) diff --git a/bazel/integration_test/MODULE.bazel b/bazel/integration_test/MODULE.bazel new file mode 100644 index 00000000..4234ff91 --- /dev/null +++ b/bazel/integration_test/MODULE.bazel @@ -0,0 +1,7 @@ +bazel_dep(name = "bazel_skylib", version = "1.7.1") + +bazel_dep(name = "xacro") +local_path_override( + module_name = "xacro", + path = "../.." +) diff --git a/bazel/integration_test/box.xml b/bazel/integration_test/box.xml new file mode 100644 index 00000000..7caa3756 --- /dev/null +++ b/bazel/integration_test/box.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bazel/integration_test/complex.xml.xacro b/bazel/integration_test/complex.xml.xacro new file mode 100644 index 00000000..7030edd5 --- /dev/null +++ b/bazel/integration_test/complex.xml.xacro @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/bazel/integration_test/conditional.xml.xacro b/bazel/integration_test/conditional.xml.xacro new file mode 100644 index 00000000..3591aa33 --- /dev/null +++ b/bazel/integration_test/conditional.xml.xacro @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bazel/integration_test/expected/conditional_default.xml b/bazel/integration_test/expected/conditional_default.xml new file mode 100644 index 00000000..d6cf2e4b --- /dev/null +++ b/bazel/integration_test/expected/conditional_default.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/bazel/integration_test/expected/conditional_false.xml b/bazel/integration_test/expected/conditional_false.xml new file mode 100644 index 00000000..b8647c04 --- /dev/null +++ b/bazel/integration_test/expected/conditional_false.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bazel/integration_test/expected/my_complex_model.xml b/bazel/integration_test/expected/my_complex_model.xml new file mode 100644 index 00000000..40c042f8 --- /dev/null +++ b/bazel/integration_test/expected/my_complex_model.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/bazel/integration_test/expected/sample1.xml b/bazel/integration_test/expected/sample1.xml new file mode 100644 index 00000000..6bc50222 --- /dev/null +++ b/bazel/integration_test/expected/sample1.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/bazel/integration_test/expected/sample2.xml b/bazel/integration_test/expected/sample2.xml new file mode 100644 index 00000000..0d9b5ccb --- /dev/null +++ b/bazel/integration_test/expected/sample2.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/bazel/integration_test/sample1.xml.xacro b/bazel/integration_test/sample1.xml.xacro new file mode 100644 index 00000000..9b3aee57 --- /dev/null +++ b/bazel/integration_test/sample1.xml.xacro @@ -0,0 +1,5 @@ + + + + + diff --git a/bazel/integration_test/sample2.xml.xacro b/bazel/integration_test/sample2.xml.xacro new file mode 100644 index 00000000..994cfe0e --- /dev/null +++ b/bazel/integration_test/sample2.xml.xacro @@ -0,0 +1,6 @@ + + + + + + diff --git a/xacro/__init__.py b/xacro/__init__.py index e1eb9aa1..c09c6f28 100644 --- a/xacro/__init__.py +++ b/xacro/__init__.py @@ -46,6 +46,17 @@ from .xmlutils import opt_attrs, reqd_attrs, first_child_element, \ next_sibling_element, replace_node +try: + # Determine if we are running under bazel, + # If so, attempt to load `//` prefixed bazel paths first + + # This import will only succeed when running under bazel + from python.runfiles import runfiles + from .bazel_support import open_bazel + OPEN_IMPLEMENTATION = open_bazel +except: + # Otherwise, default to default open + OPEN_IMPLEMENTATION = open # Dictionary of substitution args substitution_args_context = {} @@ -137,7 +148,7 @@ def load_yaml(filename): raise XacroException("yaml support not available; install python-yaml") filename = abs_filename_spec(filename) - f = open(filename) + f = OPEN_IMPLEMENTATION(filename) filestack.append(filename) try: return YamlListWrapper.wrap(yaml.safe_load(f)) @@ -1018,7 +1029,7 @@ def parse(inp, filename=None): f = None if inp is None: try: - inp = f = open(filename) + inp = f = OPEN_IMPLEMENTATION(filename) except IOError as e: # do not report currently processed file as "in file ..." filestack.pop() @@ -1076,7 +1087,7 @@ def open_output(output_filename): pass try: - return open(output_filename, 'w') + return OPEN_IMPLEMENTATION(output_filename, 'w') except IOError as e: raise XacroException("Failed to open output:", exc=e) diff --git a/xacro/bazel_support.py b/xacro/bazel_support.py new file mode 100644 index 00000000..ea0fe3c5 --- /dev/null +++ b/xacro/bazel_support.py @@ -0,0 +1,46 @@ +"""Support methods for xacro. + +Since xacro needs to access the file system for its include functionality, we +need to provide an implementation for dealing with files in bazel. +""" + +import os +import errno + +def open_bazel(path, *args, **kwargs): + """Open a file. + + If path starts with '//', assume it's a bazel path and look for it in + the list of input files (in os.environ['XACRO_INPUTS']). + + Args: + path: Path of the file to open. If this starts with '//', assume it's a + bazel path + *args: passed through to open() + **kwargs: passed through to open() + + Raises: + IOError: If the requested path is not available either on the filesystem or + in bazel. + """ + if path.startswith('//'): + # Get path of file, without leading // + bazel_path = path[2:] + # Extract list of input files from environment variable + input_paths = os.environ['XACRO_INPUTS'].split('\n') + best_match = None + # Find the match that is shortest overall – this avoids selecting + # not/my/package/file.xacro when we really want my/package/file.xacro. + for input_path in input_paths: + if input_path.endswith(bazel_path): + if best_match is None or len(best_match) > len(input_path): + best_match = input_path + + if best_match is None: + raise IOError( + errno.ENOENT, + 'Unable to find bazel file. Is it or the rule that produces it an input?', + path) + path = best_match + + return open(path, *args, **kwargs) From d9188bcb788de77d26ca83327a3ec519544c69f7 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:02:38 +0000 Subject: [PATCH 11/28] Add copyright header Signed-off-by: Michael Carroll --- xacro/bazel_support.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/xacro/bazel_support.py b/xacro/bazel_support.py index ea0fe3c5..21465b4e 100644 --- a/xacro/bazel_support.py +++ b/xacro/bazel_support.py @@ -1,3 +1,31 @@ +# Copyright (c) 2024, Open Source Robotics Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the Open Source Robotics Foundation, Inc. +# nor the names of its contributors may be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + """Support methods for xacro. Since xacro needs to access the file system for its include functionality, we From 5d04b372a7347613b2312f5e0cdb4357dd423926 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:03:27 +0000 Subject: [PATCH 12/28] Undo substitution_args change, as these are skipped in bazel now Signed-off-by: Michael Carroll --- xacro/substitution_args.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/xacro/substitution_args.py b/xacro/substitution_args.py index 1d5d1717..789f312a 100644 --- a/xacro/substitution_args.py +++ b/xacro/substitution_args.py @@ -137,13 +137,7 @@ def _dirname(resolved, a, args, context): def _eval_find(pkg): - try: - from ament_index_python.packages import get_package_share_directory - except ModuleNotFoundError: - raise SubstitutionException( - '$(find pkg) requires ament_index_python, but it was not found' - ) - + from ament_index_python.packages import get_package_share_directory return get_package_share_directory(pkg) From 56426d16645adbf53e3852ac1cea3bf54d39b3c4 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:08:18 +0000 Subject: [PATCH 13/28] Move test inputs to subdirectory Signed-off-by: Michael Carroll --- .bazelignore | 1 + bazel/.bazelignore | 1 - bazel/integration_test/BUILD.bazel | 12 ++++++------ .../expected/conditional_default.xml | 2 +- .../integration_test/expected/conditional_false.xml | 2 +- bazel/integration_test/expected/my_complex_model.xml | 2 +- bazel/integration_test/expected/sample1.xml | 2 +- bazel/integration_test/expected/sample2.xml | 2 +- bazel/integration_test/{ => inputs}/box.xml | 0 .../integration_test/{ => inputs}/complex.xml.xacro | 0 .../{ => inputs}/conditional.xml.xacro | 0 .../integration_test/{ => inputs}/sample1.xml.xacro | 0 .../integration_test/{ => inputs}/sample2.xml.xacro | 0 13 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 .bazelignore delete mode 100644 bazel/.bazelignore rename bazel/integration_test/{ => inputs}/box.xml (100%) rename bazel/integration_test/{ => inputs}/complex.xml.xacro (100%) rename bazel/integration_test/{ => inputs}/conditional.xml.xacro (100%) rename bazel/integration_test/{ => inputs}/sample1.xml.xacro (100%) rename bazel/integration_test/{ => inputs}/sample2.xml.xacro (100%) diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 00000000..726996c0 --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +bazel/integration_test diff --git a/bazel/.bazelignore b/bazel/.bazelignore deleted file mode 100644 index 806e5f4f..00000000 --- a/bazel/.bazelignore +++ /dev/null @@ -1 +0,0 @@ -integration_test diff --git a/bazel/integration_test/BUILD.bazel b/bazel/integration_test/BUILD.bazel index 568d4647..68d1b667 100644 --- a/bazel/integration_test/BUILD.bazel +++ b/bazel/integration_test/BUILD.bazel @@ -3,18 +3,18 @@ load("@bazel_skylib//rules:diff_test.bzl", "diff_test") xacro_file( name = "sample1", - src = "sample1.xml.xacro", + src = "inputs/sample1.xml.xacro", ) xacro_file( name = "sample2", - src = "sample2.xml.xacro", - data = ["box.xml"] + src = "inputs/sample2.xml.xacro", + data = ["inputs/box.xml"] ) xacro_file( name = "complex_example", - src = "complex.xml.xacro", + src = "inputs/complex.xml.xacro", out = "my_complex_model.xml", deps = [":sample1"], extra_args = ["special_argument:=foo"] @@ -22,13 +22,13 @@ xacro_file( xacro_file( name = "conditional_default", - src = "conditional.xml.xacro", + src = "inputs/conditional.xml.xacro", out = "conditional_default.xml" ) xacro_file( name = "conditional_false", - src = "conditional.xml.xacro", + src = "inputs/conditional.xml.xacro", out = "conditional_false.xml", extra_args = ["myarg:=false"] ) diff --git a/bazel/integration_test/expected/conditional_default.xml b/bazel/integration_test/expected/conditional_default.xml index d6cf2e4b..77111d80 100644 --- a/bazel/integration_test/expected/conditional_default.xml +++ b/bazel/integration_test/expected/conditional_default.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/conditional_false.xml b/bazel/integration_test/expected/conditional_false.xml index b8647c04..77bebb91 100644 --- a/bazel/integration_test/expected/conditional_false.xml +++ b/bazel/integration_test/expected/conditional_false.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/my_complex_model.xml b/bazel/integration_test/expected/my_complex_model.xml index 40c042f8..2e08e2ad 100644 --- a/bazel/integration_test/expected/my_complex_model.xml +++ b/bazel/integration_test/expected/my_complex_model.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/sample1.xml b/bazel/integration_test/expected/sample1.xml index 6bc50222..da777c3a 100644 --- a/bazel/integration_test/expected/sample1.xml +++ b/bazel/integration_test/expected/sample1.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/sample2.xml b/bazel/integration_test/expected/sample2.xml index 0d9b5ccb..7bc42c5d 100644 --- a/bazel/integration_test/expected/sample2.xml +++ b/bazel/integration_test/expected/sample2.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/box.xml b/bazel/integration_test/inputs/box.xml similarity index 100% rename from bazel/integration_test/box.xml rename to bazel/integration_test/inputs/box.xml diff --git a/bazel/integration_test/complex.xml.xacro b/bazel/integration_test/inputs/complex.xml.xacro similarity index 100% rename from bazel/integration_test/complex.xml.xacro rename to bazel/integration_test/inputs/complex.xml.xacro diff --git a/bazel/integration_test/conditional.xml.xacro b/bazel/integration_test/inputs/conditional.xml.xacro similarity index 100% rename from bazel/integration_test/conditional.xml.xacro rename to bazel/integration_test/inputs/conditional.xml.xacro diff --git a/bazel/integration_test/sample1.xml.xacro b/bazel/integration_test/inputs/sample1.xml.xacro similarity index 100% rename from bazel/integration_test/sample1.xml.xacro rename to bazel/integration_test/inputs/sample1.xml.xacro diff --git a/bazel/integration_test/sample2.xml.xacro b/bazel/integration_test/inputs/sample2.xml.xacro similarity index 100% rename from bazel/integration_test/sample2.xml.xacro rename to bazel/integration_test/inputs/sample2.xml.xacro From 1457734c0d2d4534dedf0e78f977da2e6c9ccb71 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:09:33 +0000 Subject: [PATCH 14/28] Buildifier Lint Signed-off-by: Michael Carroll --- BUILD.bazel | 67 ++++++------ MODULE.bazel | 6 +- bazel/BUILD.bazel | 11 +- bazel/defs.bzl | 157 ++++++++++++++-------------- bazel/integration_test/BUILD.bazel | 66 ++++++------ bazel/integration_test/MODULE.bazel | 5 +- 6 files changed, 158 insertions(+), 154 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index e6079a6a..ccabe96d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,6 +1,6 @@ -load("@rules_python//python:defs.bzl", "py_binary", "py_test", "py_library") load("@bazel_skylib//rules:write_file.bzl", "write_file") load("@rules_license//rules:license.bzl", "license") +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") package( default_applicable_licenses = [":license"], @@ -18,31 +18,34 @@ license( write_file( name = "write_xacro_main", + out = "xacro_main.py", # This is the same as scripts/xacro from upstream, except that we lose the # unused shebang line and we use a filename that is not subject to import # path conflicts. content = ["import xacro; xacro.main()"], - out = "xacro_main.py" ) py_library( - name = "xacro_lib", - srcs = glob([ - "xacro/**/*.py" - ], allow_empty = False), - imports = ["."], - visibility = ["//visibility:public"], - deps = [ - "@pypi//pyyaml:pkg", - "@rules_python//python/runfiles", - ] + name = "xacro_lib", + srcs = glob( + [ + "xacro/**/*.py", + ], + allow_empty = False, + ), + imports = ["."], + visibility = ["//visibility:public"], + deps = [ + "@pypi//pyyaml:pkg", + "@rules_python//python/runfiles", + ], ) py_binary( - name = "xacro_main", - srcs = ["xacro_main.py"], - main = "xacro_main.py", - deps = [":xacro_lib"], + name = "xacro_main", + srcs = ["xacro_main.py"], + main = "xacro_main.py", + deps = [":xacro_lib"], ) alias( @@ -52,26 +55,26 @@ alias( ) TEST_RESOURCES = glob([ - "test/*.xacro", - "test/*.xml", - "test/*.yaml", - "test/subdir/**", - "test/robots/**", + "test/*.xacro", + "test/*.xml", + "test/*.yaml", + "test/subdir/**", + "test/robots/**", ]) filegroup( - name = "test_data", - srcs = TEST_RESOURCES, - data = TEST_RESOURCES, + name = "test_data", + srcs = TEST_RESOURCES, + data = TEST_RESOURCES, ) py_test( - name = "test_xacro", - srcs = ["test/test_xacro.py"], - main = "test/test_xacro.py", - data = [":test_data"], - deps = [ - ":xacro_main", - "@rules_python//python/runfiles", - ] + name = "test_xacro", + srcs = ["test/test_xacro.py"], + data = [":test_data"], + main = "test/test_xacro.py", + deps = [ + ":xacro_main", + "@rules_python//python/runfiles", + ], ) diff --git a/MODULE.bazel b/MODULE.bazel index bb393057..49c20ea9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,8 +14,8 @@ python.toolchain( pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") pip.parse( - hub_name = "pypi", - python_version = "3.11", - requirements_lock = "//bazel:requirements_lock.txt" + hub_name = "pypi", + python_version = "3.11", + requirements_lock = "//bazel:requirements_lock.txt", ) use_repo(pip, "pypi") diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index 31def0f3..183ba449 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -9,9 +9,12 @@ compile_pip_requirements( ) bzl_library( - name = "defs", - srcs = ["defs.bzl"], - visibility = ["//visibility:public"], + name = "defs", + srcs = ["defs.bzl"], + visibility = ["//visibility:public"], ) -exports_files(["requirements.in", "requirements_lock.txt"]) +exports_files([ + "requirements.in", + "requirements_lock.txt", +]) diff --git a/bazel/defs.bzl b/bazel/defs.bzl index 180bcfb4..1364a665 100644 --- a/bazel/defs.bzl +++ b/bazel/defs.bzl @@ -1,97 +1,96 @@ """Provider and rules for generating xacro output at build time.""" XacroInfo = provider( - "Provider holding the result of a xacro generation step.", - fields = ["result", "data"], + "Provider holding the result of a xacro generation step.", + fields = ["result", "data"], ) def _xacro_impl(ctx): - if ctx.outputs.out: - out = ctx.outputs.out - else: - src = ctx.file.src.basename - if not src.endswith(".xacro"): - fail("xacro_file src should be named *.xacro not {}".format(src)) - out = ctx.actions.declare_file(src[:-6]) + if ctx.outputs.out: + out = ctx.outputs.out + else: + src = ctx.file.src.basename + if not src.endswith(".xacro"): + fail("xacro_file src should be named *.xacro not {}".format(src)) + out = ctx.actions.declare_file(src[:-6]) + # The list of arguments we pass to the script. + args = [ctx.file.src.path, "-o", out.path] + ctx.attr.extra_args - # The list of arguments we pass to the script. - args = [ctx.file.src.path, "-o", out.path] + ctx.attr.extra_args + # Action to call the script. + all_inputs = [ctx.file.src] + ctx.files.data + [dep[XacroInfo].result for dep in ctx.attr.deps] + ctx.actions.run( + inputs = all_inputs, + outputs = [out], + arguments = args, + env = {"XACRO_INPUTS": "\n".join([file.path for file in all_inputs])}, + progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path), + executable = ctx.executable._xacro, + ) - # Action to call the script. - all_inputs = [ctx.file.src] + ctx.files.data + [dep[XacroInfo].result for dep in ctx.attr.deps] - ctx.actions.run( - inputs = all_inputs, - outputs = [out], - arguments = args, - env = {"XACRO_INPUTS": "\n".join([file.path for file in all_inputs])}, - progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path), - executable = ctx.executable._xacro, - ) + xacro_data = depset( + direct = [out] + ctx.files.data + [d[XacroInfo].result for d in ctx.attr.deps], + transitive = [d[XacroInfo].data for d in ctx.attr.deps], + ) - xacro_data = depset( - direct = [out] + ctx.files.data + [d[XacroInfo].result for d in ctx.attr.deps], - transitive = [d[XacroInfo].data for d in ctx.attr.deps], - ) + runfiles = ctx.runfiles(files = xacro_data.to_list()) - runfiles = ctx.runfiles(files = xacro_data.to_list()) - - return [ - XacroInfo(result = out, data = xacro_data), - DefaultInfo( - files = depset([out]), - data_runfiles = ctx.runfiles(files = [out]), - ) - ] + return [ + XacroInfo(result = out, data = xacro_data), + DefaultInfo( + files = depset([out]), + data_runfiles = ctx.runfiles(files = [out]), + ), + ] _xacro_rule = rule( - attrs = { - "src": attr.label( - mandatory = True, - allow_single_file = True, - ), - "out": attr.output(), - "data": attr.label_list( - allow_files = True, - ), - "extra_args": attr.string_list(), - "deps": attr.label_list(providers = [XacroInfo]), - "_xacro": attr.label( - default = "@xacro//:xacro", - cfg = "host", - executable = True, - ), - }, - implementation = _xacro_impl, + attrs = { + "src": attr.label( + mandatory = True, + allow_single_file = True, + ), + "out": attr.output(), + "data": attr.label_list( + allow_files = True, + ), + "extra_args": attr.string_list(), + "deps": attr.label_list(providers = [XacroInfo]), + "_xacro": attr.label( + default = "@xacro//:xacro", + cfg = "host", + executable = True, + ), + }, + implementation = _xacro_impl, ) def xacro_file( - name, - src = None, - out = None, - data = [], - tags = [], - deps = [], - extra_args = [], - visibility = None): - """Runs xacro on a single input file, creating a single output file. + name, + src = None, + out = None, + data = [], + tags = [], + deps = [], + extra_args = [], + visibility = None): + """Runs xacro on a single input file, creating a single output file. - Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro. + Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro. - Args: - name: The xml output file of this rule. - src: The single xacro input file of this rule. - out: Optional output file name - data: Optional supplemental files required by the src file. - extra_args: Optional arguments to be interpreted by xacro - """ - _xacro_rule( - name = name, - src = src, - out = out, - data = data, - tags = tags, - deps = deps, - extra_args = extra_args, - visibility = visibility, - ) + Args: + name: The xml output file of this rule. + src: The single xacro input file of this rule. + out: Optional output file name + data: Optional supplemental files required by the src file. + extra_args: Optional arguments to be interpreted by xacro + """ + _xacro_rule( + name = name, + src = src, + out = out, + data = data, + tags = tags, + deps = deps, + extra_args = extra_args, + visibility = visibility, + ) diff --git a/bazel/integration_test/BUILD.bazel b/bazel/integration_test/BUILD.bazel index 68d1b667..f657fc37 100644 --- a/bazel/integration_test/BUILD.bazel +++ b/bazel/integration_test/BUILD.bazel @@ -1,64 +1,64 @@ -load("@xacro//bazel:defs.bzl", "xacro_file") load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load("@xacro//bazel:defs.bzl", "xacro_file") xacro_file( - name = "sample1", - src = "inputs/sample1.xml.xacro", + name = "sample1", + src = "inputs/sample1.xml.xacro", ) xacro_file( - name = "sample2", - src = "inputs/sample2.xml.xacro", - data = ["inputs/box.xml"] + name = "sample2", + src = "inputs/sample2.xml.xacro", + data = ["inputs/box.xml"], ) xacro_file( - name = "complex_example", - src = "inputs/complex.xml.xacro", - out = "my_complex_model.xml", - deps = [":sample1"], - extra_args = ["special_argument:=foo"] + name = "complex_example", + src = "inputs/complex.xml.xacro", + out = "my_complex_model.xml", + extra_args = ["special_argument:=foo"], + deps = [":sample1"], ) xacro_file( - name = "conditional_default", - src = "inputs/conditional.xml.xacro", - out = "conditional_default.xml" + name = "conditional_default", + src = "inputs/conditional.xml.xacro", + out = "conditional_default.xml", ) xacro_file( - name = "conditional_false", - src = "inputs/conditional.xml.xacro", - out = "conditional_false.xml", - extra_args = ["myarg:=false"] + name = "conditional_false", + src = "inputs/conditional.xml.xacro", + out = "conditional_false.xml", + extra_args = ["myarg:=false"], ) diff_test( - name = "sample1_test", - file1 = ":sample1", - file2 = "expected/sample1.xml" + name = "sample1_test", + file1 = ":sample1", + file2 = "expected/sample1.xml", ) diff_test( - name = "sample2_test", - file1 = ":sample2", - file2 = "expected/sample2.xml" + name = "sample2_test", + file1 = ":sample2", + file2 = "expected/sample2.xml", ) diff_test( - name = "complex_example_test", - file1 = ":complex_example", - file2 = "expected/my_complex_model.xml" + name = "complex_example_test", + file1 = ":complex_example", + file2 = "expected/my_complex_model.xml", ) diff_test( - name = "conditional_default_test", - file1 = ":conditional_default", - file2 = "expected/conditional_default.xml" + name = "conditional_default_test", + file1 = ":conditional_default", + file2 = "expected/conditional_default.xml", ) diff_test( - name = "conditional_false_test", - file1 = ":conditional_false", - file2 = "expected/conditional_false.xml" + name = "conditional_false_test", + file1 = ":conditional_false", + file2 = "expected/conditional_false.xml", ) diff --git a/bazel/integration_test/MODULE.bazel b/bazel/integration_test/MODULE.bazel index 4234ff91..0d7e621a 100644 --- a/bazel/integration_test/MODULE.bazel +++ b/bazel/integration_test/MODULE.bazel @@ -1,7 +1,6 @@ bazel_dep(name = "bazel_skylib", version = "1.7.1") - bazel_dep(name = "xacro") local_path_override( - module_name = "xacro", - path = "../.." + module_name = "xacro", + path = "../..", ) From 6602ced1bca6c1cb0d0c68e6bbcd289595497372 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:10:57 +0000 Subject: [PATCH 15/28] Trim trailing whitespace Signed-off-by: Michael Carroll --- bazel/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazel/README.md b/bazel/README.md index 9faf5948..c414e7be 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -34,7 +34,7 @@ xacro_file( out = "my_complex_model.xml", # Depend on the XML file that we generated in the previous step deps = [":sample1"], - # Set extra substitution args via the command line + # Set extra substitution args via the command line extra_args = ["special_argument:=foo"] ) ``` From 234809800b00629992ff06837de2e0f6c7bac12b Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:20:36 +0000 Subject: [PATCH 16/28] Add filegroup rule Signed-off-by: Michael Carroll --- bazel/README.md | 16 +++++++++++++++- bazel/defs.bzl | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/bazel/README.md b/bazel/README.md index c414e7be..ec41e864 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -20,7 +20,6 @@ xacro_file( ) ``` - A more complex example: ``` @@ -50,3 +49,18 @@ Note in the case of the more complex example, you can use bazel-specified filena ``` ## xacro_filegroup + +Allows you to transform multiple xacro files into a generated filegroup + +``` +xacro_filegroup( + name = "samples", + srcs = [ + "sample1.xml.xacro", + "sample2.xml.xacro", + ], + data = [ + "box.xml", + ], +) +``` diff --git a/bazel/defs.bzl b/bazel/defs.bzl index 1364a665..ce0a662a 100644 --- a/bazel/defs.bzl +++ b/bazel/defs.bzl @@ -94,3 +94,42 @@ def xacro_file( extra_args = extra_args, visibility = visibility, ) + +def xacro_filegroup( + name, + srcs = [], + data = [], + tags = [], + visibility = None): + """Runs xacro on several input files, creating a filegroup of the output. + + The output filenames will match the input filenames but with the ".xacro" + suffix removed. + + Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro. + + Args: + name: The name of the filegroup label. + srcs: The xacro input files of this rule. + data: Optional supplemental files required by the srcs. + """ + outs = [] + for src in srcs: + if not src.endswith(".xacro"): + fail("xacro_filegroup srcs should be named *.xacro not {}".format( + src, + )) + out = src[:-6] + outs.append(out) + xacro_file( + name = out, + src = src, + data = data, + tags = tags, + visibility = ["//visibility:private"], + ) + native.filegroup( + name = name, + srcs = outs, + visibility = visibility, + ) From 75316675a67d45931a6d3d8208756097ce2f2fe8 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 17:58:20 +0000 Subject: [PATCH 17/28] Address reviewer feedback: * Remove src glob * Add provides * Trim extension based on string length * Directly expose xacro_file rule Signed-off-by: Michael Carroll --- BUILD.bazel | 14 ++++++++------ bazel/defs.bzl | 44 ++++++++------------------------------------ 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index ccabe96d..88fc9a9c 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -27,12 +27,14 @@ write_file( py_library( name = "xacro_lib", - srcs = glob( - [ - "xacro/**/*.py", - ], - allow_empty = False, - ), + srcs = [ + "xacro/__init__.py", + "xacro/bazel_support.py", + "xacro/cli.py", + "xacro/color.py", + "xacro/substitution_args.py", + "xacro/xmlutils.py", + ], imports = ["."], visibility = ["//visibility:public"], deps = [ diff --git a/bazel/defs.bzl b/bazel/defs.bzl index ce0a662a..41e62051 100644 --- a/bazel/defs.bzl +++ b/bazel/defs.bzl @@ -5,14 +5,16 @@ XacroInfo = provider( fields = ["result", "data"], ) +XACRO_EXTENSION = ".xacro" + def _xacro_impl(ctx): if ctx.outputs.out: out = ctx.outputs.out else: src = ctx.file.src.basename - if not src.endswith(".xacro"): + if not src.endswith(XACRO_EXTENSION): fail("xacro_file src should be named *.xacro not {}".format(src)) - out = ctx.actions.declare_file(src[:-6]) + out = ctx.actions.declare_file(src[:-len(XACRO_EXTENSION)]) # The list of arguments we pass to the script. args = [ctx.file.src.path, "-o", out.path] + ctx.attr.extra_args @@ -43,7 +45,7 @@ def _xacro_impl(ctx): ), ] -_xacro_rule = rule( +xacro_file = rule( attrs = { "src": attr.label( mandatory = True, @@ -62,39 +64,9 @@ _xacro_rule = rule( ), }, implementation = _xacro_impl, + provides = [XacroInfo, DefaultInfo], ) -def xacro_file( - name, - src = None, - out = None, - data = [], - tags = [], - deps = [], - extra_args = [], - visibility = None): - """Runs xacro on a single input file, creating a single output file. - - Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro. - - Args: - name: The xml output file of this rule. - src: The single xacro input file of this rule. - out: Optional output file name - data: Optional supplemental files required by the src file. - extra_args: Optional arguments to be interpreted by xacro - """ - _xacro_rule( - name = name, - src = src, - out = out, - data = data, - tags = tags, - deps = deps, - extra_args = extra_args, - visibility = visibility, - ) - def xacro_filegroup( name, srcs = [], @@ -115,11 +87,11 @@ def xacro_filegroup( """ outs = [] for src in srcs: - if not src.endswith(".xacro"): + if not src.endswith(XACRO_EXTENSION): fail("xacro_filegroup srcs should be named *.xacro not {}".format( src, )) - out = src[:-6] + out = src[:-len(XACRO_EXTENSION)] outs.append(out) xacro_file( name = out, From b3af5667f90b90e022b23ffb093769b0d4ab1240 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 12:02:06 -0600 Subject: [PATCH 18/28] Make python dependency repo name unique. Co-authored-by: Marcel --- MODULE.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 49c20ea9..f833884a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,8 +14,8 @@ python.toolchain( pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") pip.parse( - hub_name = "pypi", + hub_name = "xacro_python_dependencies", python_version = "3.11", requirements_lock = "//bazel:requirements_lock.txt", ) -use_repo(pip, "pypi") +use_repo(pip, "xacro_python_dependencies") From a21a02e0bc2718cd15b32abee7f92f3f5084beea Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 18:02:55 +0000 Subject: [PATCH 19/28] Change build dependency to reflect repo rename Signed-off-by: Michael Carroll --- BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 88fc9a9c..9b61f8c0 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -38,7 +38,7 @@ py_library( imports = ["."], visibility = ["//visibility:public"], deps = [ - "@pypi//pyyaml:pkg", + "@xacro_python_dependencies//pyyaml:pkg", "@rules_python//python/runfiles", ], ) From a6d30bec1019f11f1d8c17d95dac663f1c41a9e4 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 18:05:21 +0000 Subject: [PATCH 20/28] Add support for multiple python versions Signed-off-by: Michael Carroll --- MODULE.bazel | 34 ++++++++++++++++++++++++++-------- bazel/requirements_lock.txt | 2 +- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index f833884a..1bde8701 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -7,15 +7,33 @@ bazel_dep(name = "rules_license", version = "1.0.0") bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "rules_python", version = "0.40.0") +PYTHON_VERSIONS = [ + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", +] + python = use_extension("@rules_python//python/extensions:python.bzl", "python") -python.toolchain( - python_version = "3.11", -) + +[ + python.toolchain( + is_default = python_version == PYTHON_VERSIONS[-1], + python_version = python_version, + ) + for python_version in PYTHON_VERSIONS +] pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") -pip.parse( - hub_name = "xacro_python_dependencies", - python_version = "3.11", - requirements_lock = "//bazel:requirements_lock.txt", -) + +[ + pip.parse( + hub_name = "xacro_python_dependencies", + python_version = python_version, + requirements_lock = "//bazel:requirements_lock.txt", + ) + for python_version in PYTHON_VERSIONS +] + use_repo(pip, "xacro_python_dependencies") diff --git a/bazel/requirements_lock.txt b/bazel/requirements_lock.txt index ebc01d2e..b12b0562 100644 --- a/bazel/requirements_lock.txt +++ b/bazel/requirements_lock.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.12 # by the following command: # # bazel run //bazel:requirements.update From c739c42bbc8e1dd9147bdbe81b6a79dc758659dc Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 20 Dec 2024 18:12:09 +0000 Subject: [PATCH 21/28] Remove concurrency constraint and add comment Signed-off-by: Michael Carroll --- .github/workflows/bazel.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 386440a2..8dce3bdc 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -1,9 +1,6 @@ name: Bazel CI -on: [push, pull_request, workflow_dispatch] -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true +on: [push, pull_request, workflow_dispatch] jobs: test: @@ -14,6 +11,9 @@ jobs: ".", "bazel/integration_test", ] + # Explicitly exclude build/test configurations where bzlmod is disabled. + # Since xacro only supports bzlmod, these will always fail. + # Remove these exclusions when workspace support is dropped. exclude: | [ {"folder": ".", "bzlmodEnabled": false}, From 747b8331bb4b37acb5a195c19cb8752194f5312b Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Wed, 8 Jan 2025 20:50:31 +0000 Subject: [PATCH 22/28] Remove bazel specific find semantics Signed-off-by: Michael Carroll --- xacro/__init__.py | 19 ++--------- xacro/bazel_support.py | 74 ------------------------------------------ 2 files changed, 3 insertions(+), 90 deletions(-) delete mode 100644 xacro/bazel_support.py diff --git a/xacro/__init__.py b/xacro/__init__.py index 535dee30..7a73e17c 100644 --- a/xacro/__init__.py +++ b/xacro/__init__.py @@ -46,22 +46,9 @@ from .xmlutils import opt_attrs, reqd_attrs, first_child_element, \ next_sibling_element, replace_node -try: - # Determine if we are running under bazel, - # If so, attempt to load `//` prefixed bazel paths first - - # This import will only succeed when running under bazel - from python.runfiles import runfiles - from .bazel_support import open_bazel - OPEN_IMPLEMENTATION = open_bazel -except: - # Otherwise, default to default open - OPEN_IMPLEMENTATION = open - # Dictionary of substitution args substitution_args_context = {} - # Stack of currently processed files / macros filestack = None macrostack = None @@ -148,7 +135,7 @@ def load_yaml(filename): raise XacroException("yaml support not available; install python-yaml") filename = abs_filename_spec(filename) - f = OPEN_IMPLEMENTATION(filename) + f = open(filename) filestack.append(filename) try: return YamlListWrapper.wrap(yaml.safe_load(f)) @@ -1029,7 +1016,7 @@ def parse(inp, filename=None): f = None if inp is None: try: - inp = f = OPEN_IMPLEMENTATION(filename) + inp = f = open(filename) except IOError as e: # do not report currently processed file as "in file ..." filestack.pop() @@ -1087,7 +1074,7 @@ def open_output(output_filename): pass try: - return OPEN_IMPLEMENTATION(output_filename, 'w') + return open(output_filename, 'w') except IOError as e: raise XacroException("Failed to open output:", exc=e) diff --git a/xacro/bazel_support.py b/xacro/bazel_support.py deleted file mode 100644 index 21465b4e..00000000 --- a/xacro/bazel_support.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2024, Open Source Robotics Foundation, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the Open Source Robotics Foundation, Inc. -# nor the names of its contributors may be used to endorse or promote -# products derived from this software without specific prior -# written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -"""Support methods for xacro. - -Since xacro needs to access the file system for its include functionality, we -need to provide an implementation for dealing with files in bazel. -""" - -import os -import errno - -def open_bazel(path, *args, **kwargs): - """Open a file. - - If path starts with '//', assume it's a bazel path and look for it in - the list of input files (in os.environ['XACRO_INPUTS']). - - Args: - path: Path of the file to open. If this starts with '//', assume it's a - bazel path - *args: passed through to open() - **kwargs: passed through to open() - - Raises: - IOError: If the requested path is not available either on the filesystem or - in bazel. - """ - if path.startswith('//'): - # Get path of file, without leading // - bazel_path = path[2:] - # Extract list of input files from environment variable - input_paths = os.environ['XACRO_INPUTS'].split('\n') - best_match = None - # Find the match that is shortest overall – this avoids selecting - # not/my/package/file.xacro when we really want my/package/file.xacro. - for input_path in input_paths: - if input_path.endswith(bazel_path): - if best_match is None or len(best_match) > len(input_path): - best_match = input_path - - if best_match is None: - raise IOError( - errno.ENOENT, - 'Unable to find bazel file. Is it or the rule that produces it an input?', - path) - path = best_match - - return open(path, *args, **kwargs) From 5ae92bb0a20359813b0b384777ba456a7557cac7 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Tue, 14 Jan 2025 17:16:16 +0000 Subject: [PATCH 23/28] Remove bazel-specific semantics Signed-off-by: Michael Carroll --- BUILD.bazel | 3 +- bazel/defs.bzl | 59 +++++++++++-------- bazel/integration_test/BUILD.bazel | 4 +- .../integration_test/inputs/complex.xml.xacro | 2 +- xacro/__init__.py | 10 +++- xacro/cli.py | 2 + 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 9b61f8c0..3fc69230 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -29,7 +29,6 @@ py_library( name = "xacro_lib", srcs = [ "xacro/__init__.py", - "xacro/bazel_support.py", "xacro/cli.py", "xacro/color.py", "xacro/substitution_args.py", @@ -38,8 +37,8 @@ py_library( imports = ["."], visibility = ["//visibility:public"], deps = [ - "@xacro_python_dependencies//pyyaml:pkg", "@rules_python//python/runfiles", + "@xacro_python_dependencies//pyyaml:pkg", ], ) diff --git a/bazel/defs.bzl b/bazel/defs.bzl index 41e62051..0adfc2c3 100644 --- a/bazel/defs.bzl +++ b/bazel/defs.bzl @@ -2,43 +2,52 @@ XacroInfo = provider( "Provider holding the result of a xacro generation step.", - fields = ["result", "data"], + fields = ["result"], ) XACRO_EXTENSION = ".xacro" def _xacro_impl(ctx): - if ctx.outputs.out: - out = ctx.outputs.out - else: - src = ctx.file.src.basename - if not src.endswith(XACRO_EXTENSION): - fail("xacro_file src should be named *.xacro not {}".format(src)) - out = ctx.actions.declare_file(src[:-len(XACRO_EXTENSION)]) + # Use declared output or derive from source name + out = ctx.outputs.out or ctx.actions.declare_file(ctx.file.src.basename[:-len(XACRO_EXTENSION)]) + + # Gather inputs for the xacro command + direct_inputs = [ctx.file.src] + ctx.files.data + dep_inputs = [dep[XacroInfo].result for dep in ctx.attr.deps] + all_inputs = direct_inputs + dep_inputs + + # Create a temporary directory + temp_dir = "TMP_XACRO/" + ctx.label.name - # The list of arguments we pass to the script. - args = [ctx.file.src.path, "-o", out.path] + ctx.attr.extra_args + symlink_paths = [] + for input in all_inputs: + symlink_path = ctx.actions.declare_file(temp_dir + "/" + input.basename) + ctx.actions.symlink( + output = symlink_path, + target_file = input, + ) + symlink_paths.append(symlink_path) + + arguments = [ + "-o", + out.path, + "--root-dir", + ctx.bin_dir.path + "/" + temp_dir, + ctx.file.src.basename, + ] + arguments += ["{}:={}".format(arg, val) for arg, val in ctx.attr.arguments.items()] - # Action to call the script. - all_inputs = [ctx.file.src] + ctx.files.data + [dep[XacroInfo].result for dep in ctx.attr.deps] ctx.actions.run( - inputs = all_inputs, + inputs = symlink_paths, outputs = [out], - arguments = args, - env = {"XACRO_INPUTS": "\n".join([file.path for file in all_inputs])}, - progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path), + arguments = arguments, executable = ctx.executable._xacro, + progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path), + mnemonic = "Xacro", ) - xacro_data = depset( - direct = [out] + ctx.files.data + [d[XacroInfo].result for d in ctx.attr.deps], - transitive = [d[XacroInfo].data for d in ctx.attr.deps], - ) - - runfiles = ctx.runfiles(files = xacro_data.to_list()) - return [ - XacroInfo(result = out, data = xacro_data), + XacroInfo(result = out), DefaultInfo( files = depset([out]), data_runfiles = ctx.runfiles(files = [out]), @@ -55,7 +64,7 @@ xacro_file = rule( "data": attr.label_list( allow_files = True, ), - "extra_args": attr.string_list(), + "arguments": attr.string_dict(), "deps": attr.label_list(providers = [XacroInfo]), "_xacro": attr.label( default = "@xacro//:xacro", diff --git a/bazel/integration_test/BUILD.bazel b/bazel/integration_test/BUILD.bazel index f657fc37..06aff5bf 100644 --- a/bazel/integration_test/BUILD.bazel +++ b/bazel/integration_test/BUILD.bazel @@ -16,7 +16,7 @@ xacro_file( name = "complex_example", src = "inputs/complex.xml.xacro", out = "my_complex_model.xml", - extra_args = ["special_argument:=foo"], + arguments = {"special_argument": "foo"}, deps = [":sample1"], ) @@ -30,7 +30,7 @@ xacro_file( name = "conditional_false", src = "inputs/conditional.xml.xacro", out = "conditional_false.xml", - extra_args = ["myarg:=false"], + arguments = {"myarg": "false"}, ) diff_test( diff --git a/bazel/integration_test/inputs/complex.xml.xacro b/bazel/integration_test/inputs/complex.xml.xacro index 7030edd5..7cf09815 100644 --- a/bazel/integration_test/inputs/complex.xml.xacro +++ b/bazel/integration_test/inputs/complex.xml.xacro @@ -1,7 +1,7 @@ - + diff --git a/xacro/__init__.py b/xacro/__init__.py index 7a73e17c..cedd5c78 100644 --- a/xacro/__init__.py +++ b/xacro/__init__.py @@ -53,6 +53,7 @@ filestack = None macrostack = None +root_dir = os.curdir def init_stacks(file): global filestack @@ -523,6 +524,7 @@ def process_include(elt, macros, symbols, func): try: # extend filestack filestack.append(filename) + print(filename) include = parse(None, filename).documentElement # recursive call to func @@ -1013,10 +1015,11 @@ def parse(inp, filename=None): :return:xml.dom.minidom.Document :raise: xml.parsers.expat.ExpatError """ + global root_dir f = None if inp is None: try: - inp = f = open(filename) + inp = f = open(os.path.join(root_dir, filename)) except IOError as e: # do not report currently processed file as "in file ..." filestack.pop() @@ -1099,6 +1102,11 @@ def process_file(input_file_name, **kwargs): """main processing pipeline""" # initialize file stack for error-reporting init_stacks(input_file_name) + + global root_dir + if 'root_dir' in kwargs: + root_dir = kwargs['root_dir'] + # parse the document into a xml.dom tree doc = parse(None, input_file_name) # perform macro replacement diff --git a/xacro/cli.py b/xacro/cli.py index 29a16376..d3a373a7 100644 --- a/xacro/cli.py +++ b/xacro/cli.py @@ -101,6 +101,8 @@ def process_args(argv, require_input=True): help="print file dependencies") parser.add_option("--inorder", "-i", action="store_true", dest="in_order", help="processing in read order (default, can be omitted)") + parser.add_option("--root-dir", dest="root_dir", metavar="DIR", + help="set the root directory to resolve relative paths to") # verbosity options parser.add_option("-q", action="store_const", dest="verbosity", const=0, From 662dfdcd79b716010c210286956398be4309dc6c Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Tue, 14 Jan 2025 17:25:26 +0000 Subject: [PATCH 24/28] Correctly resolve root_dir Signed-off-by: Michael Carroll --- xacro/__init__.py | 13 ++++++++----- xacro/cli.py | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/xacro/__init__.py b/xacro/__init__.py index cedd5c78..95691ace 100644 --- a/xacro/__init__.py +++ b/xacro/__init__.py @@ -53,8 +53,11 @@ filestack = None macrostack = None +# Allow the user to override the root directory that relative +# paths will be resolved to root_dir = os.curdir + def init_stacks(file): global filestack global macrostack @@ -1015,10 +1018,10 @@ def parse(inp, filename=None): :return:xml.dom.minidom.Document :raise: xml.parsers.expat.ExpatError """ - global root_dir f = None if inp is None: try: + global root_dir inp = f = open(os.path.join(root_dir, filename)) except IOError as e: # do not report currently processed file as "in file ..." @@ -1103,10 +1106,6 @@ def process_file(input_file_name, **kwargs): # initialize file stack for error-reporting init_stacks(input_file_name) - global root_dir - if 'root_dir' in kwargs: - root_dir = kwargs['root_dir'] - # parse the document into a xml.dom tree doc = parse(None, input_file_name) # perform macro replacement @@ -1129,6 +1128,10 @@ def process_file(input_file_name, **kwargs): def _process(input_file_name, opts): + if 'root_dir' in opts and opts['root_dir']: + global root_dir + root_dir = opts['root_dir'] + try: # open and process file doc = process_file(input_file_name, **opts) diff --git a/xacro/cli.py b/xacro/cli.py index d3a373a7..82d032d7 100644 --- a/xacro/cli.py +++ b/xacro/cli.py @@ -101,7 +101,7 @@ def process_args(argv, require_input=True): help="print file dependencies") parser.add_option("--inorder", "-i", action="store_true", dest="in_order", help="processing in read order (default, can be omitted)") - parser.add_option("--root-dir", dest="root_dir", metavar="DIR", + parser.add_option("--root-dir", dest="root_dir", metavar="DIR", default=None, help="set the root directory to resolve relative paths to") # verbosity options From 6495fd4c2c44f663e0142e71d2325be38fc845c5 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Tue, 14 Jan 2025 17:32:25 +0000 Subject: [PATCH 25/28] Update test paths Signed-off-by: Michael Carroll --- bazel/integration_test/expected/conditional_default.xml | 2 +- bazel/integration_test/expected/conditional_false.xml | 2 +- bazel/integration_test/expected/my_complex_model.xml | 2 +- bazel/integration_test/expected/sample1.xml | 2 +- bazel/integration_test/expected/sample2.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bazel/integration_test/expected/conditional_default.xml b/bazel/integration_test/expected/conditional_default.xml index 77111d80..d6cf2e4b 100644 --- a/bazel/integration_test/expected/conditional_default.xml +++ b/bazel/integration_test/expected/conditional_default.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/conditional_false.xml b/bazel/integration_test/expected/conditional_false.xml index 77bebb91..b8647c04 100644 --- a/bazel/integration_test/expected/conditional_false.xml +++ b/bazel/integration_test/expected/conditional_false.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/my_complex_model.xml b/bazel/integration_test/expected/my_complex_model.xml index 2e08e2ad..40c042f8 100644 --- a/bazel/integration_test/expected/my_complex_model.xml +++ b/bazel/integration_test/expected/my_complex_model.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/sample1.xml b/bazel/integration_test/expected/sample1.xml index da777c3a..6bc50222 100644 --- a/bazel/integration_test/expected/sample1.xml +++ b/bazel/integration_test/expected/sample1.xml @@ -1,6 +1,6 @@ - + diff --git a/bazel/integration_test/expected/sample2.xml b/bazel/integration_test/expected/sample2.xml index 7bc42c5d..0d9b5ccb 100644 --- a/bazel/integration_test/expected/sample2.xml +++ b/bazel/integration_test/expected/sample2.xml @@ -1,6 +1,6 @@ - + From b4aa47d9f9ea7116052954cee7d9c1d89dccb5f5 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Tue, 14 Jan 2025 17:50:46 +0000 Subject: [PATCH 26/28] Minimize diff Signed-off-by: Michael Carroll --- xacro/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xacro/__init__.py b/xacro/__init__.py index 95691ace..ea6d0444 100644 --- a/xacro/__init__.py +++ b/xacro/__init__.py @@ -46,9 +46,11 @@ from .xmlutils import opt_attrs, reqd_attrs, first_child_element, \ next_sibling_element, replace_node + # Dictionary of substitution args substitution_args_context = {} + # Stack of currently processed files / macros filestack = None macrostack = None @@ -527,7 +529,6 @@ def process_include(elt, macros, symbols, func): try: # extend filestack filestack.append(filename) - print(filename) include = parse(None, filename).documentElement # recursive call to func @@ -1105,7 +1106,6 @@ def process_file(input_file_name, **kwargs): """main processing pipeline""" # initialize file stack for error-reporting init_stacks(input_file_name) - # parse the document into a xml.dom tree doc = parse(None, input_file_name) # perform macro replacement From 4399f2bae67c3c2c0b242af015ddeee4ac638bfe Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Wed, 15 Jan 2025 16:25:10 +0000 Subject: [PATCH 27/28] Add test for --root-dir argument Signed-off-by: Michael Carroll --- test/test_xacro.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/test_xacro.py b/test/test_xacro.py index 2a7c89c2..5f0ea413 100644 --- a/test/test_xacro.py +++ b/test/test_xacro.py @@ -1141,6 +1141,46 @@ def test_create_subdirs(self): self.assertTrue(output_file_created) + def test_set_root_directory(self): + # Run xacro in one directory, but specify the directory to resolve + # filenames in. + tmp_dir_name = tempfile.mkdtemp() # create directory we can trash + + output_path = os.path.join(tmp_dir_name, "out") + + # Generate a pair of files to be parsed by xacro, outside of the + # test directory, to ensure the root-dir argument works + file_foo = os.path.join(tmp_dir_name, 'foo.xml.xacro') + file_bar = os.path.join(tmp_dir_name, 'bar.xml.xacro') + with open(file_foo, 'w') as f: + f.write(''' + + + +''') + with open(file_bar, 'w') as f: + f.write(''' + + + +''') + + # Run xacro with no --root-dir arg, which will then use the + # current path as the path to resolveto + self.run_xacro('foo.xml.xacro', '-o', output_path) + output_file_created = os.path.isfile(output_path) + self.assertFalse(output_file_created) + + # Run xacro with --root-dir arg set to the new temp directory + self.run_xacro('foo.xml.xacro', + '--root-dir', tmp_dir_name, + '-o', output_path) + + output_file_created = os.path.isfile(output_path) + shutil.rmtree(tmp_dir_name) # clean up after ourselves + + self.assertTrue(output_file_created) + def test_iterable_literals_plain(self): self.assert_matches(self.quick_xacro(''' From ca32c413fdf1387533bf491a2468532119c3b4ab Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Fri, 17 Jan 2025 08:42:36 -0600 Subject: [PATCH 28/28] Remove global on read-only variable Co-authored-by: Robert Haschke --- xacro/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/xacro/__init__.py b/xacro/__init__.py index ea6d0444..cb008458 100644 --- a/xacro/__init__.py +++ b/xacro/__init__.py @@ -1022,7 +1022,6 @@ def parse(inp, filename=None): f = None if inp is None: try: - global root_dir inp = f = open(os.path.join(root_dir, filename)) except IOError as e: # do not report currently processed file as "in file ..."