Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add buildlabel function #3110

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions rules/builtins.build_defs
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,142 @@ def is_semver(s:str) -> bool:

def semver_check(version:str, constraint:str) -> bool:
pass


# Returns build label
# https://github.com/thought-machine/please/blob/v17.3.1/src/core/build_label.go#L159
# https://github.com/thought-machine/please/blob/6cc3d7689ad78c667618b5ca9d089630864cd140/src/core/build_input.go#L226
def buildlabel(
label: str | list = "",
path: str = "",
target: str = "",
subtarget: str = "",
subrepo: str = "",
pkg: str = "",
package: str = "",
out: str = "",
name: str = "",
relative=False,
aslist=True,
):
"""Compose a build label

Args:
label (str|list): please label fallback into path if component cannot be guessed
path (str): classical UNIX path to package, last component can be '...'
target (str): target name, if empty will default to package name
subtarget (str): target named output
subrepo (str): subrepo name
pkg (str): alias path parameter
package (str): alias path parameter
out (str): alias subtarget parameter
name (str): alias target parameter
relative (bool): keep a relative path?
aslist (bool): return label in []
Return:
(str): build label
"""
if isinstance(label, list):
res = map(
lambda l: buildlabel(
label=l,
path=path,
target=target,
subtarget=subtarget,
subrepo=subrepo,
pkg=pkg,
package=package,
out=out,
name=name,
relative=relative,
aslist=False,
),
label,
)
return res[0] if (len(res) == 1) and not aslist else res

_res_alias = lambda als: reduce(lambda x, y: x if x else y, als)
_set_cnt = lambda l: reduce(lambda x, y: x + y, map(lambda x: 1 if x else 0, l), 0)
path = [path, pkg, package]
target = [target, name]
subtarget = [subtarget, out]
subrepo = [subrepo]

if _set_cnt(path) > 1:
return fail("can only set one of [path, pkg, package]")
if _set_cnt(target) > 1:
return fail("can only set one of [target, name]")
if _set_cnt(subtarget) > 1:
return fail("can only set one of [subtarget, out]")
if _set_cnt(subrepo) > 1:
return fail("can only set one of [subrepo]")

if label == "*": # special for vis
return ["PUBLIC"] if aslist else "PUBLIC"

# split label
if label.startswith("///"):
label = "@" + label[3:]
if "|" in label:
label, lbl_subtarget = label.split("|")
subtarget += [lbl_subtarget]
if ":" in label:
label, lbl_target = label.split(":")
target += [lbl_target]
if "//" in label:
label, lbl_path = label.split("//")
path += [f"/{lbl_path}"]
if "@" in label:
label, lbl_subrepo = label.split("@")
subrepo += [f"/{lbl_subrepo}"]
path += [label] # reminder as path

path = _res_alias(path)
target = _res_alias(target)
subtarget = _res_alias(subtarget)
subrepo = _res_alias(subrepo)

_prefix = lambda pre, s: pre + s.removeprefix(pre)
_prefix_if = lambda pre, s: _prefix(pre, s) if s else ""
_globalize_rel_path = lambda p, glf: (
p if p.startswith("/") else (glf() + _prefix("/", p))
)
_resolve_path_or_global = lambda p, glf: (
"/".join(reduce(_resolve_path, _globalize_rel_path(p, glf).split("/"), [""]))
if p
else glf()
)

def _resolve_path(acc, p):
if p == ".":
return acc
elif p == "..":
return (
acc[:-1] if len(acc) > 1 else acc
) # len > 1 to avoid a please interpreter bug
else:
return acc + ([p] if p else [])

path = path.replace("//", "/")
subrepo = subrepo.replace("///", "/")

if not relative:
path = _resolve_path_or_global(path, get_base_path)
subrepo = _resolve_path_or_global(subrepo, subrepo_name)

path = path.removeprefix("/")
subrepo = subrepo.removeprefix("/")

subrepo = _prefix("///", subrepo)
path = _prefix("//", path)
target = _prefix_if(":", target)
subtarget = _prefix_if("|", subtarget)

