| """This module defines the cipd_install repository rule. |
| |
| The cipd_install repository rule hermetically installs a CIPD package as an external Bazel |
| repository. |
| |
| Files in the CIPD package can be added as dependencies to other Bazel targets in two ways: either |
| individually via a label such as "@my_cipd_pkg//:path/to/file", or by adding |
| "@my_cipd_pkg//:all_files" as a dependency, which is a filegroup that includes the entire contents |
| of the CIPD package. The contents of the generated BUILD.bazel file which facilitates this are |
| configurable, e.g. multiple smaller packages. |
| |
| Note: Any files with spaces in their names cannot be used by Bazel and are thus excluded |
| from the generated Bazel rules. |
| |
| If a Bazel target adds a CIPD package as a dependency, its contents will appear under the runfiles |
| directory. Example: |
| |
| ``` |
| # WORKSPACE |
| cipd_install( |
| name = "git_linux", |
| package = "infra/3pp/tools/git/linux-amd64", |
| version = "version:2.29.2.chromium.6", |
| ) |
| |
| # BUILD.bazel |
| go_library( |
| name = "git_util.go", |
| srcs = ["git_util.go"], |
| data = ["@git_linux//:all_files"], |
| ... |
| ) |
| |
| # git_util.go |
| import ( |
| "path/filepath" |
| |
| "go.skia.org/infra/bazel/go/bazel" |
| ) |
| |
| func FindGitBinary() string { |
| return filepath.Join(bazel.RunfilesDir(), "external/git_linux/bin/git") |
| } |
| ``` |
| |
| For Bazel targets that must support multiple operating systems, one can declare OS-specific CIPD |
| packages in the WORKSPACE file, and select the correct package according to the host OS via a |
| select[1] statement. Example: |
| |
| ``` |
| # WORKSPACE |
| cipd_install( |
| name = "git_linux", |
| package = "infra/3pp/tools/git/linux-amd64", |
| version = "version:2.29.2.chromium.6", |
| ) |
| cipd_install( |
| name = "git_win", |
| package = "infra/3pp/tools/git/windows-amd64", |
| version = "version:2.29.2.chromium.6", |
| ) |
| |
| # BUILD.bazel |
| go_library( |
| name = "git_util.go", |
| srcs = ["git_util.go"], |
| data = select({ |
| "@platforms//os:linux": ["@git_linux//:all_files"], |
| "@platforms//os:windows": ["@git_win//:all_files"], |
| }), |
| ... |
| ) |
| ``` |
| |
| As an alternative, we could extract any such select statements as Bazel macros, which would keep |
| BUILD files short. Example: |
| |
| ``` |
| # cipd_packages.bzl |
| def git(): |
| return select({ |
| "@platforms//os:linux": ["@git_linux//:all_files"], |
| "@platforms//os:windows": ["@git_win//:all_files"], |
| }) |
| |
| # BUILD.bazel |
| load(":cipd_packages.bzl", "git") |
| |
| go_library( |
| name = "git_util.go", |
| srcs = ["git_util.go"], |
| data = git(), |
| ... |
| ) |
| ``` |
| |
| Note that runfile generation is disabled on Windows by default, and must be enabled with |
| --enable_runfiles[2] for the above mechanism to work. |
| |
| [1] https://bazel.build/docs/configurable-attributes |
| [2] https://bazel.build/reference/command-line-reference#flag--enable_runfiles |
| """ |
| |
| load(":common.bzl", "fail_if_nonzero_status") |
| |
| def _postinstall_script(repository_ctx, script_name, script_content): |
| repository_ctx.report_progress("Executing postinstall script...") |
| repository_ctx.file( |
| script_name, |
| content = script_content, |
| executable = True, |
| ) |
| exec_result = repository_ctx.execute( |
| [repository_ctx.path(script_name)], |
| quiet = repository_ctx.attr.quiet, |
| ) |
| fail_if_nonzero_status(exec_result, "Failed to run postinstall script.") |
| repository_ctx.delete(repository_ctx.path(script_name)) |
| |
| _DEFAULT_BUILD_FILE_CONTENT = """ |
| # To add a specific file inside this CIPD package as a dependency, use a label such as |
| # @my_cipd_pkg//:path/to/file. |
| # The exclude pattern prevents files with spaces in their names from tripping up Bazel. |
| exports_files(glob(include=["**/*"], exclude=["**/* *"])) |
| |
| # Convenience filegroup to add all files in this CIPD package as dependencies. |
| filegroup( |
| name = "all_files", |
| # The exclude pattern prevents files with spaces in their names from tripping up Bazel. |
| srcs = glob(include=["**/*"], exclude=["**/* *"]), |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def _cipd_install_impl(repository_ctx): |
| is_windows = "windows" in repository_ctx.os.name.lower() |
| is_posix = not is_windows # This is a safe assumption given our fleet of test machines. |
| |
| # Install the CIPD package. |
| cipd_client = Label("@depot_tools//:cipd.bat" if is_windows else "@depot_tools//:cipd") |
| repository_ctx.report_progress("Installing CIPD package...") |
| exec_result = repository_ctx.execute( |
| [ |
| repository_ctx.path(cipd_client), |
| "install", |
| repository_ctx.attr.package, |
| repository_ctx.attr.version, |
| "-root", |
| ".", |
| ], |
| quiet = repository_ctx.attr.quiet, |
| ) |
| fail_if_nonzero_status(exec_result, "Failed to fetch CIPD package.") |
| |
| # Generate BUILD.bazel file. |
| build_file_content = repository_ctx.attr.build_file_content |
| if not build_file_content: |
| build_file_content = _DEFAULT_BUILD_FILE_CONTENT |
| repository_ctx.file("BUILD.bazel", content = build_file_content) |
| |
| # Optionally run the postinstall script if one was given. |
| if is_posix and repository_ctx.attr.postinstall_script_posix != "": |
| _postinstall_script( |
| repository_ctx, |
| "postinstall.sh", |
| repository_ctx.attr.postinstall_script_posix, |
| ) |
| if is_windows and repository_ctx.attr.postinstall_script_win != "": |
| _postinstall_script( |
| repository_ctx, |
| # The .bat extension is needed under Windows, or the OS won't execute the script. |
| "postinstall.bat", |
| repository_ctx.attr.postinstall_script_win, |
| ) |
| |
| cipd_install = repository_rule( |
| implementation = _cipd_install_impl, |
| attrs = { |
| "package": attr.string( |
| doc = """CIPD package name, e.g. "infra/3pp/tools/git/linux-amd64".""", |
| mandatory = True, |
| ), |
| "version": attr.string( |
| doc = """CIPD package version, e.g. "version:2.29.2.chromium.6".""", |
| mandatory = True, |
| ), |
| "build_file_content": attr.string( |
| doc = """If set, will be used as the content of the BUILD.bazel file. Otherwise, a |
| default BUILD.bazel file will be created with an all_files target.""", |
| ), |
| "postinstall_script_posix": attr.string( |
| doc = """Contents of post-install script to execute. Ignored if Bazel is running on a |
| non-POSIX OS. Optional.""", |
| ), |
| "postinstall_script_win": attr.string( |
| doc = """Contents of post-install script to execute. Ignored if Bazel is not running on |
| Windows. Optional.""", |
| ), |
| "quiet": attr.bool( |
| default = True, |
| doc = "Whether stdout and stderr should be printed to the terminal for debugging.", |
| ), |
| }, |
| doc = "Hermetically installs a CIPD package as an external Bazel repository.", |
| ) |