diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..327b0dcb --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(gh issue *)", + "Bash(buildifier)", + "Bash(echo \"exit: $?\")" + ] + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..914d9c9e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,161 @@ +# rules_pkg — Claude Code guide + +rules_pkg is a set of Bazel rules for building distribution packages (tar, zip, deb, rpm, …). +The core abstraction is a set of package-format-agnostic mapping rules (`pkg_files`, +`pkg_filegroup`, `pkg_mkdirs`, `pkg_mklink`) that describe *what* goes where in a package; +format-specific rules (`pkg_tar`, `pkg_zip`, `pkg_deb`, `pkg_rpm`) consume those descriptions. + +## Repository layout + +``` +pkg/ Runtime rules and providers (shipped in the distribution) + mappings.bzl pkg_files, pkg_filegroup, pkg_mkdirs, pkg_mklink + providers.bzl PackageVariablesInfo and other providers + private/ Internal implementation helpers (not public API) + util.bzl substitute_package_variables, setup_output_files, … + deb/ pkg_deb implementation + tar/ pkg_tar implementation + zip/ pkg_zip implementation +tests/ All tests (not shipped) + mappings/ Analysis tests for pkg_files / pkg_filegroup + tar/ Tests for pkg_tar + deb/ Tests for pkg_deb + rpm/ Tests for pkg_rpm + zip/ Tests for pkg_zip +examples/ Runnable examples (tested in CI) +docs/ Generated reference docs (do not edit by hand) +distro/ Rules to build the distribution tarball +``` + +Top-level `.bzl` shims (`mappings.bzl`, `pkg.bzl`, etc.) are backward-compatibility +re-exports of the files inside `pkg/`. + +## Code style + +### Starlark / BUILD files — always run buildifier after editing + +After editing any `.bzl` or `BUILD` file, run: + +``` +buildifier --lint=fix +``` + +buildifier enforces load ordering, argument sorting, and other canonical style. +It will reorder loads alphabetically; let it. + +### Starlark conventions + +- All public rule attributes must have a `doc =` string. +- Use `substitute_package_variables(ctx, value)` (from `//pkg/private:util.bzl`) + to expand `$(VAR)` make-variable syntax in string attributes. + The rule must also declare a `package_variables` attribute typed + `attr.label(providers = [PackageVariablesInfo])`. +- Prefer solutions that work for all package formats (via `pkg_files`/`pkg_filegroup`) + over format-specific additions. +- Actions must not write quoted strings directly to command lines — write paths to + an intermediate file instead. + +### Python + +- Python 3 only; no Python 2 support. +- Always import with full paths from the workspace root. +- No new third-party package dependencies — standard library only. + +### General + +- No files should have trailing whitespace. +- Try to keep lines under 100 characters long. + +## Testing + +**All features and bug fixes must have tests.** + +### Mappings (pkg_files / pkg_filegroup) + +Tests live in `tests/mappings/mappings_test.bzl` and are registered via the +`mappings_analysis_tests()` macro called from `tests/mappings/BUILD`. + +- Use `pkg_files_contents_test` (defined in `mappings_test.bzl`) to assert + expected destination paths from a `pkg_files` target. +- Use `pkg_filegroup_contents_test` to compare a `pkg_filegroup` output against + reference `pkg_files` / `pkg_mkdirs` / `pkg_mklink` targets. +- Use `generic_negative_test` for targets that are expected to fail analysis. +- Add new test names to the `pkg_files_analysis_tests` test suite list at the + bottom of `mappings_analysis_tests()`. + +Run them with: + +``` +bazel test //tests/mappings/... +``` + +### package_variables / make-variable substitution + +The sample naming rule used across tests is `my_package_naming` in +`tests/my_package_name.bzl`. Load it as: + +```python +load("//tests:my_package_name.bzl", "my_package_naming") +``` + +Create an instance, then wire it to the `package_variables` attribute of +the rule under test. Example: + +```python +my_package_naming(name = "my_vars", label = "linux_x86_64", tags = ["manual"]) + +pkg_files( + name = "my_files", + srcs = [...], + prefix = "usr/lib/$(label)", + package_variables = ":my_vars", + tags = ["manual"], +) +``` + +### Format-specific tests + +``` +bazel test //tests/tar/... +bazel test //tests/deb/... +bazel test //tests/zip/... +bazel test //tests/rpm/... # may require rpm toolchain +``` + +### Running everything + +``` +bazel test //tests/... +``` + +## Regenerating docs + +After any feature change, regenerate the reference docs before committing: + +``` +bazel build //doc_build:reference +cp bazel-bin/doc_build/reference.md docs/latest.md +``` + +Do **not** `git commit` yet — that is a separate step the user will handle. + +## Common patterns + +### Adding package_variables support to an attribute + +1. Import `PackageVariablesInfo` from `//pkg:providers.bzl` and + `substitute_package_variables` from `//pkg/private:util.bzl`. +2. Add to the rule attrs: + ```python + "package_variables": attr.label( + doc = """See [Common Attributes](#package_variables)""", + providers = [PackageVariablesInfo], + ), + ``` +3. In the implementation, call substitution at the top before using the value: + ```python + prefix = substitute_package_variables(ctx, ctx.attr.prefix) + ``` +4. Use the substituted local variable everywhere instead of `ctx.attr.prefix`. +5. Run `buildifier --lint=fix` on the modified file. +6. Add analysis tests in the appropriate test file. diff --git a/docs/latest.md b/docs/latest.md index 4f10cf46..ba68f1e5 100755 --- a/docs/latest.md +++ b/docs/latest.md @@ -96,8 +96,8 @@ pkg_deb(name, data, built_using_file, changelog, conffiles, conffiles_file, config, conflicts, depends, depends_file, description, description_file, distribution, enhances, homepage, license, maintainer, md5sums, package, package_file_name, package_variables, postinst, postrm, - predepends, preinst, prerm, priority, provides, recommends, replaces, section, suggests, - templates, triggers, urgency, version, version_file) + predepends, preinst, prerm, priority, provides, provides_file, recommends, replaces, + replaces_file, section, suggests, templates, triggers, urgency, version, version_file) Create a Debian package. @@ -148,9 +148,11 @@ include both. If you need downstream rule to specifically depend on only the .de | preinst | "The pre-install script for the package. See http://www.debian.org/doc/debian-policy/ch-maintainerscripts.html. | Label | optional | `None` | | prerm | The pre-remove script for the package. See http://www.debian.org/doc/debian-policy/ch-maintainerscripts.html. | Label | optional | `None` | | priority | The priority of the package. See http://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities. | String | optional | `""` | -| provides | See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps. | List of strings | optional | `[]` | +| provides | See https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides. | List of strings | optional | `[]` | +| provides_file | File that contains a list of provided packages. Must not be used with `provides`. See https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides. | Label | optional | `None` | | recommends | See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps. | List of strings | optional | `[]` | -| replaces | See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps. | List of strings | optional | `[]` | +| replaces | See https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces. | List of strings | optional | `[]` | +| replaces_file | File that contains a list of replaced packages. Must not be used with `replaces`. See https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces. | Label | optional | `None` | | section | The section of the package. See http://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections. | String | optional | `""` | | suggests | See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps. | List of strings | optional | `[]` | | templates | templates file used for debconf integration. See https://www.debian.org/doc/debian-policy/ch-binary.html#prompting-in-maintainer-scripts. | Label | optional | `None` | @@ -235,13 +237,14 @@ find_system_rpmbuild(name="rules_pkg_rpmbuild")
 load("@rules_pkg//pkg:rpm_pfg.bzl", "pkg_rpm")
 