label = _prefix("///", subrepo + path + target + subtarget)
return [label] if aslist else label


# buildlabel aliases
bl = buildlabel
subdir = buildlabel
vis = buildlabel
107 changes: 107 additions & 0 deletions test/buildlabel/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
CONFIG.setdefault("TEST_ERRS", [])


def should(msg, a, b):
err = [f'should|{msg} ("{a}" !== "{b}")']
if (isinstance(a, str) and not isinstance(b, str)) or (isinstance(a, list) and not isinstance(b, list)) or (isinstance(a, str) and a != b) or (isinstance(a, list) and not all([x == y for x, y in zip(a, b)])):
# not equal
CONFIG["TEST_ERRS"] += err


def assert_tests():
if CONFIG["TEST_ERRS"]:
fail('\nvvvvvvv\n' + '\n'.join(CONFIG["TEST_ERRS"]) + '\n^^^^^^^\n\n')


bl_f = buildlabel
vis_f = buildlabel

pkg_path = get_base_path()
def_target = basename(pkg_path)
sr_name = subrepo_name()

## TESTS

should("return components in correct position and form", ["///c//a:b|d"], bl_f(path='a', target='b', subrepo='c', subtarget='d', relative=True, aslist=True))
should("return components in correct position and form", ["///c//a:b|d"], bl_f(path='//a', target=':b', subrepo='///c', subtarget='|d', relative=True, aslist=True))

should("return given target from root", "/////:all", bl_f(target='all', relative=True, aslist=False))
should("return given target global", f"///{sr_name}//{pkg_path}:all", bl_f(target='all', relative=False, aslist=False))
should("return current subrepo and package", f"///{sr_name}//{pkg_path}", bl_f(relative=False, aslist=False))
should("return root target", f"/////:all", bl_f(subrepo="///", path="//", target='all', relative=False, aslist=False))
should("return root target", f"/////:all", bl_f(subrepo="/", path="/", target='all', relative=False, aslist=False))

# path resolution

path_append = lambda b, p: f'{b}/{p}' if b else p
sr_c = path_append(sr_name, 'c/c')
pkg_a = path_append(pkg_path, 'a/a')

should("not resolve path when relative=True", f"///../b//../a", bl_f(subrepo='../b', path='../a', relative=True, aslist=False))
should("not resolve path starting with /", "///c//a", bl_f(path='/a', subrepo='/c', relative=False, aslist=False))
should("resolve path not starting with /", f"///{sr_c}//{pkg_a}", bl_f(path='a/a', subrepo='c/c', relative=False, aslist=False))

pkg_a = path_append(dirname(pkg_path), 'a/c')

should("get parent directory with ../ and remove ./", f"/////{pkg_a}", bl_f(path='../a/b/./../c', subrepo="/", relative=False, aslist=False))
should("get parent directory with ../ and remove ./", f"///a/c//", bl_f(subrepo='a/./b/../c', path="/", relative=False, aslist=False))
should("get parent directory with ../ and remove ./", f"/////a/c", bl_f(path='/a/b/../c/.', relative=False, subrepo="/", aslist=False))

should("return path from root with lots of ../", f"/////a/c", bl_f(path='/../../../../../../../a/b/../c', subrepo="/", relative=False, aslist=False))
should("return path from root with lots of ../", f"/////a/c", bl_f(path='../../../../../../../a/b/../c', subrepo="/", relative=False, aslist=False))
should("return path from root with lots of ../", f"///a/c//", bl_f(subrepo='/../../../../../../../a/b/../c', path="/", relative=False, aslist=False))
should("return path from root with lots of ../", f"///a/c//", bl_f(subrepo='../../../../../../../a/b/../c', path="/", relative=False, aslist=False))

pkg_c = path_append(pkg_path, 'a/c')

should("return path from root with ./", f"/////{pkg_path}", bl_f(path='.', subrepo="/", relative=False, aslist=False))
should("return path from root with ./", f"/////{pkg_c}", bl_f(path='./a/c', subrepo="/", relative=False, aslist=False))
should("return path from root with ./", f"///{sr_c}//", bl_f(subrepo='./c/c', path="/", relative=False, aslist=False))

