Skip to content

Commit 46eabd3

Browse files
authored
Create an example of the kind of link tree Node users must create (#603)
* Create an example of the kind of link tree Node users muse create
1 parent 60dbd92 commit 46eabd3

File tree

8 files changed

+156
-12
lines changed

8 files changed

+156
-12
lines changed

pkg/private/pkg_files.bzl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,15 @@ def _process_pkg_symlink(content_map, pkg_symlink_info, origin, default_mode, de
127127
)
128128

129129
def _process_pkg_filegroup(content_map, pkg_filegroup_info, origin, default_mode, default_user, default_group):
130-
for d in pkg_filegroup_info.pkg_dirs:
131-
_process_pkg_dirs(content_map, d[0], d[1], default_mode, default_user, default_group)
132-
for pf in pkg_filegroup_info.pkg_files:
133-
_process_pkg_files(content_map, pf[0], pf[1], default_mode, default_user, default_group)
134-
for psl in pkg_filegroup_info.pkg_symlinks:
135-
_process_pkg_symlink(content_map, psl[0], psl[1], default_mode, default_user, default_group)
130+
if hasattr(pkg_filegroup_info, "pkg_dirs"):
131+
for d in pkg_filegroup_info.pkg_dirs:
132+
_process_pkg_dirs(content_map, d[0], d[1], default_mode, default_user, default_group)
133+
if hasattr(pkg_filegroup_info, "pkg_files"):
134+
for pf in pkg_filegroup_info.pkg_files:
135+
_process_pkg_files(content_map, pf[0], pf[1], default_mode, default_user, default_group)
136+
if hasattr(pkg_filegroup_info, "pkg_symlinks"):
137+
for psl in pkg_filegroup_info.pkg_symlinks:
138+
_process_pkg_symlink(content_map, psl[0], psl[1], default_mode, default_user, default_group)
136139

137140
def process_src(content_map, files, src, origin, default_mode, default_user,
138141
default_group):

tests/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ filegroup(
3939
visibility = ["//visibility:public"],
4040
)
4141

42+
filegroup(
43+
name = "file_and_link",
44+
srcs = [
45+
"BUILD",
46+
"testdata/outer_BUILD",
47+
],
48+
visibility = ["//visibility:public"],
49+
)
50+
4251
filegroup(
4352
name = "glob_for_texts",
4453
srcs = glob(["**/*.txt"]),

tests/mappings/BUILD

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ load(
2727
"pkg_mkdirs",
2828
"strip_prefix",
2929
)
30-
load("//tests/util:defs.bzl", "directory", "write_content_manifest")
30+
load("//tests/util:defs.bzl", "directory", "link_tree", "write_content_manifest")
3131
load("@rules_python//python:defs.bzl", "py_test")
3232

3333
package(default_applicable_licenses = ["//:license"])
@@ -158,3 +158,30 @@ manifest_golden_test(
158158
expected = "glob_for_texts_manifest.golden",
159159
target = "glob_for_texts_manifest",
160160
)
161+
162+
link_tree(
163+
name = "node_modules",
164+
links = {
165+
"foo": ".pnpm/[email protected]/node_modules/foo",
166+
".pnpm/[email protected]/node_modules/bar": "STORE/bar",
167+
".pnpm/[email protected]/node_modules/qar": "../../[email protected]/node_modules/qar",
168+
".pnpm/[email protected]/node_modules/foo": "STORE/foo",
169+
".pnpm/[email protected]/node_modules/bar": "../../[email protected]/node_modules/bar",
170+
".pnpm/[email protected]/node_modules/qar": "../../[email protected]/node_modules/qar",
171+
".pnpm/[email protected]/node_modules/qar": "STORE/qar",
172+
},
173+
package_dir = "node_modules",
174+
)
175+
176+
write_content_manifest(
177+
name = "node_modules_manifest",
178+
srcs = [
179+
":node_modules",
180+
],
181+
)
182+
183+
manifest_golden_test(
184+
name = "link_tree_test",
185+
expected = "node_modules_manifest.golden",
186+
target = "node_modules_manifest",
187+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[
2+
[1,"node_modules/.pnpm/[email protected]/node_modules/bar","STORE/bar","",null,null],
3+
[1,"node_modules/.pnpm/[email protected]/node_modules/qar","../../[email protected]/node_modules/qar","",null,null],
4+
[1,"node_modules/.pnpm/[email protected]/node_modules/bar","../../[email protected]/node_modules/bar","",null,null],
5+
[1,"node_modules/.pnpm/[email protected]/node_modules/foo","STORE/foo","",null,null],
6+
[1,"node_modules/.pnpm/[email protected]/node_modules/qar","../../[email protected]/node_modules/qar","",null,null],
7+
[1,"node_modules/.pnpm/[email protected]/node_modules/qar","STORE/qar","",null,null],
8+
[1,"node_modules/foo",".pnpm/[email protected]/node_modules/foo","",null,null]
9+
]

tests/tar/BUILD

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
load("//pkg:mappings.bzl", "pkg_files", "strip_prefix")
1919
load("//pkg/private/tar:tar.bzl", "SUPPORTED_TAR_COMPRESSIONS", "pkg_tar")
2020
load("//tests:my_package_name.bzl", "my_package_naming")
21-
load("//tests/util:defs.bzl", "directory", "fake_artifact")
21+
load("//tests/util:defs.bzl", "directory", "link_tree", "fake_artifact")
2222
load("@rules_python//python:defs.bzl", "py_test")
2323
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
2424

25-
package(default_applicable_licenses = ["//:license"])
25+
package(
26+
default_applicable_licenses = ["//:license"],
27+
default_visibility = ["//visibility:private"],
28+
)
2629

2730
py_test(
2831
name = "tar_writer_test",
@@ -396,3 +399,49 @@ py_test(
396399
"@bazel_tools//tools/python/runfiles",
397400
],
398401
)
402+
403+
link_tree(
404+
name = "node_modules",
405+
links = {
406+
"foo": ".pnpm/[email protected]/node_modules/foo",
407+
".pnpm/[email protected]/node_modules/bar": "STORE/bar",
408+
".pnpm/[email protected]/node_modules/foo": "STORE/foo",
409+
".pnpm/[email protected]/node_modules/bar": "../../[email protected]/node_modules/bar",
410+
},
411+
package_dir = "node_modules",
412+
)
413+
414+
directory(
415+
name = "tree_artifact_with_links",
416+
contents = "hello there",
417+
filenames = [
418+
"foo/hello.txt",
419+
"foo/bar/baz",
420+
],
421+
links = {
422+
"foo/bar/hello": "../hello.txt",
423+
"foo/bar/alt_baz": "baz",
424+
"foo/alt_baz": "bar/baz",
425+
},
426+
)
427+
428+
429+
# This target has symlinks 3 ways.
430+
# - mklinks rules.
431+
# - source files that are symlinks
432+
# - tree artifacts
433+
pkg_tar(
434+
name = "relative_links_tar",
435+
srcs = [
436+
":node_modules",
437+
":tree_artifact_with_links",
438+
"//tests:file_and_link",
439+
],
440+
)
441+
442+
pkg_tar(
443+
name = "relative_links_re_tar",
444+
deps = [
445+
":relative_links_tar",
446+
],
447+
)

tests/testdata/outer_BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../BUILD

tests/util/create_directory_with_contents.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,13 @@
3636
os.makedirs(dirname, exist_ok=True)
3737

3838
for fname, contents in files_contents_map.items():
39+
path = os.path.join(dirname, fname)
3940
os.makedirs(
40-
os.path.join(dirname, os.path.dirname(fname)),
41+
os.path.dirname(path),
4142
exist_ok=True,
4243
)
43-
with open(os.path.join(dirname, fname), 'w') as fh:
44-
fh.write(contents)
44+
if contents.startswith('@@'):
45+
os.symlink(contents[2:], path)
46+
else:
47+
with open(path, 'w') as fh:
48+
fh.write(contents)

tests/util/defs.bzl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Rules to aid testing"""
1616

1717
load("//pkg/private:pkg_files.bzl", "add_label_list", "write_manifest")
18+
load("//pkg:providers.bzl", "PackageFilegroupInfo", "PackageSymlinkInfo")
1819
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
1920
load("@rules_python//python:defs.bzl", "py_binary")
2021

@@ -24,10 +25,15 @@ def _directory_impl(ctx):
2425
args = ctx.actions.args()
2526
args.add(out_dir_file.path)
2627

28+
# This helper is horrible. We should pass all the args in files.
2729
for fn in ctx.attr.filenames:
2830
args.add(fn)
2931
args.add(ctx.attr.contents)
3032

33+
for link, target in ctx.attr.links.items():
34+
args.add(link)
35+
args.add('@@' + target)
36+
3137
ctx.actions.run(
3238
outputs = [out_dir_file],
3339
inputs = [],
@@ -48,6 +54,11 @@ creation capabilities are "unsound".
4854
doc = """Paths to create in the directory.
4955
5056
Paths containing directories will also have the intermediate directories created too.""",
57+
),
58+
"links": attr.string_dict(
59+
doc = """Set of (virtual) links to create.
60+
61+
The keys of links are paths to create. The values are the target of the links.""",
5162
),
5263
"contents": attr.string(),
5364
"outdir": attr.string(),
@@ -101,6 +112,37 @@ cc_binary in complexity, but does not depend on a large toolchain.""",
101112
},
102113
)
103114

115+
def _link_tree_impl(ctx):
116+
links = []
117+
prefix = ctx.attr.package_dir or ""
118+
if prefix and not prefix.endswith('/'):
119+
prefix = prefix + "/"
120+
for link, target in ctx.attr.links.items():
121+
print(' %s -> %s ' % (link, target))
122+
links.append(
123+
(PackageSymlinkInfo(destination = prefix + link, target = target),
124+
ctx.label))
125+
return [PackageFilegroupInfo(pkg_symlinks = links)]
126+
127+
link_tree = rule(
128+
doc = """Helper rule to create a lot of fake symlinks.
129+
130+
The inspiration is to create test data for the kinds of layouts needed by
131+
nodejs. See. https://pnpm.io/symlinked-node-modules-structure
132+
""",
133+
implementation = _link_tree_impl,
134+
attrs = {
135+
"links": attr.string_dict(
136+
doc = """Set of (virtual) links to create.
137+
138+
The keys of links are paths to create. The values are the target of the links.""",
139+
mandatory = True,
140+
),
141+
"package_dir": attr.string(doc = """Prefix to apply to all link paths."""),
142+
},
143+
provides = [PackageFilegroupInfo],
144+
)
145+
104146
def _write_content_manifest_impl(ctx):
105147
content_map = {} # content handled in the manifest
106148
file_deps = [] # inputs we depend on

0 commit comments

Comments
 (0)