pax_global_header00006660000000000000000000000064147467421630014530gustar00rootroot0000000000000052 comment=8120a5186fd67f6248245ba28f2f76c899a83c07 rules_shell-0.4.0/000077500000000000000000000000001474674216300140525ustar00rootroot00000000000000rules_shell-0.4.0/.bazelci/000077500000000000000000000000001474674216300155415ustar00rootroot00000000000000rules_shell-0.4.0/.bazelci/presubmit.yml000066400000000000000000000016671474674216300203100ustar00rootroot00000000000000--- buildifier: latest matrix: platform: - ubuntu2004 - macos - windows bazel: - 6.5.0 - 7.4.0 tasks: test_module_bzlmod: name: "Test module (Bzlmod)" working_directory: "tests/bcr" bazel: ${{ bazel }} platform: ${{ platform }} build_flags: - "--enable_bzlmod" build_targets: - "//..." test_flags: - "--enable_bzlmod" test_targets: - "//..." test_module_workspace: name: "Test module (WORKSPACE)" working_directory: "tests/bcr" bazel: ${{ bazel }} platform: ${{ platform }} build_flags: - "--enable_bzlmod" build_targets: - "//..." test_flags: - "--enable_bzlmod" test_targets: - "//..." test_module_head: name: "Test module (Bazel@HEAD, Bzlmod)" working_directory: "tests/bcr" bazel: last_green platform: ${{ platform }} build_targets: - "//..." test_targets: - "//..." rules_shell-0.4.0/.bazelignore000066400000000000000000000000121474674216300163450ustar00rootroot00000000000000tests/bcr rules_shell-0.4.0/.bazelversion000066400000000000000000000000061474674216300165520ustar00rootroot000000000000007.4.0 rules_shell-0.4.0/.bcr/000077500000000000000000000000001474674216300146765ustar00rootroot00000000000000rules_shell-0.4.0/.bcr/config.yml000066400000000000000000000003161474674216300166660ustar00rootroot00000000000000# See https://github.com/bazel-contrib/publish-to-bcr#a-note-on-release-automation # for guidance about whether to uncomment this section: # fixedReleaser: login: meteorcloudy email: pcloudy@google.com rules_shell-0.4.0/.bcr/metadata.template.json000066400000000000000000000007351474674216300211700ustar00rootroot00000000000000{ "homepage": "https://github.com/bazelbuild/rules_shell", "maintainers": [ { "email": "pcloudy@google.com", "github": "meteorcloudy", "name": "Yun Peng" }, { "email": "fabian@meumertzhe.im", "github": "fmeum", "name": "Fabian Meumertzheim" } ], "repository": [ "github:bazelbuild/rules_shell" ], "versions": [], "yanked_versions": {} } rules_shell-0.4.0/.bcr/presubmit.yml000066400000000000000000000005041474674216300174320ustar00rootroot00000000000000bcr_test_module: module_path: tests/bcr matrix: platform: - centos7 - debian10 - ubuntu2004 - macos - windows bazel: [6.x, 7.x] tasks: run_test_module: name: Run test module platform: ${{ platform }} bazel: ${{ bazel }} test_targets: - "//..." rules_shell-0.4.0/.bcr/source.template.json000066400000000000000000000002601474674216300207010ustar00rootroot00000000000000{ "integrity": "**leave this alone**", "strip_prefix": "{REPO}-{VERSION}", "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{TAG}.tar.gz" } rules_shell-0.4.0/.github/000077500000000000000000000000001474674216300154125ustar00rootroot00000000000000rules_shell-0.4.0/.github/workflows/000077500000000000000000000000001474674216300174475ustar00rootroot00000000000000rules_shell-0.4.0/.github/workflows/release.yml000066400000000000000000000006651474674216300216210ustar00rootroot00000000000000# Cut a release whenever a new tag is pushed to the repo. # You should use an annotated tag, like `git tag -a v1.2.3` # and put the release notes into the commit message for the tag. name: Release on: push: tags: - "v*.*.*" permissions: contents: write jobs: release: uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v6 with: prerelease: false release_files: rules_shell-*.tar.gz rules_shell-0.4.0/.github/workflows/release_prep.sh000077500000000000000000000026741474674216300224650ustar00rootroot00000000000000#!/usr/bin/env bash set -o errexit -o nounset -o pipefail # Set by GH actions, see # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables TAG=${GITHUB_REF_NAME} # The prefix is chosen to match what GitHub generates for source archives # This guarantees that users can easily switch from a released artifact to a source archive # with minimal differences in their code (e.g. strip_prefix remains the same) PREFIX="rules_shell-${TAG:1}" ARCHIVE="rules_shell-$TAG.tar.gz" # NB: configuration for 'git archive' is in /.gitattributes git archive --format=tar --prefix="${PREFIX}/" "${TAG}" | gzip > "$ARCHIVE" SHA=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') cat << EOF ## Using Bzlmod with Bazel 6 or greater 1. (Bazel 6 only) Enable with \`common --enable_bzlmod\` in \`.bazelrc\`. 2. Add to your \`MODULE.bazel\` file: \`\`\`starlark bazel_dep(name = "rules_shell", version = "${TAG:1}") \`\`\` ## Using WORKSPACE Paste this snippet into your \`WORKSPACE.bazel\` file: \`\`\`starlark load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_shell", sha256 = "${SHA}", strip_prefix = "${PREFIX}", url = "https://github.com/bazelbuild/rules_shell/releases/download/${TAG}/${ARCHIVE}", ) load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains") rules_shell_dependencies() rules_shell_toolchains() \`\`\` EOF rules_shell-0.4.0/.gitignore000066400000000000000000000000321474674216300160350ustar00rootroot00000000000000bazel-* MODULE.bazel.lock rules_shell-0.4.0/CONTRIBUTING.md000066400000000000000000000021521474674216300163030ustar00rootroot00000000000000# How to contribute We'd love to accept your patches and contributions to this project. ## Before you begin ### Sign our Contributor License Agreement Contributions to this project must be accompanied by a [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project. If you or your current employer have already signed the Google CLA (even if it was for a different project), you probably don't need to do it again. Visit to see your current agreements or to sign a new one. ### Review our community guidelines This project follows [Google's Open Source Community Guidelines](https://opensource.google/conduct/). ## Contribution process ### Code reviews All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. rules_shell-0.4.0/LICENSE000066400000000000000000000261361474674216300150670ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rules_shell-0.4.0/MODULE.bazel000066400000000000000000000006251474674216300160610ustar00rootroot00000000000000module( name = "rules_shell", version = "0.0.0", ) bazel_dep(name = "bazel_features", version = "1.18.0") bazel_dep(name = "bazel_skylib", version = "1.6.1") bazel_dep(name = "platforms", version = "0.0.10") sh_configure = use_extension("//shell/private/extensions:sh_configure.bzl", "sh_configure") use_repo(sh_configure, "local_config_shell") register_toolchains("@local_config_shell//:all") rules_shell-0.4.0/README.md000066400000000000000000000001151474674216300153260ustar00rootroot00000000000000# rules_shell This repository contains the Bazel ruleset for shell scripts. rules_shell-0.4.0/WORKSPACE000066400000000000000000000002561474674216300153360ustar00rootroot00000000000000workspace(name = "rules_shell") load("//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains") rules_shell_dependencies() rules_shell_toolchains() rules_shell-0.4.0/shell/000077500000000000000000000000001474674216300151615ustar00rootroot00000000000000rules_shell-0.4.0/shell/BUILD000066400000000000000000000014251474674216300157450ustar00rootroot00000000000000# A runtime toolchain for shell scripts. # # Use `sh_toolchain` to register a toolchain for this type. # # Every toolchain registered for this type has the following attributes: # - `path`: The path to the shell interpreter for the target platform. # # Other attribute may be present but are considered implementation details of # Bazel's sh_* rules. # # Toolchains registered for this type should have target constraints. load("@bazel_skylib//:bzl_library.bzl", "bzl_library") toolchain_type( name = "toolchain_type", visibility = ["//visibility:public"], ) bzl_library( name = "rules_bzl", srcs = [ "sh_binary.bzl", "sh_library.bzl", "sh_test.bzl", ], visibility = ["//visibility:public"], deps = ["//shell/private:private_bzl"], ) rules_shell-0.4.0/shell/private/000077500000000000000000000000001474674216300166335ustar00rootroot00000000000000rules_shell-0.4.0/shell/private/BUILD000066400000000000000000000004021474674216300174110ustar00rootroot00000000000000load("@bazel_skylib//:bzl_library.bzl", "bzl_library") bzl_library( name = "private_bzl", srcs = [ "sh_binary.bzl", "sh_executable.bzl", "sh_library.bzl", "sh_test.bzl", ], visibility = ["//shell:__pkg__"], ) rules_shell-0.4.0/shell/private/extensions/000077500000000000000000000000001474674216300210325ustar00rootroot00000000000000rules_shell-0.4.0/shell/private/extensions/BUILD000066400000000000000000000000001474674216300216020ustar00rootroot00000000000000rules_shell-0.4.0/shell/private/extensions/sh_configure.bzl000066400000000000000000000020571474674216300242220ustar00rootroot00000000000000# Copyright 2018 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """The sh_configure module extension.""" load("@bazel_features//:features.bzl", "bazel_features") load("//shell/private/repositories:sh_config.bzl", "sh_config") def _sh_configure_impl(module_ctx): sh_config(name = "local_config_shell") if bazel_features.external_deps.extension_metadata_has_reproducible: return module_ctx.extension_metadata(reproducible = True) return None sh_configure = module_extension(implementation = _sh_configure_impl) rules_shell-0.4.0/shell/private/repositories/000077500000000000000000000000001474674216300213625ustar00rootroot00000000000000rules_shell-0.4.0/shell/private/repositories/BUILD000066400000000000000000000000001474674216300221320ustar00rootroot00000000000000rules_shell-0.4.0/shell/private/repositories/sh_config.bzl000066400000000000000000000104611474674216300240340ustar00rootroot00000000000000# Copyright 2018 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Configure sh_toolchains based on the local machine.""" visibility(["//shell", "//shell/private/extensions"]) _DEFAULT_SHELL_PATHS = { "windows": "c:/msys64/usr/bin/bash.exe", "linux": "/bin/bash", "osx": "/bin/bash", "freebsd": "/usr/local/bin/bash", "openbsd": "/usr/local/bin/bash", } _UNIX_SH_TOOLCHAIN_TEMPLATE = """ sh_toolchain( name = "{os}_sh", path = {sh_path}, ) """ _WINDOWS_SH_TOOLCHAIN_TEMPLATE = """ sh_toolchain( name = "{os}_sh", path = {sh_path}, launcher = "@bazel_tools//tools/launcher", launcher_maker = "@bazel_tools//tools/launcher:launcher_maker", ) """ _TOOLCHAIN_TEMPLATE = """ toolchain( name = "{os}_sh_toolchain", toolchain = ":{os}_sh", toolchain_type = "@rules_shell//shell:toolchain_type", target_compatible_with = [ "@platforms//os:{os}", ], ) """ def _sh_config_impl(repository_ctx): """sh_config rule implementation. Creates sh_toolchains for commonly supported target platforms. For the target platform matching the local machine, it detects the path of the shell interpreter instead of using the default path. Args: repository_ctx: the repository rule context object """ toolchains = [] for os, default_shell_path in _DEFAULT_SHELL_PATHS.items(): is_host = repository_ctx.os.name.startswith(os) if is_host: # This toolchain was first added before optional toolchains were # available, so instead of not registering a toolchain if we # couldn't find the shell, we register a toolchain with an empty # path. sh_path = _detect_local_shell_path(repository_ctx) or "" else: sh_path = default_shell_path sh_toolchain_template = _WINDOWS_SH_TOOLCHAIN_TEMPLATE if os == "windows" else _UNIX_SH_TOOLCHAIN_TEMPLATE toolchains.append(sh_toolchain_template.format( os = os, sh_path = repr(sh_path), )) toolchains.append(_TOOLCHAIN_TEMPLATE.format( os = os, )) repository_ctx.file("BUILD", """ load("@rules_shell//shell/toolchains:sh_toolchain.bzl", "sh_toolchain") """ + "\n".join(toolchains)) sh_config = repository_rule( environ = [ "BAZEL_SH", "WINDIR", "PATH", ], configure = True, implementation = _sh_config_impl, ) def _detect_local_shell_path(repository_ctx): if repository_ctx.os.name.startswith("windows"): return _detect_local_shell_path_windows(repository_ctx) else: return _detect_local_shell_path_unix(repository_ctx) def _detect_local_shell_path_windows(repository_ctx): sh_path = repository_ctx.os.environ.get("BAZEL_SH") if sh_path: return sh_path.replace("\\", "/") sh_path_obj = repository_ctx.which("bash.exe") if sh_path_obj: # repository_ctx.which returns a path object, convert that to # string so we can call string.startswith on it. sh_path = str(sh_path_obj) # When the Windows Subsystem for Linux is installed there's a # bash.exe under %WINDIR%\system32\bash.exe that launches Ubuntu # Bash which cannot run native Windows programs so it's not what # we want. windir = repository_ctx.os.environ.get("WINDIR") if not windir or not sh_path.startswith(windir): return sh_path.replace("\\", "/") return None def _detect_local_shell_path_unix(repository_ctx): sh_path = repository_ctx.os.environ.get("BAZEL_SH") if sh_path: return sh_path sh_path_obj = repository_ctx.which("bash") if sh_path_obj: return str(sh_path_obj) sh_path_obj = repository_ctx.which("sh") if sh_path_obj: return str(sh_path_obj) return None rules_shell-0.4.0/shell/private/root_symlinks.bzl000066400000000000000000000035671474674216300222730ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Helper rule to preserve the legacy runfiles path for the runfiles lib.""" # Requires the private skip_conflict_checking parameter on ctx.runfiles, which # is only available as of Bazel 7.4.0. We only use it when the native shell # rules are not available. ROOT_SYMLINKS_SUPPORTED = not hasattr(native, "sh_binary") def _single_file_or_fail(target): files = target[DefaultInfo].files.to_list() if len(files) != 1: fail("Expected exactly one file in {}, got {}".format(target.label, files)) return files[0] def _root_symlinks_impl(ctx): runfiles = ctx.runfiles( root_symlinks = { path: _single_file_or_fail(target) for target, path in ctx.attr.root_symlinks.items() }, # Adding root symlinks from Starlark usually enables conflict checking, # but that would break backwards compatibility as it affects all # runfiles, not just the symlinks. skip_conflict_checking = True, ) return [ DefaultInfo( files = depset(), runfiles = runfiles, ), ] root_symlinks = rule( implementation = _root_symlinks_impl, attrs = { "root_symlinks": attr.label_keyed_string_dict( allow_files = True, mandatory = True, ), }, ) rules_shell-0.4.0/shell/private/sh_binary.bzl000066400000000000000000000014341474674216300213240ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """sh_binary rule definition.""" load(":sh_executable.bzl", "make_sh_executable_rule") # For doc generation only. visibility("public") sh_binary = make_sh_executable_rule(executable = True) rules_shell-0.4.0/shell/private/sh_executable.bzl000066400000000000000000000166301474674216300221650ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Common code for sh_binary and sh_test rules.""" visibility(["//shell"]) _SH_TOOLCHAIN_TYPE = Label("//shell:toolchain_type") def _sh_executable_impl(ctx): if len(ctx.files.srcs) != 1: fail("you must specify exactly one file in 'srcs'", attr = "srcs") symlink = ctx.actions.declare_file(ctx.label.name) src = ctx.files.srcs[0] ctx.actions.symlink( output = symlink, target_file = src, is_executable = True, progress_message = "Symlinking %{label}", ) direct_files = [src, symlink] # TODO: Consider extracting this logic into a function provided by # sh_toolchain to allow users to inject launcher creation logic for # non-Windows platforms. if ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]): main_executable = _launcher_for_windows(ctx, symlink, src) direct_files.append(main_executable) else: main_executable = symlink files = depset(direct = direct_files) runfiles = ctx.runfiles(transitive_files = files, collect_default = True) default_info = DefaultInfo( executable = main_executable, files = files, runfiles = runfiles, ) instrumented_files_info = coverage_common.instrumented_files_info( ctx, source_attributes = ["srcs"], dependency_attributes = ["deps", "data"], ) run_environment_info = RunEnvironmentInfo( environment = { key: ctx.expand_make_variables( "env", ctx.expand_location(value, ctx.attr.data, short_paths = True), {}, ) for key, value in ctx.attr.env.items() }, inherited_environment = ctx.attr.env_inherit, ) return [ default_info, instrumented_files_info, run_environment_info, ] _WINDOWS_EXECUTABLE_EXTENSIONS = [ "exe", "cmd", "bat", ] def _is_windows_executable(file): return file.extension in _WINDOWS_EXECUTABLE_EXTENSIONS def _create_windows_exe_launcher(ctx, sh_toolchain, primary_output): if not sh_toolchain.launcher or not sh_toolchain.launcher_maker: fail("Windows sh_toolchain requires both 'launcher' and 'launcher_maker' to be set") bash_launcher = ctx.actions.declare_file(ctx.label.name + ".exe") launch_info = ctx.actions.args().use_param_file("%s", use_always = True).set_param_file_format("multiline") launch_info.add("binary_type=Bash") launch_info.add(ctx.workspace_name, format = "workspace_name=%s") launch_info.add("1" if ctx.configuration.runfiles_enabled() else "0", format = "symlink_runfiles_enabled=%s") launch_info.add(sh_toolchain.path, format = "bash_bin_path=%s") bash_file_short_path = primary_output.short_path if bash_file_short_path.startswith("../"): bash_file_rlocationpath = bash_file_short_path[3:] else: bash_file_rlocationpath = ctx.workspace_name + "/" + bash_file_short_path launch_info.add(bash_file_rlocationpath, format = "bash_file_rlocationpath=%s") launcher_artifact = sh_toolchain.launcher ctx.actions.run( executable = sh_toolchain.launcher_maker, inputs = [launcher_artifact], outputs = [bash_launcher], arguments = [launcher_artifact.path, launch_info, bash_launcher.path], use_default_shell_env = True, toolchain = _SH_TOOLCHAIN_TYPE, ) return bash_launcher def _launcher_for_windows(ctx, primary_output, main_file): if _is_windows_executable(main_file): if main_file.extension == primary_output.extension: return primary_output else: fail("Source file is a Windows executable file, target name extension should match source file extension") # bazel_tools should always registers a toolchain for Windows, but it may have an empty path. sh_toolchain = ctx.toolchains[_SH_TOOLCHAIN_TYPE] if not sh_toolchain or not sh_toolchain.path: # Let fail print the toolchain type with an apparent repo name. fail( """No suitable shell toolchain found: * if you are running Bazel on Windows, set the BAZEL_SH environment variable to the path of bash.exe * if you are running Bazel on a non-Windows platform but are targeting Windows, register an sh_toolchain for the""", _SH_TOOLCHAIN_TYPE, "toolchain type", ) return _create_windows_exe_launcher(ctx, sh_toolchain, primary_output) def make_sh_executable_rule(extra_attrs = {}, **kwargs): return rule( _sh_executable_impl, doc = """