# defaults

should("default aslist=True", ["///c//a:b|d"], bl_f(path='a', target='b', subrepo='c', subtarget='d', relative=True))
should("default relative=False", "///c//a:b|d", bl_f(path='/a', target='b', subrepo='/c', subtarget='d', aslist=False))
should("default arguments positions label,path,target,subtarget,subrepo", "///d//a:b|c", bl_f('', 'a', 'b', 'c', 'd', relative=True, aslist=False))
should("default label", '///a//c:d|e', bl_f('///a//c:d|e', relative=True, aslist=False))
should("default label", ['///a//c:d|e'], bl_f('///a//c:d|e', relative=True, aslist=True))

# aliases

should("alias path with pkg", "///c//a:b|d", bl_f(pkg='a', target='b', subrepo='c', subtarget='d', relative=True, aslist=False))
should("alias path with package", "///c//a:b|d", bl_f(package='a', target='b', subrepo='c', subtarget='d', relative=True, aslist=False))
should("alias subtarget with out", "///c//a:b|d", bl_f(path='a', target='b', subrepo='c', out='d', relative=True, aslist=False))
should("alias target with name", "///c//a:b|d", bl_f(path='a', name='b', subrepo='c', out='d', relative=True, aslist=False))

# should("fail when two alias are set", "///c//a:b|d", bl_f(path='a', pkg='n', target='b', subrepo='c', out='d', relative=True, aslist=False))
# should("fail when two alias are set", "///c//a:b|d", bl_f(path='a', target='b', subrepo='c', subtarget='e', out='d', relative=True, aslist=False))

# label

should("compose with first argument", "///a//b:c|d", bl_f('///a//b:c', out='d', relative=True, aslist=False))
should("compose with first argument", "///a//b|d", bl_f('///a', path='b', out='d', relative=True, aslist=False))
should("compose with first argument", f"///a//:x|d", bl_f('///a', target='x', out='d', relative=True, aslist=False))
should("compose with first argument", f"///a//{pkg_path}:x|d", bl_f('///a', target='x', out='d', relative=False, aslist=False))
should("compose with first argument", f"/////:x|d", bl_f(':r', target='x', out='d', relative=True, aslist=False))
should("compose with first argument", f"/////r:x|d", bl_f('//r', target='x', out='d', relative=True, aslist=False))

# list

should("accept list on first argument [1] aslist=False", "///a//b:c|d", bl_f(['///a//b:c'], out='d', relative=True, aslist=False))
should("accept list on first argument [1] aslist=True", ["///a//b:c|d"], bl_f(['///a//b:c'], out='d', relative=True, aslist=True))
should("accept list on first argument [2] aslist=False", ["///a//b:c|d", "///a//b:n|d"], bl_f(['///a//b:c', '///a//b:n'], out='d', relative=True, aslist=False))
should("accept list on first argument [2] aslist=True", ["///a//b:c|d", "///a//b:n|d"], bl_f(['///a//b:c', '///a//b:n'], out='d', relative=True, aslist=True))
should("accept list on first argument [0] aslist=False", [], bl_f([], out='d', relative=True, aslist=False))

# vis

should("return PUBLIC for *", ["PUBLIC"], vis_f('*', aslist=True))
should("return PUBLIC for *", "PUBLIC", vis_f('*', aslist=False))
should("keep ... for vis", "/////a/...", vis_f('a/...', relative=True, aslist=False))
should("keep ... for vis", f"/////{pkg_path}/a/...", vis_f('a/...', relative=False, aslist=False))
should("keep ... for vis", f"/////{pkg_path}/a/c/...", vis_f('a/b/../c/...', relative=False, aslist=False))
should("keep ... for vis", "/////a/c/...", vis_f('/a/b/../c/...', relative=False, aslist=False))

assert_tests()

# fail("TEST OK")
Loading