Skip to content

Commit 672c09e

Browse files
committed
Hoist pkg_content functions into public scope
This makes it possible to write extensions for rules_pkg like custom installers or easily read the structure from existing pkg_* rules
1 parent 3612404 commit 672c09e

File tree

8 files changed

+284
-205
lines changed

8 files changed

+284
-205
lines changed

doc_build/BUILD

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ ORDER = [
5555
("PackageFilesInfo", "//providers:providers.bzl"),
5656
("PackageSymlinkInfo", "//providers:providers.bzl"),
5757
("pkg_install", None),
58+
("package_content", None),
5859
("legacy_pkg_rpm", None),
5960
]
6061

@@ -108,6 +109,21 @@ stardoc(
108109
],
109110
)
110111

112+
stardoc(
113+
name = "package_content",
114+
out = "package_content.md",
115+
input = "//pkg:package_content.bzl",
116+
symbol_names = [
117+
"create_mapping_context_from_ctx",
118+
"add_label_list",
119+
"process_src",
120+
"write_manifest",
121+
],
122+
deps = [
123+
"//pkg:rules_pkg_lib",
124+
],
125+
)
126+
111127
stardoc(
112128
name = "pkg_install",
113129
out = "pkg_install.md",

doc_build/toc.md.tpl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@
2525
<li><a href="#pkg_attributes">//pkg:mappings.bzl%pkg_attributes</a></li>
2626
<li><a href="#strip_prefix.files_only">//pkg:mappings.bzl%strip_prefix</a></li>
2727
</ul>
28+
29+
<h2>Extending rules_pkg</h2>
30+
<ul>
31+
<li><a href="#create_mapping_context_from_ctx">//pkg:package_content.bzl%create_mapping_context_from_ctx</a></li>
32+
<li><a href="#add_label_list">//pkg:package_content.bzl%add_label_list</a></li>
33+
<li><a href="#process_src">//pkg:package_content.bzl%process_src</a></li>
34+
<li><a href="#write_manifest">//pkg:package_content.bzl%write_manifest</a></li>
35+
</ul>
2836
</div>

pkg/install.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ run`-able installation script.
2020
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
2121
load("@rules_python//python:defs.bzl", "py_binary")
2222
load("//pkg:providers.bzl", "PackageDirsInfo", "PackageFilegroupInfo", "PackageFilesInfo", "PackageSymlinkInfo")
23-
load("//pkg/private:pkg_files.bzl", "create_mapping_context_from_ctx", "process_src", "write_manifest")
23+
load("//pkg:package_content.bzl", "create_mapping_context_from_ctx", "process_src", "write_manifest")
2424

2525
def _pkg_install_script_impl(ctx):
2626
script_file = ctx.actions.declare_file(ctx.attr.name + ".py")

pkg/package_content.bzl

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Copyright 2021 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Utilities for collecting and serializing package content.
15+
16+
This module provides the building blocks for implementing custom packaging rules
17+
on top of `rules_pkg`. A typical custom rule implementation will:
18+
19+
1. Call `create_mapping_context_from_ctx` to set up a `MappingContext`.
20+
2. Call `add_label_list` to resolve source labels into a content map.
21+
3. Call `write_manifest` to serialize the content map as a JSON manifest.
22+
23+
Example usage in a custom rule implementation:
24+
25+
```starlark
26+
load("@rules_pkg//pkg:package_content.bzl",
27+
"add_label_list",
28+
"create_mapping_context_from_ctx",
29+
"write_manifest",
30+
)
31+
32+
def _my_packager_impl(ctx):
33+
mapping_context = create_mapping_context_from_ctx(ctx, ctx.label)
34+
add_label_list(mapping_context, ctx.attr.srcs)
35+
36+
manifest = ctx.actions.declare_file(ctx.attr.name + "-manifest.json")
37+
write_manifest(ctx, manifest, mapping_context.content_map)
38+
...
39+
```
40+
"""
41+
42+
load("//pkg:path.bzl", "compute_data_path")
43+
load(
44+
"//pkg:providers.bzl",
45+
"PackageDirsInfo",
46+
"PackageFilegroupInfo",
47+
"PackageFilesInfo",
48+
"PackageSymlinkInfo",
49+
)
50+
load(
51+
"//pkg/private:pkg_files.bzl",
52+
"MappingContext",
53+
"add_from_default_info",
54+
"encode_manifest_entry",
55+
"process_pkg_dirs",
56+
"process_pkg_filegroup",
57+
"process_pkg_files",
58+
"process_pkg_symlink",
59+
)
60+
61+
# buildifier: disable=function-docstring-args
62+
def create_mapping_context_from_ctx(
63+
ctx,
64+
label,
65+
allow_duplicates_with_different_content = None,
66+
strip_prefix = None,
67+
include_runfiles = None,
68+
default_mode = None,
69+
path_mapper = None):
70+
"""Construct a MappingContext.
71+
72+
Args: See the provider definition.
73+
74+
Returns:
75+
MappingContext
76+
"""
77+
if allow_duplicates_with_different_content == None:
78+
allow_duplicates_with_different_content = ctx.attr.allow_duplicates_with_different_content if hasattr(ctx.attr, "allow_duplicates_with_different_content") else False
79+
if strip_prefix == None:
80+
strip_prefix = ctx.attr.strip_prefix if hasattr(ctx.attr, "strip_prefix") else ""
81+
if include_runfiles == None:
82+
include_runfiles = ctx.attr.include_runfiles if hasattr(ctx.attr, "include_runfiles") else False
83+
if default_mode == None:
84+
default_mode = ctx.attr.mode if hasattr(ctx.attr, "default_mode") else ""
85+
86+
return MappingContext(
87+
content_map = dict(),
88+
file_deps_direct = list(),
89+
file_deps_transitive = list(),
90+
label = label,
91+
allow_duplicates_with_different_content = allow_duplicates_with_different_content,
92+
strip_prefix = strip_prefix,
93+
include_runfiles = include_runfiles,
94+
workspace_name = ctx.workspace_name,
95+
default_mode = default_mode,
96+
path_mapper = path_mapper or (lambda x: x),
97+
# TODO(aiuto): allow these to be passed in as needed. But, before doing
98+
# that, explore defauilt_uid/gid as 0 rather than None
99+
default_user = "",
100+
default_group = "",
101+
default_uid = None,
102+
default_gid = None,
103+
)
104+
105+
def process_src(mapping_context, src, origin):
106+
"""Add an entry to the content map.
107+
108+
Args:
109+
mapping_context: (r/w) a MappingContext
110+
src: Source Package*Info object
111+
origin: The rule instance adding this entry
112+
113+
Returns:
114+
True if src was a Package*Info and added to content_map.
115+
"""
116+
117+
# Gather the files for every srcs entry here, even if it is not from
118+
# a pkg_* rule.
119+
if DefaultInfo in src:
120+
mapping_context.file_deps_transitive.append(src[DefaultInfo].files)
121+
found_info = False
122+
if PackageFilesInfo in src:
123+
process_pkg_files(
124+
mapping_context,
125+
src[PackageFilesInfo],
126+
origin,
127+
)
128+
found_info = True
129+
if PackageFilegroupInfo in src:
130+
process_pkg_filegroup(
131+
mapping_context,
132+
src[PackageFilegroupInfo],
133+
)
134+
found_info = True
135+
if PackageSymlinkInfo in src:
136+
process_pkg_symlink(
137+
mapping_context,
138+
src[PackageSymlinkInfo],
139+
origin,
140+
)
141+
found_info = True
142+
if PackageDirsInfo in src:
143+
process_pkg_dirs(
144+
mapping_context,
145+
src[PackageDirsInfo],
146+
origin,
147+
)
148+
found_info = True
149+
return found_info
150+
151+
def add_label_list(mapping_context, srcs):
152+
"""Helper method to add a list of labels (typically 'srcs') to a content_map.
153+
154+
Args:
155+
mapping_context: (r/w) a MappingContext
156+
srcs: List of source objects
157+
"""
158+
159+
# Compute the relative path
160+
data_path = compute_data_path(
161+
mapping_context.label,
162+
mapping_context.strip_prefix,
163+
)
164+
data_path_without_prefix = compute_data_path(
165+
mapping_context.label,
166+
".",
167+
)
168+
169+
for src in srcs:
170+
if not process_src(
171+
mapping_context,
172+
src = src,
173+
origin = src.label,
174+
):
175+
# Add in the files of srcs which are not pkg_* types
176+
add_from_default_info(
177+
mapping_context,
178+
src,
179+
data_path,
180+
data_path_without_prefix,
181+
mapping_context.include_runfiles,
182+
mapping_context.workspace_name,
183+
)
184+
185+
def write_manifest(ctx, manifest_file, content_map, use_short_path = False, pretty_print = False):
186+
"""Write a content map to a manifest file.
187+
188+
The format of this file is currently undocumented, as it is a private
189+
contract between the rule implementation and the package writers. It will
190+
become a published interface in a future release.
191+
192+
For reproducibility, the manifest file must be ordered consistently.
193+
194+
Args:
195+
ctx: rule context
196+
manifest_file: File object used as the output destination
197+
content_map: content_map (see concepts at top of file)
198+
use_short_path: write out the manifest file destinations in terms of "short" paths, suitable for `bazel run`.
199+
pretty_print: indent the output nicely. Takes more space so it is off by default.
200+
"""
201+
ctx.actions.write(
202+
manifest_file,
203+
"[\n" + ",\n".join(
204+
[
205+
encode_manifest_entry(ctx, dst, content_map[dst], use_short_path, pretty_print)
206+
for dst in sorted(content_map.keys())
207+
],
208+
) + "\n]\n",
209+
)

0 commit comments

Comments
 (0)