Skip to content

Commit 082bd87

Browse files
authored
Add label, start using it in crawler (#197)
1 parent 9cadff5 commit 082bd87

File tree

11 files changed

+701
-187
lines changed

11 files changed

+701
-187
lines changed

BUILD

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ py_test(
156156
python_version = python_version,
157157
)
158158

159+
py_test(
160+
name = "labeltest",
161+
srcs = ["tests/labeltest.py"],
162+
deps = [":pomgen_lib"],
163+
imports = ["src"],
164+
size = "small",
165+
python_version = python_version,
166+
)
167+
159168
py_test(
160169
name = "libaggregatortest",
161170
srcs = ["tests/libaggregatortest.py"],
@@ -230,7 +239,6 @@ py_test(
230239

231240
py_test(
232241
name = "versiontest",
233-
234242
srcs = ["tests/versiontest.py"],
235243
deps = [":pomgen_lib"],
236244
imports = ["src"],

misc/extdeps_pomgen.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ class ThirdPartyDepsPomGen(pom.DynamicPomGen):
5353

5454
def __init__(self, workspace, artifact_def, dependencies, pom_template):
5555
super(ThirdPartyDepsPomGen, self).__init__(workspace, artifact_def,
56-
dependency=None,
5756
pom_template=pom_template)
5857
self.dependencies = dependencies
5958

src/common/label.py

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
"""
2+
Copyright (c) 2025, salesforce.com, inc.
3+
All rights reserved.
4+
SPDX-License-Identifier: BSD-3-Clause
5+
For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
7+
This module has abstractions for Bazel labels.
8+
"""
9+
10+
11+
import os
12+
13+
14+
_BUILD_FILE_NAMES = ("BUILD", "BUILD.bazel")
15+
16+
17+
def for_package(root_dir, package_path):
18+
"""
19+
Returns a label instance for the Bazel package at the given relative path,
20+
rooted at the given root_dir. Returns None if no build file exists at that
21+
location.
22+
"""
23+
for fname in _BUILD_FILE_NAMES:
24+
rel_path = os.path.join(package_path, fname)
25+
abs_path = os.path.join(root_dir, rel_path)
26+
if os.path.isfile(abs_path):
27+
return Label(rel_path)
28+
return None
29+
30+
31+
def find_packages(root_dir, package_path=""):
32+
"""
33+
Walks the directory tree, starting at root dir, and returns a list of
34+
Label instances for all Bazel packages that exist under the given root_dir.
35+
36+
If package_path is specified, the search starts at that location.
37+
"""
38+
labels = []
39+
for path, dirs, files in os.walk(os.path.join(root_dir, package_path)):
40+
for fname in files:
41+
if fname in _BUILD_FILE_NAMES:
42+
rel_path = os.path.join(os.path.relpath(path, root_dir), fname)
43+
if rel_path.startswith("./"):
44+
# build file at root dir, remove "./" so that the package
45+
# of the Label is empty
46+
rel_path = rel_path[2:]
47+
labels.append(Label(rel_path))
48+
return labels
49+
50+
51+
class Label(object):
52+
"""
53+
Represents a Bazel Label.
54+
"""
55+
56+
def __init__(self, name):
57+
"""
58+
Initializes a Label with the given name, a string. name represents
59+
a path-like structure with an optional target [path:target].
60+
61+
If the last path segment is a build file (/BUILD or /BUILD.bazel),
62+
it is removed from the path.
63+
64+
"""
65+
assert name is not None
66+
name = name.strip()
67+
if name.endswith("/"):
68+
name = name[:-1]
69+
fname = os.path.basename(name)
70+
if fname in ("BUILD", "BUILD.bazel"):
71+
name = os.path.dirname(name)
72+
self._build_file_name = fname
73+
else:
74+
self._build_file_name = None
75+
self._name = name
76+
77+
@property
78+
def name(self):
79+
"""
80+
The name this instance was initialized with.
81+
"""
82+
return self._name
83+
84+
@property
85+
def package(self):
86+
"""
87+
The Bazel Package of this label.
88+
For example, for "//a/b/c:foo", return "//a/b/c"
89+
"""
90+
i = self._name.find(":")
91+
if i == -1:
92+
if self._name.endswith("..."):
93+
return self._name[:-3]
94+
return self._name
95+
return self._name[0:i]
96+
97+
@property
98+
def package_path(self):
99+
"""
100+
Returns the Package of this label as a valid relative path.
101+
"""
102+
p = self.package
103+
if p.startswith("//"):
104+
p = p[2:]
105+
if p.endswith("/"):
106+
p = p[:-1]
107+
return p
108+
109+
@property
110+
def target(self):
111+
"""
112+
The Bazel Target of this label.
113+
For example, for "//a/b/c:foo", return "foo"
114+
"""
115+
i = self._name.find(":")
116+
if i == -1:
117+
return os.path.basename(self._name)
118+
return self._name[i+1:]
119+
120+
@property
121+
def target_name(self):
122+
"""
123+
An alias for the "target" property.
124+
"""
125+
return self.target
126+
127+
@property
128+
def is_default_target(self):
129+
"""
130+
Returns True if this label refers to the default target in the package,
131+
ie the target that has the same name as the directory the BUILD file
132+
lives in.
133+
"""
134+
package = self.package
135+
target = self.target
136+
if package is None:
137+
return False
138+
if target is None:
139+
return True
140+
return os.path.basename(package) == target
141+
142+
@property
143+
def is_root_target(self):
144+
"""
145+
Returns True if this label's target is defined in the root BUILD
146+
file, ie if the label has this pattern "//:"
147+
"""
148+
return "//:" in self._name
149+
150+
@property
151+
def fqname(self):
152+
"""
153+
The name of this label with a default repo prefix, iff the
154+
initial name did not specify such a prefix and this is not a src ref.
155+
"""
156+
if self.is_source_ref:
157+
return self._name
158+
if self.has_repo_prefix:
159+
return self._name
160+
else:
161+
# the default prefix we use for names without repo prefix:
162+
# if name is foo, fqname will be @maven//:foo
163+
# "maven" doesn't really make sense to use anymore, but it isn't
164+
# clear what to use instead - probably defaulting the repo doesn't
165+
# make sense
166+
default_repo = "maven"
167+
return self.prefix_with(default_repo).name
168+
169+
@property
170+
def simple_name(self):
171+
"""
172+
The name of this label without the remote repo prefix.
173+
If this label does not have a remote repo prefix, returns just
174+
its name.
175+
"""
176+
if self.is_source_ref:
177+
return self._name
178+
if self.has_repo_prefix:
179+
prefix = self.repo_prefix
180+
return self._name[len(prefix)+4:] # 4 = additional chars @//:
181+
else:
182+
return self._name
183+
184+
@property
185+
def is_private(self):
186+
"""
187+
Returns True if this label refers to a private target (starts with ":")
188+
"""
189+
return self._name.startswith(":")
190+
191+
@property
192+
def has_repo_prefix(self):
193+
"""
194+
Whether this label name has a remote repo prefix.
195+
"""
196+
return self.repo_prefix is not None
197+
198+
@property
199+
def repo_prefix(self):
200+
"""
201+
The remote repo prefix, or workspace name of this label; None if this
202+
label name doesn't have one.
203+
204+
For example, for a label like "@pomgen//maven", returns "pomgen".
205+
"""
206+
if self._name.startswith("@"):
207+
i = self._name.find("//")
208+
if i != -1:
209+
return self._name[1:i]
210+
return None
211+
212+
@property
213+
def is_source_ref(self):
214+
"""
215+
True if this name is a reference to source in the same repository.
216+
"""
217+
return self._name.startswith("//")
218+
219+
@property
220+
def has_file_extension(self):
221+
ext = os.path.splitext(self._name)[1]
222+
return ext in (".jar", ".proto", ".h", ".c", ".cc", ".cpp", ".m", ".py", ".pyc", ".java", ".go")
223+
224+
@property
225+
def has_extension_suffix(self):
226+
return self._name.endswith("_extension")
227+
228+
@property
229+
def is_sources_artifact(self):
230+
return "_jar_sources" in self._name
231+
232+
@property
233+
def build_file_path(self):
234+
"""
235+
The path to the build file of this package, if this Label instance was
236+
created with a path that pointed to a build file.
237+
None if this Label instance does not know about the build file it was
238+
created for.
239+
"""
240+
if self._build_file_name is None:
241+
return None
242+
return os.path.join(self.package_path, self._build_file_name)
243+
244+
def prefix_with(self, repo_prefix):
245+
"""
246+
Returns a new Label instance that is qualified with the
247+
specified repo_prefix. This method asserts that this instance is not
248+
already fully qualified.
249+
"""
250+
assert not self.has_repo_prefix, "This label already has a repo prefix: %s" % self._name
251+
return Label("@%s//:%s" % (repo_prefix, self._name))
252+
253+
def with_target(self, target):
254+
"""
255+
Returns a new Label instance that has the specified target.
256+
"""
257+
return Label("%s:%s" % (self.package, target))
258+
259+
def as_wildcard_label(self, wildcard):
260+
if wildcard == "...":
261+
return Label("%s/%s" % (self.package, wildcard))
262+
else:
263+
return Label("%s:%s" % (self.package, wildcard))
264+
265+
def as_alternate_default_target_syntax(self):
266+
"""
267+
Labels may omit the target if they refer to the default target, or they
268+
may not omit it. If this Label instance refers to the default target,
269+
this method returns the other syntax.
270+
So:
271+
Given this Label instance is: //a/b/c, returns //a/b/c:c
272+
Or, given this Label instance is //a/b/c:c, returns //a/b/c
273+
"""
274+
assert self.is_default_target, "label must refer to the default target"
275+
if ":" in self.name:
276+
return Label(self.package)
277+
else:
278+
return Label("%s:%s" % (self.package, self.target))
279+
280+
def __hash__(self):
281+
return hash((self.package_path, self.target))
282+
283+
def __eq__(self, other):
284+
if other is None:
285+
return False
286+
return self.package_path == other.package_path and self.target == other.target
287+
288+
def __ne__(self, other):
289+
return not self == other
290+
291+
def __len__(self):
292+
return len(self._name)
293+
294+
def __repr__(self):
295+
return self._name
296+
297+
__str__ = __repr__

src/crawl/artifactgenctx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(self, workspace, pom_template, artifact_def, dependency):
1616

1717
# TODO remove/make this factory/config based
1818
self._generator = crawl.pom.get_pom_generator(
19-
workspace, pom_template, artifact_def, dependency)
19+
workspace, pom_template, artifact_def)
2020

2121
@property
2222
def artifact_def(self):

0 commit comments

Comments
 (0)