forked from v2ray/v2ray-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzip.bzl
164 lines (155 loc) · 5.35 KB
/
zip.bzl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Copied from google/nomulus project as we don't want to import the whole repository.
ZIPPER = "@bazel_tools//tools/zip:zipper"
def long_path(ctx, file_):
"""Constructs canonical runfile path relative to TEST_SRCDIR.
Args:
ctx: A Skylark rule context.
file_: A File object that should appear in the runfiles for the test.
Returns:
A string path relative to TEST_SRCDIR suitable for use in tests and
testing infrastructure.
"""
if file_.short_path.startswith("../"):
return file_.short_path[3:]
if file_.owner and file_.owner.workspace_root:
return file_.owner.workspace_root + "/" + file_.short_path
return ctx.workspace_name + "/" + file_.short_path
def collect_runfiles(targets):
"""Aggregates runfiles from targets.
Args:
targets: A list of Bazel targets.
Returns:
A list of Bazel files.
"""
data = depset()
for target in targets:
if hasattr(target, "runfiles"):
data += target.runfiles.files
continue
if hasattr(target, "data_runfiles"):
data += target.data_runfiles.files
if hasattr(target, "default_runfiles"):
data += target.default_runfiles.files
return data
def _get_runfiles(target, attribute):
runfiles = getattr(target, attribute, None)
if runfiles:
return runfiles.files
return []
def _zip_file(ctx):
"""Implementation of zip_file() rule."""
for s, d in ctx.attr.mappings.items():
if (s.startswith("/") or s.endswith("/") or
d.startswith("/") or d.endswith("/")):
fail("mappings should not begin or end with slash")
srcs = depset()
srcs += ctx.files.srcs
srcs += ctx.files.data
srcs += collect_runfiles(ctx.attr.data)
mapped = _map_sources(ctx, srcs, ctx.attr.mappings)
cmd = [
"#!/bin/sh",
"set -e",
'repo="$(pwd)"',
'zipper="${repo}/%s"' % ctx.file._zipper.path,
'archive="${repo}/%s"' % ctx.outputs.out.path,
'tmp="$(mktemp -d "${TMPDIR:-/tmp}/zip_file.XXXXXXXXXX")"',
'cd "${tmp}"',
]
cmd += [
'"${zipper}" x "${repo}/%s"' % dep.zip_file.path
for dep in ctx.attr.deps
]
cmd += ["rm %s" % filename for filename in ctx.attr.exclude]
cmd += [
'mkdir -p "${tmp}/%s"' % zip_path
for zip_path in depset(
[
zip_path[:zip_path.rindex("/")]
for _, zip_path in mapped
if "/" in zip_path
],
)
]
cmd += [
'ln -sf "${repo}/%s" "${tmp}/%s"' % (path, zip_path)
for path, zip_path in mapped
]
cmd += [
("find . | sed 1d | cut -c 3- | LC_ALL=C sort" +
' | xargs "${zipper}" cC "${archive}"'),
'cd "${repo}"',
'rm -rf "${tmp}"',
]
script = ctx.new_file(ctx.bin_dir, "%s.sh" % ctx.label.name)
ctx.file_action(output = script, content = "\n".join(cmd), executable = True)
inputs = [ctx.file._zipper]
inputs += [dep.zip_file for dep in ctx.attr.deps]
inputs += list(srcs)
ctx.action(
inputs = inputs,
outputs = [ctx.outputs.out],
executable = script,
mnemonic = "zip",
progress_message = "Creating zip with %d inputs %s" % (
len(inputs),
ctx.label,
),
)
return struct(files = depset([ctx.outputs.out]), zip_file = ctx.outputs.out)
def _map_sources(ctx, srcs, mappings):
"""Calculates paths in zip file for srcs."""
# order mappings with more path components first
mappings = sorted([
(-len(source.split("/")), source, dest)
for source, dest in mappings.items()
])
# get rid of the integer part of tuple used for sorting
mappings = [(source, dest) for _, source, dest in mappings]
mappings_indexes = range(len(mappings))
used = {i: False for i in mappings_indexes}
mapped = []
for file_ in srcs:
run_path = long_path(ctx, file_)
zip_path = None
for i in mappings_indexes:
source = mappings[i][0]
dest = mappings[i][1]
if not source:
if dest:
zip_path = dest + "/" + run_path
else:
zip_path = run_path
elif source == run_path:
if dest:
zip_path = dest
else:
zip_path = run_path
elif run_path.startswith(source + "/"):
if dest:
zip_path = dest + run_path[len(source):]
else:
zip_path = run_path[len(source) + 1:]
else:
continue
used[i] = True
break
if not zip_path:
fail("no mapping matched: " + run_path)
mapped += [(file_.path, zip_path)]
for i in mappings_indexes:
if not used[i]:
fail('superfluous mapping: "%s" -> "%s"' % mappings[i])
return mapped
pkg_zip = rule(
implementation = _zip_file,
attrs = {
"out": attr.output(mandatory = True),
"srcs": attr.label_list(allow_files = True),
"data": attr.label_list(allow_files = True),
"deps": attr.label_list(providers = ["zip_file"]),
"exclude": attr.string_list(),
"mappings": attr.string_dict(),
"_zipper": attr.label(default = Label(ZIPPER), single_file = True),
},
)