The sh_binary rule is used to declare executable shell scripts. (sh_binary is a misnomer: its outputs aren't necessarily binaries.) This rule ensures that all dependencies are built, and appear in the runfiles area at execution time. We recommend that you name your sh_binary() rules after the name of the script minus the extension (e.g. .sh); the rule name and the file name must be distinct. sh_binary respects shebangs, so any available interpreter may be used (eg. #!/bin/zsh)

Example

For a simple shell script with no dependencies and some data files:

sh_binary(
    name = "foo",
    srcs = ["foo.sh"],
    data = glob(["datafiles/*.txt"]),
)
""", attrs = { "srcs": attr.label_list( allow_files = True, doc = """ The list of input files.

This attribute should be used to list shell script source files that belong to this library. Scripts can load other scripts using the shell's source or . command.

""", ), "data": attr.label_list( allow_files = True, flags = ["SKIP_CONSTRAINTS_OVERRIDE"], ), "deps": attr.label_list( allow_rules = ["sh_library"], doc = """ The list of "library" targets to be aggregated into this target. See general comments about deps at Typical attributes defined by most build rules.

This attribute should be used to list other sh_library rules that provide interpreted program source code depended on by the code in srcs. The files provided by these rules will be present among the runfiles of this target.

""", ), "env": attr.string_dict(), "env_inherit": attr.string_list(), "_windows_constraint": attr.label( default = "@platforms//os:windows", ), } | extra_attrs, toolchains = [ config_common.toolchain_type(_SH_TOOLCHAIN_TYPE, mandatory = False), ], **kwargs ) rules_shell-0.4.0/shell/private/sh_library.bzl000066400000000000000000000076071474674216300215140ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """sh_library rule definition.""" # For doc generation only. visibility("public") def _sh_library_impl(ctx): transitive_files = [] for target in ctx.attr.srcs: transitive_files.append(target[DefaultInfo].files) for target in ctx.attr.deps: transitive_files.append(target[DefaultInfo].files) for target in ctx.attr.data: transitive_files.append(target[DefaultInfo].files) files = depset(transitive = transitive_files) runfiles = ctx.runfiles(transitive_files = files, collect_default = True) instrumented_files_info = coverage_common.instrumented_files_info( ctx, source_attributes = ["srcs"], dependency_attributes = ["deps", "data"], ) return [ DefaultInfo( files = files, runfiles = runfiles, ), instrumented_files_info, ] sh_library = rule( _sh_library_impl, doc = """

The main use for this rule is to aggregate together a logical "library" consisting of related scripts—programs in an interpreted language that does not require compilation or linking, such as the Bourne shell—and any data those programs need at run-time. Such "libraries" can then be used from the data attribute of one or more sh_binary rules.

You can use the filegroup rule to aggregate data files.

In interpreted programming languages, there's not always a clear distinction between "code" and "data": after all, the program is just "data" from the interpreter's point of view. For this reason this rule has three attributes which are all essentially equivalent: srcs, deps and data. The current implementation does not distinguish between the elements of these lists. All three attributes accept rules, source files and generated files. It is however good practice to use the attributes for their usual purpose (as with other rules).

Examples

sh_library(
    name = "foo",
    data = [
        ":foo_service_script",  # an sh_binary with srcs
        ":deploy_foo",  # another sh_binary with srcs
    ],
)
""", attrs = { "srcs": attr.label_list( allow_files = True, doc = """ The list of input files.

This attribute should be used to list shell script source files that belong to this library. Scripts can load other scripts using the shell's source or . command.

""", ), "data": attr.label_list( allow_files = True, flags = ["SKIP_CONSTRAINTS_OVERRIDE"], ), "deps": attr.label_list( allow_rules = ["sh_library"], doc = """ The list of "library" targets to be aggregated into this target. See general comments about deps at Typical attributes defined by most build rules.

This attribute should be used to list other sh_library rules that provide interpreted program source code depended on by the code in srcs. The files provided by these rules will be present among the runfiles of this target.

""", ), }, ) rules_shell-0.4.0/shell/private/sh_test.bzl000066400000000000000000000025421474674216300210200ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """sh_test rule definition.""" load(":sh_executable.bzl", "make_sh_executable_rule") # For doc generation only. visibility("public") sh_test = make_sh_executable_rule( test = True, fragments = ["coverage"], extra_attrs = { "_lcov_merger": attr.label( cfg = "exec", default = configuration_field(fragment = "coverage", name = "output_generator"), executable = True, ), # Add the script as an attribute in order for sh_test to output code coverage results for # code covered by CC binaries invocations. "_collect_cc_coverage": attr.label( cfg = "exec", default = "@bazel_tools//tools/test:collect_cc_coverage", executable = True, ), }, ) rules_shell-0.4.0/shell/repositories.bzl000066400000000000000000000032051474674216300204210ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """WORKSPACE dependency macros for the shell rules.""" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load("//shell/private/repositories:sh_config.bzl", "sh_config") visibility("public") # buildifier: disable=unnamed-macro def rules_shell_dependencies(): """Instantiates repositories required by rules_shell.""" maybe( http_archive, name = "bazel_skylib", sha256 = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz", "https://github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz", ], ) # buildifier: disable=unnamed-macro def rules_shell_toolchains(): """Register sh_toolchains for the host and common other platforms.""" sh_config(name = "local_config_shell") native.register_toolchains("@local_config_shell//:all") rules_shell-0.4.0/shell/runfiles/000077500000000000000000000000001474674216300170105ustar00rootroot00000000000000rules_shell-0.4.0/shell/runfiles/BUILD000066400000000000000000000011321474674216300175670ustar00rootroot00000000000000load("//shell:sh_library.bzl", "sh_library") load("//shell/private:root_symlinks.bzl", "ROOT_SYMLINKS_SUPPORTED", "root_symlinks") alias( name = "runfiles", actual = ":runfiles_impl" if ROOT_SYMLINKS_SUPPORTED else "@bazel_tools//tools/bash/runfiles", visibility = ["//visibility:public"], ) sh_library( name = "runfiles_impl", data = [":runfiles_at_legacy_location"], tags = ["manual"], ) root_symlinks( name = "runfiles_at_legacy_location", root_symlinks = { "runfiles.bash": "bazel_tools/tools/bash/runfiles/runfiles.bash", }, tags = ["manual"], ) rules_shell-0.4.0/shell/runfiles/runfiles.bash000066400000000000000000000522041474674216300215010ustar00rootroot00000000000000# Copyright 2018 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Runfiles lookup library for Bazel-built Bash binaries and tests, version 3. # # VERSION HISTORY: # - version 3: Fixes a bug in the init code on macOS and makes the library aware # of Bzlmod repository mappings. # Features: # - With Bzlmod enabled, rlocation now takes the repository mapping of the # Bazel repository containing the calling script into account when # looking up runfiles. The new, optional second argument to rlocation can # be used to specify the canonical name of the Bazel repository to use # instead of this default. The new runfiles_current_repository function # can be used to obtain the canonical name of the N-th caller's Bazel # repository. # Fixed: # - Sourcing a shell script that contains the init code from a shell script # that itself contains the init code no longer fails on macOS. # Compatibility: # - The init script and the runfiles library are backwards and forwards # compatible with version 2. # - version 2: Shorter init code. # Features: # - "set -euo pipefail" only at end of init code. # "set -e" breaks the source || source || ... scheme on # macOS, because it terminates if path1 does not exist. # - Not exporting any environment variables in init code. # This is now done in runfiles.bash itself. # Compatibility: # - The v1 init code can load the v2 library, i.e. if you have older source # code (still using v1 init) then you can build it with newer Bazel (which # contains the v2 library). # - The reverse is not true: the v2 init code CANNOT load the v1 library, # i.e. if your project (or any of its external dependencies) use v2 init # code, then you need a newer Bazel version (which contains the v2 # library). # - version 1: Original Bash runfiles library. # # ENVIRONMENT: # - If RUNFILES_LIB_DEBUG=1 is set, the script will print diagnostic messages to # stderr. # # USAGE: # 1. Depend on this runfiles library from your build rule: # # sh_binary( # name = "my_binary", # ... # deps = ["@bazel_tools//tools/bash/runfiles"], # ) # # 2. Source the runfiles library. # # The runfiles library itself defines rlocation which you would need to look # up the library's runtime location, thus we have a chicken-and-egg problem. # Insert the following code snippet to the top of your main script: # # # --- begin runfiles.bash initialization v3 --- # # Copy-pasted from the Bazel Bash runfiles library v3. # set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash # # shellcheck disable=SC1090 # source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ # source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ # source "$0.runfiles/$f" 2>/dev/null || \ # source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ # source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ # { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e # # --- end runfiles.bash initialization v3 --- # # # 3. Use rlocation to look up runfile paths. # # cat "$(rlocation my_workspace/path/to/my/data.txt)" # if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ -f "$0.runfiles_manifest" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" elif [[ -f "$0.runfiles/MANIFEST" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then export RUNFILES_DIR="$0.runfiles" fi fi case "$(uname -s | tr [:upper:] [:lower:])" in msys*|mingw*|cygwin*) # matches an absolute Windows path export _RLOCATION_ISABS_PATTERN="^[a-zA-Z]:[/\\]" # Windows paths are case insensitive and Bazel and MSYS2 capitalize differently, so we can't # assume that all paths are in the same native case. export _RLOCATION_GREP_CASE_INSENSITIVE_ARGS=-i ;; *) # matches an absolute Unix path export _RLOCATION_ISABS_PATTERN="^/[^/].*" export _RLOCATION_GREP_CASE_INSENSITIVE_ARGS= ;; esac # Does not exit with a non-zero exit code if no match is found and performs a case-insensitive # search on Windows. function __runfiles_maybe_grep() { grep $_RLOCATION_GREP_CASE_INSENSITIVE_ARGS "$@" || test $? = 1; } export -f __runfiles_maybe_grep # Prints to stdout the runtime location of a data-dependency. # The optional second argument can be used to specify the canonical name of the # repository whose repository mapping should be used to resolve the repository # part of the provided path. If not specified, the repository of the caller is # used. function rlocation() { if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): start" fi if [[ "$1" =~ $_RLOCATION_ISABS_PATTERN ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): absolute path, return" fi # If the path is absolute, print it as-is. echo "$1" return 0 elif [[ "$1" == ../* || "$1" == */.. || "$1" == ./* || "$1" == */./* || "$1" == "*/." || "$1" == *//* ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "ERROR[runfiles.bash]: rlocation($1): path is not normalized" fi return 1 elif [[ "$1" == \\* ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "ERROR[runfiles.bash]: rlocation($1): absolute path without" \ "drive name" fi return 1 fi if [[ -f "$RUNFILES_REPO_MAPPING" ]]; then local -r target_repo_apparent_name=$(echo "$1" | cut -d / -f 1) # Use -s to get an empty remainder if the argument does not contain a slash. # The repo mapping should not be applied to single segment paths, which may # be root symlinks. local -r remainder=$(echo "$1" | cut -s -d / -f 2-) if [[ -n "$remainder" ]]; then if [[ -z "${2+x}" ]]; then local -r source_repo=$(runfiles_current_repository 2) else local -r source_repo=$2 fi if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): looking up canonical name for ($target_repo_apparent_name) from ($source_repo) in ($RUNFILES_REPO_MAPPING)" fi local -r target_repo=$(__runfiles_maybe_grep -m1 "^$source_repo,$target_repo_apparent_name," "$RUNFILES_REPO_MAPPING" | cut -d , -f 3) if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): canonical name of target repo is ($target_repo)" fi if [[ -n "$target_repo" ]]; then local -r rlocation_path="$target_repo/$remainder" else local -r rlocation_path="$1" fi else local -r rlocation_path="$1" fi else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): not using repository mapping ($RUNFILES_REPO_MAPPING) since it does not exist" fi local -r rlocation_path="$1" fi runfiles_rlocation_checked "$rlocation_path" } export -f rlocation # Exports the environment variables that subprocesses need in order to use # runfiles. # If a subprocess is a Bazel-built binary rule that also uses the runfiles # libraries under @bazel_tools//tools//runfiles, then that binary needs # these envvars in order to initialize its own runfiles library. function runfiles_export_envvars() { if [[ ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" \ && ! -d "${RUNFILES_DIR:-/dev/null}" ]]; then return 1 fi if [[ ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ -f "$RUNFILES_DIR/MANIFEST" ]]; then export RUNFILES_MANIFEST_FILE="$RUNFILES_DIR/MANIFEST" elif [[ -f "${RUNFILES_DIR}_manifest" ]]; then export RUNFILES_MANIFEST_FILE="${RUNFILES_DIR}_manifest" else export RUNFILES_MANIFEST_FILE= fi elif [[ ! -d "${RUNFILES_DIR:-/dev/null}" ]]; then if [[ "$RUNFILES_MANIFEST_FILE" == */MANIFEST \ && -d "${RUNFILES_MANIFEST_FILE%/MANIFEST}" ]]; then export RUNFILES_DIR="${RUNFILES_MANIFEST_FILE%/MANIFEST}" export JAVA_RUNFILES="$RUNFILES_DIR" elif [[ "$RUNFILES_MANIFEST_FILE" == *_manifest \ && -d "${RUNFILES_MANIFEST_FILE%_manifest}" ]]; then export RUNFILES_DIR="${RUNFILES_MANIFEST_FILE%_manifest}" export JAVA_RUNFILES="$RUNFILES_DIR" else export RUNFILES_DIR= fi fi } export -f runfiles_export_envvars # Returns the canonical name of the Bazel repository containing the script that # calls this function. # The optional argument N, which defaults to 1, can be used to return the # canonical name of the N-th caller instead. # # Note: This function only works correctly with Bzlmod enabled. Without Bzlmod, # its return value is ignored if passed to rlocation. function runfiles_current_repository() { local -r idx=${1:-1} local -r raw_caller_path="${BASH_SOURCE[$idx]}" # Make the caller path absolute if needed to handle the case where the script is run directly # from bazel-bin, with working directory a subdirectory of bazel-bin. if [[ "$raw_caller_path" =~ $_RLOCATION_ISABS_PATTERN ]]; then local -r caller_path="$raw_caller_path" else local -r caller_path="$(cd $(dirname "$raw_caller_path"); pwd)/$(basename "$raw_caller_path")" fi if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): caller's path is ($caller_path)" fi local rlocation_path= # If the runfiles manifest exists, search for an entry with target the caller's path. if [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then # Escape $caller_path for use in the grep regex below. Also replace \ with / since the manifest # uses / as the path separator even on Windows. local -r normalized_caller_path="$(echo "$caller_path" | sed 's|\\\\*|/|g')" local -r escaped_caller_path="$(echo "$normalized_caller_path" | sed 's/[.[\*^$]/\\&/g')" rlocation_path=$(__runfiles_maybe_grep -m1 "^[^ ]* ${escaped_caller_path}$" "${RUNFILES_MANIFEST_FILE}" | cut -d ' ' -f 1) if [[ -z "$rlocation_path" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "ERROR[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) is not the target of an entry in the runfiles manifest ($RUNFILES_MANIFEST_FILE)" fi # The binary may also be run directly from bazel-bin or bazel-out. local -r repository=$(echo "$normalized_caller_path" | __runfiles_maybe_grep -E -o '(^|/)(bazel-out/[^/]+/bin|bazel-bin)/external/[^/]+/' | tail -1 | awk -F/ '{print $(NF-1)}') if [[ -n "$repository" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in repository ($repository) (parsed exec path)" fi echo "$repository" else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in the main repository (parsed exec path)" fi echo "" fi return 1 else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) is the target of ($rlocation_path) in the runfiles manifest" fi fi fi # If the runfiles directory exists, check if the caller's path is of the form # $RUNFILES_DIR/rlocation_path and if so, set $rlocation_path. if [[ -z "$rlocation_path" && -d "${RUNFILES_DIR:-/dev/null}" ]]; then normalized_caller_path="$(echo "$caller_path" | sed 's|\\\\*|/|g')" normalized_dir="$(echo "${RUNFILES_DIR%[\/]}" | sed 's|\\\\*|/|g')" if [[ -n "${_RLOCATION_GREP_CASE_INSENSITIVE_ARGS}" ]]; then # When comparing file paths insensitively, also normalize the case of the prefixes. normalized_caller_path=$(echo "$normalized_caller_path" | tr '[:upper:]' '[:lower:]') normalized_dir=$(echo "$normalized_dir" | tr '[:upper:]' '[:lower:]') fi if [[ "$normalized_caller_path" == "$normalized_dir"/* ]]; then rlocation_path=${normalized_caller_path:${#normalized_dir}} rlocation_path=${rlocation_path:1} fi if [[ -z "$rlocation_path" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) does not lie under the runfiles directory ($normalized_dir)" fi # The only shell script that is not executed from the runfiles directory (if it is populated) # is the sh_binary entrypoint. Parse its path under the execroot, using the last match to # allow for nested execroots (e.g. in Bazel integration tests). The binary may also be run # directly from bazel-bin. local -r repository=$(echo "$normalized_caller_path" | __runfiles_maybe_grep -E -o '(^|/)(bazel-out/[^/]+/bin|bazel-bin)/external/[^/]+/' | tail -1 | awk -F/ '{print $(NF-1)}') if [[ -n "$repository" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in repository ($repository) (parsed exec path)" fi echo "$repository" else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in the main repository (parsed exec path)" fi echo "" fi return 0 else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($caller_path) has path ($rlocation_path) relative to the runfiles directory ($RUNFILES_DIR)" fi fi fi if [[ -z "$rlocation_path" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "ERROR[runfiles.bash]: runfiles_current_repository($idx): cannot determine repository for ($caller_path) since neither the runfiles directory (${RUNFILES_DIR:-}) nor the runfiles manifest (${RUNFILES_MANIFEST_FILE:-}) exist" fi return 1 fi if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($caller_path) corresponds to rlocation path ($rlocation_path)" fi # Normalize the rlocation path to be of the form repo/pkg/file. rlocation_path=${rlocation_path#_main/external/} rlocation_path=${rlocation_path#_main/../} local -r repository=$(echo "$rlocation_path" | cut -d / -f 1) if [[ "$repository" == _main ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($rlocation_path) lies in the main repository" fi echo "" else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($rlocation_path) lies in repository ($repository)" fi echo "$repository" fi } export -f runfiles_current_repository function runfiles_rlocation_checked() { # FIXME: If the runfiles lookup fails, the exit code of this function is 0 if # and only if the runfiles manifest exists. In particular, the exit code # behavior is not consistent across platforms. if [[ -e "${RUNFILES_DIR:-/dev/null}/$1" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): found under RUNFILES_DIR ($RUNFILES_DIR), return" fi echo "${RUNFILES_DIR}/$1" elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): looking in RUNFILES_MANIFEST_FILE ($RUNFILES_MANIFEST_FILE)" fi # If the rlocation path contains a space or newline, it needs to be prefixed # with a space and spaces, newlines, and backslashes have to be escaped as # \s, \n, and \b. if [[ "$1" == *" "* || "$1" == *$'\n'* ]]; then local search_prefix=" $(echo -n "$1" | sed 's/\\/\\b/g; s/ /\\s/g')" search_prefix="${search_prefix//$'\n'/\\n}" local escaped=true if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): using escaped search prefix ($search_prefix)" fi else local search_prefix="$1" local escaped=false fi # The extra space below is added because cut counts from 1. local trim_length=$(echo -n "$search_prefix " | wc -c | tr -d ' ') # Escape the search prefix for use in the grep regex below *after* # determining the trim length. local result=$(__runfiles_maybe_grep -m1 "^$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b "${trim_length}-") if [[ -z "$result" ]]; then # If path references a runfile that lies under a directory that itself # is a runfile, then only the directory is listed in the manifest. Look # up all prefixes of path in the manifest and append the relative path # from the prefix if there is a match. local prefix="$1" local prefix_result= local new_prefix= while true; do new_prefix="${prefix%/*}" [[ "$new_prefix" == "$prefix" ]] && break prefix="$new_prefix" if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): looking for prefix ($prefix)" fi if [[ "$prefix" == *" "* || "$prefix" == *$'\n'* ]]; then search_prefix=" $(echo -n "$prefix" | sed 's/\\/\\b/g; s/ /\\s/g')" search_prefix="${search_prefix//$'\n'/\\n}" escaped=true if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): using escaped search prefix ($search_prefix)" fi else search_prefix="$prefix" escaped=false fi # The extra space below is added because cut counts from 1. trim_length=$(echo -n "$search_prefix " | wc -c) prefix_result=$(__runfiles_maybe_grep -m1 "$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b ${trim_length}-) if [[ "$escaped" = true ]]; then prefix_result="${prefix_result//\\n/$'\n'}" prefix_result="${prefix_result//\\b/\\}" fi [[ -z "$prefix_result" ]] && continue local -r candidate="${prefix_result}${1#"${prefix}"}" if [[ -e "$candidate" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($candidate) via prefix ($prefix)" fi echo "$candidate" return 0 fi # At this point, the manifest lookup of prefix has been successful, # but the file at the relative path given by the suffix does not # exist. We do not continue the lookup with a shorter prefix for two # reasons: # 1. Manifests generated by Bazel never contain a path that is a # prefix of another path. # 2. Runfiles libraries for other languages do not check for file # existence and would have returned the non-existent path. It seems # better to return no path rather than a potentially different, # non-empty path. if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($candidate) via prefix ($prefix), but file does not exist" fi break done if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): not found in manifest" fi echo "" else if [[ "$escaped" = true ]]; then result="${result//\\n/$'\n'}" result="${result//\\b/\\}" fi if [[ -e "$result" ]]; then if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($result)" fi echo "$result" else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($result), but file does not exist" fi echo "" fi fi else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "ERROR[runfiles.bash]: cannot look up runfile \"$1\" " \ "(RUNFILES_DIR=\"${RUNFILES_DIR:-}\"," \ "RUNFILES_MANIFEST_FILE=\"${RUNFILES_MANIFEST_FILE:-}\")" fi return 1 fi } export -f runfiles_rlocation_checked export RUNFILES_REPO_MAPPING=$(runfiles_rlocation_checked _repo_mapping 2> /dev/null) rules_shell-0.4.0/shell/sh_binary.bzl000066400000000000000000000014111474674216300176450ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """sh_binary rule definition.""" load("//shell/private:sh_binary.bzl", _sh_binary = "sh_binary") visibility("public") sh_binary = getattr(native, "sh_binary", _sh_binary) rules_shell-0.4.0/shell/sh_library.bzl000066400000000000000000000014201474674216300200250ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """sh_library rule definition.""" load("//shell/private:sh_library.bzl", _sh_library = "sh_library") visibility("public") sh_library = getattr(native, "sh_library", _sh_library) rules_shell-0.4.0/shell/sh_test.bzl000066400000000000000000000013731474674216300173470ustar00rootroot00000000000000# Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """sh_test rule definition.""" load("//shell/private:sh_test.bzl", _sh_test = "sh_test") visibility("public") sh_test = getattr(native, "sh_test", _sh_test) rules_shell-0.4.0/shell/toolchains/000077500000000000000000000000001474674216300173245ustar00rootroot00000000000000rules_shell-0.4.0/shell/toolchains/BUILD000066400000000000000000000000001474674216300200740ustar00rootroot00000000000000rules_shell-0.4.0/shell/toolchains/sh_toolchain.bzl000066400000000000000000000033571474674216300225170ustar00rootroot00000000000000# Copyright 2018 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Define a toolchain rule for the shell.""" visibility("public") def _sh_toolchain_impl(ctx): """sh_toolchain rule implementation.""" return [ platform_common.ToolchainInfo( path = ctx.attr.path, launcher = ctx.executable.launcher, launcher_maker = ctx.executable.launcher_maker, ), ] sh_toolchain = rule( doc = "A runtime toolchain for shell targets.", attrs = { "path": attr.string( doc = "Absolute path to the shell interpreter.", mandatory = True, ), "launcher": attr.label( doc = "The generic launcher binary to use to run sh_binary/sh_test targets (only used when targeting Windows).", cfg = "target", allow_single_file = True, executable = True, ), "launcher_maker": attr.label( doc = "The tool to use to create a target-specific launcher from the generic launcher binary (only used when targeting Windows).", cfg = "exec", allow_single_file = True, executable = True, ), }, implementation = _sh_toolchain_impl, ) rules_shell-0.4.0/tests/000077500000000000000000000000001474674216300152145ustar00rootroot00000000000000rules_shell-0.4.0/tests/bcr/000077500000000000000000000000001474674216300157625ustar00rootroot00000000000000rules_shell-0.4.0/tests/bcr/BUILD000066400000000000000000000010411474674216300165400ustar00rootroot00000000000000load("@rules_shell//shell:sh_binary.bzl", "sh_binary") load("@rules_shell//shell:sh_library.bzl", "sh_library") load("@rules_shell//shell:sh_test.bzl", "sh_test") sh_library( name = "lib", srcs = ["lib.sh"], data = ["greeting.txt"], deps = ["@rules_shell//shell/runfiles"], ) sh_binary( name = "bin", srcs = ["bin.sh"], deps = [ ":lib", "@rules_shell//shell/runfiles", ], ) sh_test( name = "test", srcs = ["test.sh"], data = [":bin"], deps = ["@rules_shell//shell/runfiles"], ) rules_shell-0.4.0/tests/bcr/MODULE.bazel000066400000000000000000000002201474674216300177600ustar00rootroot00000000000000module(name = "rules_shell_tests") bazel_dep(name = "rules_shell") local_path_override( module_name = "rules_shell", path = "../..", ) rules_shell-0.4.0/tests/bcr/WORKSPACE000066400000000000000000000004031474674216300172400ustar00rootroot00000000000000workspace(name = "rules_shell_tests") local_repository( name = "rules_shell", path = "../..", ) load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains") rules_shell_dependencies() rules_shell_toolchains() rules_shell-0.4.0/tests/bcr/WORKSPACE.bzlmod000066400000000000000000000000001474674216300205170ustar00rootroot00000000000000rules_shell-0.4.0/tests/bcr/bin.sh000077500000000000000000000025441474674216300170760ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # --- begin runfiles.bash initialization v3 --- # Copy-pasted from the Bazel Bash runfiles library v3. set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash # shellcheck disable=SC1090 source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ source "$0.runfiles/$f" 2>/dev/null || \ source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e # --- end runfiles.bash initialization v3 --- source "$(rlocation "rules_shell_tests/lib.sh")" get_greeting rules_shell-0.4.0/tests/bcr/greeting.txt000066400000000000000000000000271474674216300203260ustar00rootroot00000000000000hello from rules_shell rules_shell-0.4.0/tests/bcr/lib.sh000077500000000000000000000026341474674216300170740ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # --- begin runfiles.bash initialization v3 --- # Copy-pasted from the Bazel Bash runfiles library v3. set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash # shellcheck disable=SC1090 source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ source "$0.runfiles/$f" 2>/dev/null || \ source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e # --- end runfiles.bash initialization v3 --- function get_greeting() { greeting_path=$(rlocation "rules_shell_tests/greeting.txt") cat "${greeting_path}" } rules_shell-0.4.0/tests/bcr/test.sh000077500000000000000000000031611474674216300173010ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2024 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # --- begin runfiles.bash initialization v3 --- # Copy-pasted from the Bazel Bash runfiles library v3. set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash # shellcheck disable=SC1090 source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ source "$0.runfiles/$f" 2>/dev/null || \ source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e # --- end runfiles.bash initialization v3 --- bin_path="$(rlocation "rules_shell_tests/bin.sh")" if [[ ! -x "${bin_path}" ]]; then echo "Expected '${bin_path}' to be an executable" exit 1 fi runfiles_export_envvars greeting=$("${bin_path}") if [[ "${greeting}" != "hello from rules_shell" ]]; then echo "Expected 'hello from rules_shell', got '${greeting}'" exit 1 fi rules_shell-0.4.0/tests/runfiles/000077500000000000000000000000001474674216300170435ustar00rootroot00000000000000rules_shell-0.4.0/tests/runfiles/BUILD000066400000000000000000000002231474674216300176220ustar00rootroot00000000000000load("//shell:sh_test.bzl", "sh_test") sh_test( name = "runfiles_test", srcs = ["runfiles_test.bash"], deps = ["//shell/runfiles"], ) rules_shell-0.4.0/tests/runfiles/runfiles_test.bash000077500000000000000000000465451474674216300226110ustar00rootroot00000000000000#!/bin/bash # # Copyright 2018 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -euo pipefail function _log_base() { prefix=$1 shift echo >&2 "${prefix}[$(basename "${BASH_SOURCE[0]}"):${BASH_LINENO[1]} ($(date "+%H:%M:%S %z"))] $*" } function fail() { _log_base "FAILED" "$@" exit 1 } function log_fail() { # non-fatal version of fail() _log_base "FAILED" $* } function log_info() { _log_base "INFO" $* } which uname >&/dev/null || fail "cannot locate GNU coreutils" case "$(uname -s | tr [:upper:] [:lower:])" in msys*|mingw*|cygwin*) function is_windows() { true; } ;; *) function is_windows() { false; } ;; esac function find_runfiles_lib() { # Unset existing definitions of the functions we want to test. if type rlocation >&/dev/null; then unset rlocation unset runfiles_export_envvars fi if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then if [[ -f "$0.runfiles_manifest" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" elif [[ -f "$0.runfiles/MANIFEST" ]]; then export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then export RUNFILES_DIR="$0.runfiles" fi fi if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then echo "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2- else echo >&2 "ERROR: cannot find //shell/runfiles:runfiles.bash" exit 1 fi } function test_rlocation_call_requires_no_envvars() { export RUNFILES_DIR=mock/runfiles export RUNFILES_MANIFEST_FILE= export RUNFILES_MANIFEST_ONLY= source "$runfiles_lib_path" || fail } function test_rlocation_argument_validation() { export RUNFILES_DIR= export RUNFILES_MANIFEST_FILE= export RUNFILES_MANIFEST_ONLY= source "$runfiles_lib_path" # Test invalid inputs to make sure rlocation catches these. if rlocation "../foo" >&/dev/null; then fail fi if rlocation "foo/.." >&/dev/null; then fail fi if rlocation "foo/../bar" >&/dev/null; then fail fi if rlocation "./foo" >&/dev/null; then fail fi if rlocation "foo/." >&/dev/null; then fail fi if rlocation "foo/./bar" >&/dev/null; then fail fi if rlocation "//foo" >&/dev/null; then fail fi if rlocation "foo//" >&/dev/null; then fail fi if rlocation "foo//bar" >&/dev/null; then fail fi if rlocation "\\foo" >&/dev/null; then fail fi } function test_rlocation_abs_path() { export RUNFILES_DIR= export RUNFILES_MANIFEST_FILE= export RUNFILES_MANIFEST_ONLY= source "$runfiles_lib_path" if is_windows; then [[ "$(rlocation "c:/Foo" || echo failed)" == "c:/Foo" ]] || fail [[ "$(rlocation "c:\\Foo" || echo failed)" == "c:\\Foo" ]] || fail else [[ "$(rlocation "/Foo" || echo failed)" == "/Foo" ]] || fail fi } function test_init_manifest_based_runfiles() { local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" cat > $tmpdir/foo.runfiles_manifest << EOF a/b $tmpdir/c/d e/f $tmpdir/g h y $tmpdir/y c/dir $tmpdir/dir unresolved $tmpdir/unresolved h/\si $tmpdir/ j k h/\s\bi $tmpdir/ j k b h/\n\bi $tmpdir/ \bnj k \na dir\swith\sspaces $tmpdir/dir with spaces space\snewline\nbackslash\b_dir $tmpdir/space newline\nbackslash\ba EOF mkdir "${tmpdir}/c" mkdir "${tmpdir}/y" mkdir -p "${tmpdir}/dir/deeply/nested" touch "${tmpdir}/c/d" "${tmpdir}/g h" touch "${tmpdir}/dir/file" ln -s /does/not/exist "${tmpdir}/dir/unresolved" touch "${tmpdir}/dir/deeply/nested/file" touch "${tmpdir}/dir/deeply/nested/file with spaces" ln -s /does/not/exist "${tmpdir}/unresolved" touch "${tmpdir}/ j k" touch "${tmpdir}/ j k b" mkdir -p "${tmpdir}/dir with spaces/nested" touch "${tmpdir}/dir with spaces/nested/file" if ! is_windows; then touch "${tmpdir}/ \nj k "$'\n'a mkdir -p "${tmpdir}/space newline"$'\n'"backslash\a" touch "${tmpdir}/space newline"$'\n'"backslash\a/f i\le" fi export RUNFILES_DIR= export RUNFILES_MANIFEST_FILE=$tmpdir/foo.runfiles_manifest source "$runfiles_lib_path" [[ -z "$(rlocation a || echo failed)" ]] || fail [[ -z "$(rlocation c/d || echo failed)" ]] || fail [[ "$(rlocation a/b || echo failed)" == "$tmpdir/c/d" ]] || fail [[ "$(rlocation e/f || echo failed)" == "$tmpdir/g h" ]] || fail [[ "$(rlocation y || echo failed)" == "$tmpdir/y" ]] || fail [[ -z "$(rlocation c || echo failed)" ]] || fail [[ -z "$(rlocation c/di || echo failed)" ]] || fail [[ "$(rlocation c/dir || echo failed)" == "$tmpdir/dir" ]] || fail [[ "$(rlocation c/dir/file || echo failed)" == "$tmpdir/dir/file" ]] || fail [[ -z "$(rlocation c/dir/unresolved || echo failed)" ]] || fail [[ "$(rlocation c/dir/deeply/nested/file || echo failed)" == "$tmpdir/dir/deeply/nested/file" ]] || fail [[ "$(rlocation "c/dir/deeply/nested/file with spaces" || echo failed)" == "$tmpdir/dir/deeply/nested/file with spaces" ]] || fail [[ -z "$(rlocation unresolved || echo failed)" ]] || fail [[ "$(rlocation "h/ i" || echo failed)" == "$tmpdir/ j k" ]] || fail [[ "$(rlocation "h/ \i" || echo failed)" == "$tmpdir/ j k b" ]] || fail [[ "$(rlocation "dir with spaces" || echo failed)" == "$tmpdir/dir with spaces" ]] || fail [[ "$(rlocation "dir with spaces/nested/file" || echo failed)" == "$tmpdir/dir with spaces/nested/file" ]] || fail if ! is_windows; then [[ "$(rlocation $'h/\n\\i' || echo failed)" == "$tmpdir/ \nj k "$'\n'a ]] || fail [[ "$(rlocation "space newline"$'\n'"backslash\_dir/f i\le" || echo failed)" == "${tmpdir}/space newline"$'\n'"backslash\a/f i\le" ]] || fail fi rm -r "$tmpdir/c/d" "$tmpdir/g h" "$tmpdir/y" "$tmpdir/dir" "$tmpdir/unresolved" "$tmpdir/ j k" "$tmpdir/dir with spaces" if ! is_windows; then rm -r "$tmpdir/ \nj k "$'\n'a "${tmpdir}/space newline"$'\n'"backslash\a" [[ -z "$(rlocation $'h/\n\\i' || echo failed)" ]] || fail [[ -z "$(rlocation "space newline"$'\n'"backslash\_dir/f i\le" || echo failed)" ]] || fail fi [[ -z "$(rlocation a/b || echo failed)" ]] || fail [[ -z "$(rlocation e/f || echo failed)" ]] || fail [[ -z "$(rlocation y || echo failed)" ]] || fail [[ -z "$(rlocation c/dir || echo failed)" ]] || fail [[ -z "$(rlocation c/dir/file || echo failed)" ]] || fail [[ -z "$(rlocation c/dir/deeply/nested/file || echo failed)" ]] || fail [[ -z "$(rlocation "h/ i" || echo failed)" ]] || fail [[ -z "$(rlocation "dir with spaces" || echo failed)" ]] || fail [[ -z "$(rlocation "dir with spaces/nested/file" || echo failed)" ]] || fail } function test_manifest_based_envvars() { local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" echo "a b" > $tmpdir/foo.runfiles_manifest export RUNFILES_DIR= export RUNFILES_MANIFEST_FILE=$tmpdir/foo.runfiles_manifest mkdir -p $tmpdir/foo.runfiles source "$runfiles_lib_path" runfiles_export_envvars [[ "${RUNFILES_DIR:-}" == "$tmpdir/foo.runfiles" ]] || fail [[ "${RUNFILES_MANIFEST_FILE:-}" == "$tmpdir/foo.runfiles_manifest" ]] || fail } function test_init_directory_based_runfiles() { local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" export RUNFILES_DIR=${tmpdir}/mock/runfiles export RUNFILES_MANIFEST_FILE= source "$runfiles_lib_path" mkdir -p "$RUNFILES_DIR/a" touch "$RUNFILES_DIR/a/b" "$RUNFILES_DIR/c d" [[ "$(rlocation a || echo failed)" == "$RUNFILES_DIR/a" ]] || fail [[ "$(rlocation c/d || echo failed)" == failed ]] || fail [[ "$(rlocation a/b || echo failed)" == "$RUNFILES_DIR/a/b" ]] || fail [[ "$(rlocation "c d" || echo failed)" == "$RUNFILES_DIR/c d" ]] || fail [[ "$(rlocation "c" || echo failed)" == failed ]] || fail rm -r "$RUNFILES_DIR/a" "$RUNFILES_DIR/c d" [[ "$(rlocation a || echo failed)" == failed ]] || fail [[ "$(rlocation a/b || echo failed)" == failed ]] || fail [[ "$(rlocation "c d" || echo failed)" == failed ]] || fail } function test_directory_based_runfiles_with_repo_mapping_from_main() { local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" export RUNFILES_DIR=${tmpdir}/mock/runfiles mkdir -p "$RUNFILES_DIR" cat > "$RUNFILES_DIR/_repo_mapping" < "$RUNFILES_DIR/_repo_mapping" < "$tmpdir/foo.repo_mapping" < "$RUNFILES_MANIFEST_FILE" << EOF _repo_mapping $tmpdir/foo.repo_mapping config.json $tmpdir/config.json protobuf+3.19.2/foo/runfile $tmpdir/protobuf+3.19.2/foo/runfile _main/bar/runfile $tmpdir/_main/bar/runfile protobuf+3.19.2/bar/dir $tmpdir/protobuf+3.19.2/bar/dir EOF source "$runfiles_lib_path" mkdir -p "$tmpdir/_main/bar" touch "$tmpdir/_main/bar/runfile" mkdir -p "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted" touch "$tmpdir/protobuf+3.19.2/bar/dir/file" touch "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" mkdir -p "$tmpdir/protobuf+3.19.2/foo" touch "$tmpdir/protobuf+3.19.2/foo/runfile" touch "$tmpdir/config.json" [[ "$(rlocation "my_module/bar/runfile" "" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail [[ "$(rlocation "my_workspace/bar/runfile" "" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail [[ "$(rlocation "my_protobuf/foo/runfile" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail [[ "$(rlocation "my_protobuf/bar/dir" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail [[ "$(rlocation "my_protobuf/bar/dir/file" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail [[ "$(rlocation "my_protobuf/bar/dir/de eply/nes ted/fi+le" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail [[ -z "$(rlocation "protobuf/foo/runfile" "" || echo failed)" ]] || fail [[ -z "$(rlocation "protobuf/bar/dir/dir/de eply/nes ted/fi+le" "" || echo failed)" ]] || fail [[ "$(rlocation "_main/bar/runfile" "" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail [[ "$(rlocation "protobuf+3.19.2/foo/runfile" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail [[ "$(rlocation "protobuf+3.19.2/bar/dir" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail [[ "$(rlocation "protobuf+3.19.2/bar/dir/file" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail [[ "$(rlocation "protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail [[ "$(rlocation "config.json" "" || echo failed)" == "$tmpdir/config.json" ]] || fail } function test_manifest_based_runfiles_with_repo_mapping_from_other_repo() { local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" cat > "$tmpdir/foo.repo_mapping" < "$RUNFILES_MANIFEST_FILE" << EOF _repo_mapping $tmpdir/foo.repo_mapping config.json $tmpdir/config.json protobuf+3.19.2/foo/runfile $tmpdir/protobuf+3.19.2/foo/runfile _main/bar/runfile $tmpdir/_main/bar/runfile protobuf+3.19.2/bar/dir $tmpdir/protobuf+3.19.2/bar/dir EOF source "$runfiles_lib_path" mkdir -p "$tmpdir/_main/bar" touch "$tmpdir/_main/bar/runfile" mkdir -p "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted" touch "$tmpdir/protobuf+3.19.2/bar/dir/file" touch "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" mkdir -p "$tmpdir/protobuf+3.19.2/foo" touch "$tmpdir/protobuf+3.19.2/foo/runfile" touch "$tmpdir/config.json" [[ "$(rlocation "protobuf/foo/runfile" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail [[ "$(rlocation "protobuf/bar/dir" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail [[ "$(rlocation "protobuf/bar/dir/file" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail [[ "$(rlocation "protobuf/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail [[ -z "$(rlocation "my_module/bar/runfile" "protobuf+3.19.2" || echo failed)" ]] || fail [[ -z "$(rlocation "my_protobuf/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" ]] || fail [[ "$(rlocation "_main/bar/runfile" "protobuf+3.19.2" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail [[ "$(rlocation "protobuf+3.19.2/foo/runfile" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail [[ "$(rlocation "protobuf+3.19.2/bar/dir" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail [[ "$(rlocation "protobuf+3.19.2/bar/dir/file" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail [[ "$(rlocation "protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail [[ "$(rlocation "config.json" "protobuf+3.19.2" || echo failed)" == "$tmpdir/config.json" ]] || fail } function test_directory_based_envvars() { export RUNFILES_DIR=mock/runfiles export RUNFILES_MANIFEST_FILE= source "$runfiles_lib_path" runfiles_export_envvars [[ "${RUNFILES_DIR:-}" == "mock/runfiles" ]] || fail [[ -z "${RUNFILES_MANIFEST_FILE:-}" ]] || fail } function main() { local -r manifest_file="${RUNFILES_MANIFEST_FILE:-}" local -r dir="${RUNFILES_DIR:-}" local -r runfiles_lib_path=$(find_runfiles_lib) local -r tests=$(declare -F | grep " -f test" | awk '{print $3}') local failure=0 for t in $tests; do export RUNFILES_MANIFEST_FILE="$manifest_file" export RUNFILES_DIR="$dir" if ! ($t); then failure=1 fi done return $failure } main