bazel: Make generator configurable, minor improvements

This change merges the internal version of `sapi.bzl` with the external version again:
- Add more docstrings to the various macros
- Skip creation of `.isystem` file, get info from toolchain instead

PiperOrigin-RevId: 437730588
Change-Id: I6f670d32e3d7177a6a160fd24cbee6f8f3ca9503
This commit is contained in:
Christian Blichmann 2022-03-28 05:28:42 -07:00 committed by Copybara-Service
parent f928f1dd7c
commit 466cc07254

View File

@ -12,9 +12,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
"""Macros that simplifies header and library generation for Sandboxed API.""" """Starlark rules for projects using Sandboxed API."""
load("//sandboxed_api/bazel:embed_data.bzl", "sapi_cc_embed_data") load("//sandboxed_api/bazel:embed_data.bzl", "sapi_cc_embed_data")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
# Helper functions # Helper functions
def append_arg(arguments, name, value): def append_arg(arguments, name, value):
@ -31,6 +32,16 @@ def get_embed_dir():
return native.package_name() return native.package_name()
def sort_deps(deps): def sort_deps(deps):
"""Sorts a list of dependencies.
This does not convert absolute references targeting the current package
into relative ones.
Args:
deps: List of labels to be sorted
Returns:
A sorted list of dependencies, with local deps (starting with ":") first.
"""
deps = depset(deps).to_list() deps = depset(deps).to_list()
colon_deps = [x for x in deps if x.startswith(":")] colon_deps = [x for x in deps if x.startswith(":")]
other_deps = [x for x in deps if not x.startswith(":")] other_deps = [x for x in deps if not x.startswith(":")]
@ -39,6 +50,9 @@ def sort_deps(deps):
def sapi_interface_impl(ctx): def sapi_interface_impl(ctx):
"""Implementation of build rule that generates SAPI interface.""" """Implementation of build rule that generates SAPI interface."""
cpp_toolchain = find_cpp_toolchain(ctx)
use_clang_generator = ctx.executable.generator.basename == "generator_tool"
# TODO(szwl): warn if input_files is not set and we didn't find anything # TODO(szwl): warn if input_files is not set and we didn't find anything
input_files_paths = [] input_files_paths = []
input_files = [] input_files = []
@ -50,10 +64,6 @@ def sapi_interface_impl(ctx):
append_arg(args, "--sapi_embed_name", ctx.attr.embed_name) append_arg(args, "--sapi_embed_name", ctx.attr.embed_name)
append_arg(args, "--sapi_functions", ",".join(ctx.attr.functions)) append_arg(args, "--sapi_functions", ",".join(ctx.attr.functions))
append_arg(args, "--sapi_ns", ctx.attr.namespace) append_arg(args, "--sapi_ns", ctx.attr.namespace)
if ctx.attr.isystem:
isystem = ctx.attr.isystem.files.to_list()[0]
append_arg(args, "--sapi_isystem", isystem.path)
input_files += [isystem]
if ctx.attr.limit_scan_depth: if ctx.attr.limit_scan_depth:
args.append("--sapi_limit_scan_depth") args.append("--sapi_limit_scan_depth")
@ -67,7 +77,7 @@ def sapi_interface_impl(ctx):
# package path. Including extra headers is harmless except that # package path. Including extra headers is harmless except that
# we may hit Bazel's file-count limit, so be conservative and # we may hit Bazel's file-count limit, so be conservative and
# pass a lot through that we don't strictly need. # pass a lot through that we don't strictly need.
#
extra_flags = [] extra_flags = []
cc_ctx = ctx.attr.lib[CcInfo].compilation_context cc_ctx = ctx.attr.lib[CcInfo].compilation_context
@ -75,20 +85,31 @@ def sapi_interface_impl(ctx):
input_files += cc_ctx.headers.to_list() input_files += cc_ctx.headers.to_list()
quote_includes = cc_ctx.quote_includes.to_list() quote_includes = cc_ctx.quote_includes.to_list()
append_all(extra_flags, "-D", cc_ctx.defines.to_list())
append_all(extra_flags, "-isystem", cc_ctx.system_includes.to_list()) if use_clang_generator:
append_all(extra_flags, "-iquote", quote_includes) input_files += cpp_toolchain.all_files.to_list()
# TODO(cblichmann): Get language standard from the toolchain
extra_flags.append("--extra-arg=-std=c++17")
# Disable warnings in parsed code
extra_flags.append("--extra-arg=-Wno-everything")
extra_flags += ["--extra-arg=-isystem{}".format(d) for d in cpp_toolchain.built_in_include_directories]
extra_flags += ["--extra-arg=-D{}".format(d) for d in cc_ctx.defines.to_list()]
extra_flags += ["--extra-arg=-isystem{}".format(i) for i in cc_ctx.system_includes.to_list()]
extra_flags += ["--extra-arg=-iquote{}".format(i) for i in quote_includes]
else:
append_all(extra_flags, "-D", cc_ctx.defines.to_list())
append_all(extra_flags, "-isystem", cc_ctx.system_includes.to_list())
append_all(extra_flags, "-iquote", quote_includes)
if ctx.attr.input_files: if ctx.attr.input_files:
for target in ctx.attr.input_files: for f in ctx.files.input_files:
if target.files: input_files.append(f)
for f in target.files.to_list(): input_files_paths.append(f.path)
input_files_paths.append(f.path)
input_files.append(f)
else: else:
# Try to find files automatically # Try to find files automatically
for h in cc_ctx.direct_headers: for h in cc_ctx.direct_headers:
# Collect all headers as dependency.
if h.extension != "h" or "/PROTECTED/" in h.path: if h.extension != "h" or "/PROTECTED/" in h.path:
continue continue
@ -97,8 +118,11 @@ def sapi_interface_impl(ctx):
if (h.owner.package == ctx.attr.lib.label.package): if (h.owner.package == ctx.attr.lib.label.package):
input_files_paths.append(h.path) input_files_paths.append(h.path)
append_arg(args, "--sapi_in", ",".join(input_files_paths)) if use_clang_generator:
args += ["--"] + extra_flags args += extra_flags + input_files_paths
else:
append_arg(args, "--sapi_in", ",".join(input_files_paths))
args += ["--"] + extra_flags
progress_msg = ("Generating {} from {} header files." + progress_msg = ("Generating {} from {} header files." +
"").format(ctx.outputs.out.short_path, len(input_files_paths)) "").format(ctx.outputs.out.short_path, len(input_files_paths))
@ -107,7 +131,7 @@ def sapi_interface_impl(ctx):
outputs = [ctx.outputs.out], outputs = [ctx.outputs.out],
arguments = args, arguments = args,
progress_message = progress_msg, progress_message = progress_msg,
executable = ctx.executable._sapi_generator, executable = ctx.executable.generator,
) )
# Build rule that generates SAPI interface. # Build rule that generates SAPI interface.
@ -118,22 +142,23 @@ sapi_interface = rule(
"embed_dir": attr.string(), "embed_dir": attr.string(),
"embed_name": attr.string(), "embed_name": attr.string(),
"functions": attr.string_list(allow_empty = True, default = []), "functions": attr.string_list(allow_empty = True, default = []),
"include_prefix": attr.string(),
"input_files": attr.label_list(allow_files = True), "input_files": attr.label_list(allow_files = True),
"lib": attr.label(providers = [CcInfo], mandatory = True), "lib": attr.label(providers = [CcInfo], mandatory = True),
"lib_name": attr.string(mandatory = True), "lib_name": attr.string(mandatory = True),
"namespace": attr.string(), "namespace": attr.string(),
"isystem": attr.label(),
"limit_scan_depth": attr.bool(default = False), "limit_scan_depth": attr.bool(default = False),
"_sapi_generator": attr.label( "generator": attr.label(
executable = True, executable = True,
cfg = "host", cfg = "host",
allow_files = True, allow_files = True,
default = Label("@com_google_sandboxed_api//sandboxed_api/" + default = Label("//sandboxed_api/tools/generator2:sapi_generator"),
"tools/generator2:sapi_generator"), ),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
), ),
}, },
output_to_genfiles = True, output_to_genfiles = True,
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
) )
def sapi_library( def sapi_library(
@ -143,6 +168,7 @@ def sapi_library(
namespace = "", namespace = "",
embed = True, embed = True,
add_default_deps = True, add_default_deps = True,
limit_scan_depth = False,
srcs = [], srcs = [],
hdrs = [], hdrs = [],
functions = [], functions = [],
@ -150,10 +176,35 @@ def sapi_library(
input_files = [], input_files = [],
deps = [], deps = [],
tags = [], tags = [],
generator_executable = "//sandboxed_api/tools/generator2:sapi_generator",
visibility = None): visibility = None):
"""Provides the implementation of a Sandboxed API library.""" """Provides the implementation of a Sandboxed API library.
Args:
name: Name of the sandboxed library
lib: Label of the library target to sandbox
lib_name: Name of the class which will proxy the library functions from
the functions list
malloc: Override the default dependency on malloc
namespace: A C++ namespace identifier to place the API class into
embed: Whether the SAPI library should be embedded inside the host code
add_default_deps: Add SAPI dependencies to target (deprecated)
limit_scan_depth: Limit include depth for header generator (deprecated)
srcs: Any additional sources to include with the sandboxed library
hdrs: Like srcs, any additional headers to include with the sandboxed
library
functions: A list for function to use from host code
header: If set, do not generate a header, but use the specified one
(deprecated).
input_files: List of source files which the SAPI interface generator
should scan for function declaration
deps: Extra dependencies to add to the SAPI library
tags: Extra tags to associate with the target
generator_executable: Label of the SAPI interface generator to use
(experimental).
visibility: Target visibility
"""
rprefix = "@com_google_sandboxed_api"
common = { common = {
"tags": tags, "tags": tags,
} }
@ -164,7 +215,7 @@ def sapi_library(
# Reference (pull into the archive) required functions only. If the functions' # Reference (pull into the archive) required functions only. If the functions'
# array is empty, pull in the whole archive (may not compile with MSAN). # array is empty, pull in the whole archive (may not compile with MSAN).
exported_funcs = ["-Wl,--export-dynamic-symbol," + s for s in functions] exported_funcs = ["-Wl,-u," + s for s in functions]
if (not exported_funcs): if (not exported_funcs):
exported_funcs = [ exported_funcs = [
"-Wl,--whole-archive", "-Wl,--whole-archive",
@ -177,7 +228,7 @@ def sapi_library(
else: else:
lib_hdrs += [generated_header] lib_hdrs += [generated_header]
default_deps = [rprefix + "//sandboxed_api/sandbox2"] default_deps = ["//sandboxed_api/sandbox2"]
# Library that contains generated interface and sandboxed binary as a data # Library that contains generated interface and sandboxed binary as a data
# dependency. Add this as a dependency instead of original library. # dependency. Add this as a dependency instead of original library.
@ -188,11 +239,12 @@ def sapi_library(
data = [":" + name + ".bin"], data = [":" + name + ".bin"],
deps = sort_deps( deps = sort_deps(
[ [
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/status", "@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor", "@com_google_absl//absl/status:statusor",
rprefix + "//sandboxed_api:sapi", "//sandboxed_api:sapi",
rprefix + "//sandboxed_api/util:status", "//sandboxed_api/util:status",
rprefix + "//sandboxed_api:vars", "//sandboxed_api:vars",
] + deps + ] + deps +
([":" + name + "_embed"] if embed else []) + ([":" + name + "_embed"] if embed else []) +
(default_deps if add_default_deps else []), (default_deps if add_default_deps else []),
@ -204,14 +256,12 @@ def sapi_library(
name = name + ".bin", name = name + ".bin",
linkopts = [ linkopts = [
"-ldl", # For dlopen(), dlsym() "-ldl", # For dlopen(), dlsym()
# The sandboxing client must have access to all symbols used in # The sandboxing client must have access to all
# the sandboxed library, so these must be both referenced, and "-Wl,-E", # symbols used in the sandboxed library, so these
# exported ] + exported_funcs, # must be both referenced, and exported
"-Wl,-E",
] + exported_funcs,
deps = [ deps = [
":" + name + ".lib", ":" + name + ".lib",
rprefix + "//sandboxed_api:client", "//sandboxed_api:client",
], ],
**common **common
) )
@ -246,15 +296,7 @@ def sapi_library(
embed_name = embed_name, embed_name = embed_name,
embed_dir = embed_dir, embed_dir = embed_dir,
namespace = namespace, namespace = namespace,
isystem = ":" + name + ".isystem", generator = generator_executable,
limit_scan_depth = limit_scan_depth,
**common **common
) )
native.genrule(
name = name + ".isystem",
outs = [name + ".isystem.list"],
cmd = """$(CC) -E -x c++ -v /dev/null 2>&1 |
awk '/> search starts here:/{f=1;next}/^End of search/{f=0}f{print $$1}' > $@
""",
toolchains = ["@bazel_tools//tools/cpp:current_cc_toolchain"],
)