-pkg_rpm(name, srcs, data, architecture, binary_payload_compression, changelog, conflicts, debug,
-        debuginfo, defines, description, description_file, epoch, group, license, obsoletes,
-        package_file_name, package_name, package_variables, post_scriptlet, post_scriptlet_file,
-        posttrans_scriptlet, posttrans_scriptlet_file, postun_scriptlet, postun_scriptlet_file,
-        pre_scriptlet, pre_scriptlet_file, preun_scriptlet, preun_scriptlet_file, provides, release,
-        release_file, requires, requires_contextual, rpmbuild_path, source_date_epoch,
-        source_date_epoch_file, spec_template, subrpms, summary, url, version, version_file)
+pkg_rpm(name, srcs, data, architecture, binary_payload_compression, changelog, conflicts,
+             debug, debuginfo, defines, description, description_file, epoch, group, license,
+             obsoletes, package_file_name, package_name, package_variables, post_scriptlet,
+             post_scriptlet_file, posttrans_scriptlet, posttrans_scriptlet_file, postun_scriptlet,
+             postun_scriptlet_file, pre_scriptlet, pre_scriptlet_file, preun_scriptlet,
+             preun_scriptlet_file, private_stamp_detect, provides, release, release_file, requires,
+             requires_contextual, rpmbuild_path, source_date_epoch, source_date_epoch_file,
+             spec_template, stamp, subrpms, summary, url, version, version_file)
 
