-
Notifications
You must be signed in to change notification settings - Fork 35
/
sh_binary.bzl
130 lines (117 loc) · 5.65 KB
/
sh_binary.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
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under both the MIT license found in the
# LICENSE-MIT file in the root directory of this source tree and the Apache
# License, Version 2.0 found in the LICENSE-APACHE file in the root directory
# of this source tree.
load("@prelude//:paths.bzl", "paths")
load("@prelude//os_lookup:defs.bzl", "OsLookup")
def _derive_link(artifact):
if artifact.is_source:
return artifact.short_path
# TODO(cjhopman): Reject cross-repo resources. Buck1 does that. It's probably
# easier for us (compared to v1) to construct a scheme for them that is
# correct, but not necessary yet.
return paths.join(artifact.owner.package, artifact.owner.name)
def _generate_script(
name: str,
main: Artifact,
resources: list[Artifact],
append_script_extension: bool,
actions: AnalysisActions,
is_windows: bool) -> (Artifact, Artifact):
main_path = main.short_path
if not append_script_extension:
main_link = main_path
elif is_windows:
main_link = main_path if main_path.endswith(".bat") or main_path.endswith(".cmd") else main_path + ".bat"
else:
main_link = main_path if main_path.endswith(".sh") else main_path + ".sh"
resources = {_derive_link(src): src for src in resources}
resources[main_link] = main
resources_dir = actions.symlinked_dir("resources", resources)
script_name = name + (".bat" if is_windows else "")
script = actions.declare_output(script_name)
# This is much, much simpler than the buck1 sh_binary template. A couple reasons:
# 1. we don't invoke the script through a symlink and so don't need to use and implement a cross-platform `readlink -e`
# 2. we don't construct an invocation-specific sandbox. The implementation of
# that in buck1 is pretty crazy and it shouldn't actually be necessary.
# 3. we don't construct the cell symlinks. those were also strange. They were
# used for the links in the invocation-specific sandbox (so things would
# point through the cell symlinks to their original locations). Instead we
# construct links directly to things (which buck1 actually also did for its
# BUCK_DEFAULT_RUNTIME_RESOURCES).
if not is_windows:
script_content = cmd_args(
"#!/usr/bin/env bash",
"set -e",
# If we access this sh_binary via a unhashed symlink we need to
# update the relative source.
'__SRC="${BASH_SOURCE[0]}"',
'__SRC="$(realpath "$__SRC")"',
'__SCRIPT_DIR=$(dirname "$__SRC")',
# The format of the directory tree is different in v1 and v2. We
# should unify the two, but prior to doing this we should also
# identify what the right format is. For now, this variable lets
# callees disambiguate (see D28960177 for more context).
"export BUCK_SH_BINARY_VERSION_UNSTABLE=2",
cmd_args("export BUCK_PROJECT_ROOT=\"$__SCRIPT_DIR/", resources_dir, "\"", delimiter = ""),
# In buck1, the paths for resources that are outputs of rules have
# different paths in BUCK_PROJECT_ROOT and
# BUCK_DEFAULT_RUNTIME_RESOURCES, but we use the same paths. buck1's
# BUCK_PROJECT_ROOT paths would use the actual buck-out path rather
# than something derived from the target and so to use that people
# would need to hardcode buck-out paths into their scripts. For repo
# sources, the paths are the same for both.
"export BUCK_DEFAULT_RUNTIME_RESOURCES=\"$BUCK_PROJECT_ROOT\"",
"exec \"$BUCK_PROJECT_ROOT/{}\" \"$@\"".format(main_link),
relative_to = (script, 1),
)
else:
script_content = cmd_args(
"@echo off",
"setlocal EnableDelayedExpansion",
# Fully qualified script path.
"set __SRC=%~f0",
# This is essentially a realpath.
'for /f "tokens=2 delims=[]" %%a in (\'dir %__SRC% ^|%SYSTEMROOT%\\System32\\find.exe "<SYMLINK>"\') do set "__SRC=%%a"',
# Get parent folder.
'for %%a in ("%__SRC%") do set "__SCRIPT_DIR=%%~dpa"',
"set BUCK_SH_BINARY_VERSION_UNSTABLE=2",
cmd_args("set BUCK_PROJECT_ROOT=%__SCRIPT_DIR%\\", resources_dir, delimiter = ""),
"set BUCK_DEFAULT_RUNTIME_RESOURCES=%BUCK_PROJECT_ROOT%",
"%BUCK_PROJECT_ROOT%\\{} %*".format(main_link),
relative_to = (script, 1),
)
actions.write(
script,
script_content,
is_executable = True,
)
return (script, resources_dir)
# Attrs:
# "deps": attrs.list(attrs.dep(), default = []),
# "main": attrs.source(),
# "resources": attrs.list(attrs.source(), default = []),
def sh_binary_impl(ctx):
# TODO: implement deps (not sure what those even do, though)
if len(ctx.attrs.deps) > 0:
fail("sh_binary deps unsupported. Got `{}`".format(repr(ctx.attrs)))
is_windows = ctx.attrs._target_os_type[OsLookup].platform == "windows"
(script, resources_dir) = _generate_script(
ctx.label.name,
ctx.attrs.main,
ctx.attrs.resources,
ctx.attrs.append_script_extension,
ctx.actions,
is_windows,
)
script = script.with_associated_artifacts([resources_dir])
return [
DefaultInfo(default_output = script, other_outputs = [resources_dir]),
RunInfo(
# TODO(cjhopman): Figure out if we need to specify the link targets
# as inputs. We shouldn't need to, but need to verify it.
args = cmd_args(script, hidden = resources_dir),
),
]