Creates an RPM format package via `pkg_filegroup` and friends. @@ -315,8 +318,9 @@ include both. If you need downstream rule to specifically depend on only the .rp | pre_scriptlet_file | File containing the RPM `%pre` scriptlet | Label | optional | `None` | | preun_scriptlet | RPM `%preun` scriptlet. Currently only allowed to be a shell script.

`preun_scriptlet` and `preun_scriptlet_file` are mutually exclusive. | String | optional | `""` | | preun_scriptlet_file | File containing the RPM `%preun` scriptlet | Label | optional | `None` | +| private_stamp_detect | - | Boolean | optional | `False` | | provides | List of rpm capabilities that this package provides.

Corresponds to the "Provides" preamble tag.

See also: https://rpm-software-management.github.io/rpm/manual/dependencies.html | List of strings | optional | `[]` | -| release | RPM "Release" tag

Exactly one of `release` or `release_file` must be provided. | String | optional | `""` | +| release | RPM "Release" tag

Exactly one of `release` or `release_file` must be provided.

When `stamp` is enabled, workspace status variable placeholders of the form `{VARIABLE_NAME}` will be substituted at build time using values from the stable and volatile status files. For example, setting `release = "0.{BUILD_TIMESTAMP}"` with `stamp = 1` will embed the build timestamp in the release tag. See https://bazel.build/docs/user-manual#workspace-status for details on workspace status variables. | String | optional | `""` | | release_file | File containing RPM "Release" tag. | Label | optional | `None` | | requires | List of rpm capability expressions that this package requires.

Corresponds to the "Requires" preamble tag.

See also: https://rpm-software-management.github.io/rpm/manual/dependencies.html | List of strings | optional | `[]` | | requires_contextual | Contextualized requirement specifications

This is a map of various properties (often scriptlet types) to capability name specifications, e.g.:

{"pre": ["GConf2"],"post": ["GConf2"], "postun": ["GConf2"]}


Which causes the below to be added to the spec file's preamble:

Requires(pre): GConf2
Requires(post): GConf2
Requires(postun): GConf2


This is most useful for ensuring that required tools exist when scriptlets are run, although there may be other valid use cases. Valid keys for this attribute may include, but are not limited to:

- `pre` - `post` - `preun` - `postun` - `pretrans` - `posttrans`

For capabilities that are always required by packages at runtime, use the `requires` attribute instead.

See also: https://rpm-software-management.github.io/rpm/manual/more_dependencies.html

NOTE: `pkg_rpm` does not check if the keys of this dictionary are acceptable to `rpm(8)`. | Dictionary: String -> List of strings | optional | `{}` | @@ -324,6 +328,7 @@ include both. If you need downstream rule to specifically depend on only the .rp | source_date_epoch | Value to export as SOURCE_DATE_EPOCH to facilitate reproducible builds

Implicitly sets the `%clamp_mtime_to_source_date_epoch` in the subordinate call to `rpmbuild` to facilitate more consistent in-RPM file timestamps.

Negative values (like the default) disable this feature. | Integer | optional | `-1` | | source_date_epoch_file | File containing the SOURCE_DATE_EPOCH value.

Implicitly sets the `%clamp_mtime_to_source_date_epoch` in the subordinate call to `rpmbuild` to facilitate more consistent in-RPM file timestamps. | Label | optional | `None` | | spec_template | Spec file template.

Use this if you need to add additional logic to your spec files that is not available by default.

In most cases, you should not need to override this attribute. | Label | optional | `"@rules_pkg//pkg/rpm:template.spec.tpl"` | +| stamp | Enable stamping for volatile release values. Possible values:
  • stamp = 1: Substitute workspace status variables in the release tag.
  • stamp = 0: No substitution; release tag used as-is.
  • stamp = -1: Controlled by the --[no]stamp flag. | Integer | optional | `0` | | subrpms | Sub RPMs to build with this RPM

    A list of `pkg_sub_rpm` instances that can be used to create sub RPMs as part of the overall package build.

    NOTE: use of `subrpms` is incompatible with the legacy `spec_file` mode | List of labels | optional | `[]` | | summary | RPM "Summary" tag.

    One-line summary of this package. Must not contain newlines. | String | required | | | url | RPM "URL" tag; this project/vendor's home on the Internet. | String | optional | `""` | @@ -510,7 +515,7 @@ will fail. See the individual attributes for details.
     load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup")
     
    -pkg_filegroup(name, srcs, prefix)
    +pkg_filegroup(name, srcs, package_variables, prefix)
     
    Package contents grouping rule. @@ -526,6 +531,7 @@ such as a prefix or a human-readable category. | :------------- | :------------- | :------------- | :------------- | :------------- | | name | A unique name for this target. | Name | required | | | srcs | A list of packaging specifications to be grouped together. | List of labels | required | | +| package_variables | See [Common Attributes](#package_variables) | Label | optional | `None` | | prefix | A prefix to prepend to provided paths, applied like so:

    - For files and directories, this is simply prepended to the destination - For symbolic links, this is prepended to the "destination" part. | String | optional | `""` | @@ -536,7 +542,8 @@ such as a prefix or a human-readable category.
     load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
     
    -pkg_files(name, srcs, attributes, excludes, include_runfiles, prefix, renames, strip_prefix)
    +pkg_files(name, srcs, attributes, excludes, include_runfiles, package_variables, prefix, renames,
    +          strip_prefix)
     
    General-purpose package target-to-destination mapping rule. @@ -562,6 +569,7 @@ special requirements. Consult `pkg_mkdirs` for more details. | attributes | Attributes to set on packaged files.

    Always use `pkg_attributes()` to set this rule attribute.

    If not otherwise overridden, the file's mode will be set to UNIX "0644", or the target platform's equivalent.

    Consult the "Mapping Attributes" documentation in the rules_pkg reference for more details. | String | optional | `"{}"` | | excludes | List of files or labels to exclude from the inputs to this rule.

    Mostly useful for removing files from generated outputs or preexisting `filegroup`s. | List of labels | optional | `[]` | | include_runfiles | Add runfiles for all srcs.

    The runfiles are in the paths that Bazel uses. For example, for the target `//my_prog:foo`, we would see files under paths like `foo.runfiles//my_prog/` | Boolean | optional | `False` | +| package_variables | See [Common Attributes](#package_variables) | Label | optional | `None` | | prefix | Installation prefix.

    This may be an arbitrary string, but it should be understandable by the packaging system you are using to have the desired outcome. For example, RPM macros like `%{_libdir}` may work correctly in paths for RPM packages, not, say, Debian packages.

    If any part of the directory structure of the computed destination of a file provided to `pkg_filegroup` or any similar rule does not already exist within a package, the package builder will create it for you with a reasonable set of default permissions (typically `0755 root.root`).

    It is possible to establish directory structures with arbitrary permissions using `pkg_mkdirs`. | String | optional | `""` | | renames | Destination override map.

    This attribute allows the user to override destinations of files in `pkg_file`s relative to the `prefix` attribute. Keys to the dict are source files/labels, values are destinations relative to the `prefix`, ignoring whatever value was provided for `strip_prefix`.

    If the key refers to a TreeArtifact (directory output), you may specify the constant `REMOVE_BASE_DIRECTORY` as the value, which will result in all containing files and directories being installed relative to the otherwise specified install prefix (via the `prefix` and `strip_prefix` attributes), not the directory name.

    The following keys are rejected:

    - Any label that expands to more than one file (mappings must be one-to-one).

    - Any label or file that was either not provided or explicitly `exclude`d.

    The following values result in undefined behavior:

    - "" (the empty string)

    - "."

    - Anything containing ".." | Dictionary: Label -> String | optional | `{}` | | strip_prefix | What prefix of a file's path to discard prior to installation.

    This specifies what prefix of an incoming file's path should not be included in the output package at after being appended to the install prefix (the `prefix` attribute). Note that this is only applied to full directory names, see `strip_prefix` for more details.

    Use the `strip_prefix` struct to define this attribute. If this attribute is not specified, all directories will be stripped from all files prior to being included in packages (`strip_prefix.files_only()`).

    If prefix stripping fails on any file provided in `srcs`, the build will fail.

    Note that this only functions on paths that are known at analysis time. Specifically, this will not consider directories within TreeArtifacts (directory outputs), or the directories themselves. See also #269. | String | optional | `"."` | diff --git a/pkg/mappings.bzl b/pkg/mappings.bzl index 0e44158a..beeb3923 100644 --- a/pkg/mappings.bzl +++ b/pkg/mappings.bzl @@ -28,8 +28,15 @@ here. """ load("@bazel_skylib//lib:paths.bzl", "paths") -load("//pkg:providers.bzl", "PackageDirsInfo", "PackageFilegroupInfo", "PackageFilesInfo", "PackageSymlinkInfo") -load("//pkg/private:util.bzl", "get_repo_mapping_manifest") +load( + "//pkg:providers.bzl", + "PackageDirsInfo", + "PackageFilegroupInfo", + "PackageFilesInfo", + "PackageSymlinkInfo", + "PackageVariablesInfo", +) +load("//pkg/private:util.bzl", "get_repo_mapping_manifest", "substitute_package_variables") # TODO(#333): strip_prefix module functions should produce unique outputs. In # particular, this one and `_sp_from_pkg` can overlap. @@ -216,6 +223,7 @@ def _path_relative_to_repo_root(file): def _pkg_files_impl(ctx): # The input sources are already known. Let's calculate the destinations... + prefix = substitute_package_variables(ctx, ctx.attr.prefix) # Exclude excludes srcs = [] # srcs is source File objects, not Targets @@ -227,11 +235,11 @@ def _pkg_files_impl(ctx): file_to_target[f] = src if ctx.attr.strip_prefix == _PKGFILEGROUP_STRIP_ALL: - src_dest_paths_map = {src: paths.join(ctx.attr.prefix, src.basename) for src in srcs} + src_dest_paths_map = {src: paths.join(prefix, src.basename) for src in srcs} elif ctx.attr.strip_prefix.startswith("/"): # Relative to workspace/repository root src_dest_paths_map = {src: paths.join( - ctx.attr.prefix, + prefix, _do_strip_prefix( _path_relative_to_repo_root(src), ctx.attr.strip_prefix[1:], @@ -241,7 +249,7 @@ def _pkg_files_impl(ctx): else: # Relative to package src_dest_paths_map = {src: paths.join( - ctx.attr.prefix, + prefix, _do_strip_prefix( _path_relative_to_package(src), ctx.attr.strip_prefix, @@ -283,10 +291,10 @@ def _pkg_files_impl(ctx): # REMOVE_BASE_DIRECTORY results in the contents being dropped into # place directly in the prefix path. - src_dest_paths_map[src_file] = ctx.attr.prefix + src_dest_paths_map[src_file] = prefix else: - src_dest_paths_map[src_file] = paths.join(ctx.attr.prefix, rename_dest) + src_dest_paths_map[src_file] = paths.join(prefix, rename_dest) # At this point, we have a fully valid src -> dest mapping for all the # explicitly named targets in srcs. Now we can fill in their runfiles. @@ -466,6 +474,10 @@ pkg_files = rule( `foo.runfiles//my_prog/` """, ), + "package_variables": attr.label( + doc = """See [Common Attributes](#package_variables)""", + providers = [PackageVariablesInfo], + ), }, provides = [PackageFilesInfo], ) @@ -632,8 +644,9 @@ def _pkg_filegroup_impl(ctx): dirs = [] links = [] mapped_files_depsets = [] + prefix = substitute_package_variables(ctx, ctx.attr.prefix) - if ctx.attr.prefix: + if prefix: # If "prefix" is provided, we need to manipulate the incoming providers. for s in ctx.attr.srcs: if PackageFilegroupInfo in s: @@ -643,7 +656,7 @@ def _pkg_filegroup_impl(ctx): ( PackageFilesInfo( dest_src_map = { - paths.join(ctx.attr.prefix, dest): src + paths.join(prefix, dest): src for dest, src in pfi.dest_src_map.items() }, attributes = pfi.attributes, @@ -655,7 +668,7 @@ def _pkg_filegroup_impl(ctx): dirs += [ ( PackageDirsInfo( - dirs = [paths.join(ctx.attr.prefix, d) for d in pdi.dirs], + dirs = [paths.join(prefix, d) for d in pdi.dirs], attributes = pdi.attributes, ), origin, @@ -666,7 +679,7 @@ def _pkg_filegroup_impl(ctx): ( PackageSymlinkInfo( target = psi.target, - destination = paths.join(ctx.attr.prefix, psi.destination), + destination = paths.join(prefix, psi.destination), attributes = psi.attributes, ), origin, @@ -679,7 +692,7 @@ def _pkg_filegroup_impl(ctx): if PackageFilesInfo in s: new_pfi = PackageFilesInfo( dest_src_map = { - paths.join(ctx.attr.prefix, dest): src + paths.join(prefix, dest): src for dest, src in s[PackageFilesInfo].dest_src_map.items() }, attributes = s[PackageFilesInfo].attributes, @@ -691,7 +704,7 @@ def _pkg_filegroup_impl(ctx): if PackageDirsInfo in s: new_pdi = PackageDirsInfo( - dirs = [paths.join(ctx.attr.prefix, d) for d in s[PackageDirsInfo].dirs], + dirs = [paths.join(prefix, d) for d in s[PackageDirsInfo].dirs], attributes = s[PackageDirsInfo].attributes, ) dirs.append((new_pdi, s.label)) @@ -699,7 +712,7 @@ def _pkg_filegroup_impl(ctx): if PackageSymlinkInfo in s: new_psi = PackageSymlinkInfo( target = s[PackageSymlinkInfo].target, - destination = paths.join(ctx.attr.prefix, s[PackageSymlinkInfo].destination), + destination = paths.join(prefix, s[PackageSymlinkInfo].destination), attributes = s[PackageSymlinkInfo].attributes, ) links.append((new_psi, s.label)) @@ -763,6 +776,10 @@ pkg_filegroup = rule( """, ), + "package_variables": attr.label( + doc = """See [Common Attributes](#package_variables)""", + providers = [PackageVariablesInfo], + ), }, provides = [PackageFilegroupInfo], ) diff --git a/tests/mappings/mappings_test.bzl b/tests/mappings/mappings_test.bzl index 27a61cba..f663c526 100644 --- a/tests/mappings/mappings_test.bzl +++ b/tests/mappings/mappings_test.bzl @@ -34,6 +34,7 @@ load( "PackageFilesInfo", "PackageSymlinkInfo", ) +load("//tests:my_package_name.bzl", "my_package_naming") load( "//tests/util:defs.bzl", "directory", @@ -903,6 +904,72 @@ def _test_pkg_filegroup(name): ], ) +########## +# Test package_variables substitution in prefix +########## + +def _test_pkg_files_package_variables(): + my_package_naming( + name = "pf_pkg_vars_naming", + label = "amazing", + tags = ["manual"], + ) + + pkg_files( + name = "pf_with_package_variables_g", + srcs = ["testdata/hello.txt"], + prefix = "usr/$(label)/share", + package_variables = ":pf_pkg_vars_naming", + tags = ["manual"], + ) + + pkg_files_contents_test( + name = "pf_with_package_variables", + target_under_test = ":pf_with_package_variables_g", + expected_dests = ["usr/amazing/share/hello.txt"], + ) + +def _test_pkg_filegroup_package_variables(): + my_package_naming( + name = "pfg_pkg_vars_naming", + label = "amazing", + tags = ["manual"], + ) + + # Inner pkg_files with a literal prefix; variable substitution happens in + # the pkg_filegroup that wraps it. + pkg_files( + name = "pfg_pkg_vars_inner_files_g", + srcs = ["foo", "bar"], + prefix = "bin", + tags = ["manual"], + ) + + pkg_filegroup( + name = "pfg_with_package_variables_g", + srcs = [":pfg_pkg_vars_inner_files_g"], + prefix = "usr/$(label)", + package_variables = ":pfg_pkg_vars_naming", + tags = ["manual"], + ) + + # Reference target: the expected result after variable substitution. + pkg_files( + name = "pfg_pkg_vars_expected_g", + srcs = ["foo", "bar"], + prefix = "usr/amazing/bin", + tags = ["manual"], + ) + + pkg_filegroup_contents_test( + name = "pfg_with_package_variables", + target_under_test = ":pfg_with_package_variables_g", + expected_pkg_files = [":pfg_pkg_vars_expected_g"], + # Origins will differ (inner target vs. reference target); only check + # the resolved destinations. + verify_origins = False, + ) + ########## # Test strip_prefix pseudo-module ########## @@ -930,6 +997,8 @@ def mappings_analysis_tests(): # TODO(nacl) migrate the above to use a scheme the one used here. At the very # least, the test suites should be easy to find/name. _test_pkg_filegroup(name = "pfg_tests") + _test_pkg_files_package_variables() + _test_pkg_filegroup_package_variables() native.test_suite( name = "pkg_files_analysis_tests", @@ -968,6 +1037,9 @@ def mappings_analysis_tests(): ":pkg_mklink_mode_overlay_if_not_provided", # Tests involving pkg_filegroup ":pfg_tests", + # Tests for package_variables in prefix + ":pf_with_package_variables", + ":pfg_with_package_variables", ], )