1*ec63e07aSXin Li# Copyright 2019 Google LLC 2*ec63e07aSXin Li# 3*ec63e07aSXin Li# Licensed under the Apache License, Version 2.0 (the "License"); 4*ec63e07aSXin Li# you may not use this file except in compliance with the License. 5*ec63e07aSXin Li# You may obtain a copy of the License at 6*ec63e07aSXin Li# 7*ec63e07aSXin Li# https://www.apache.org/licenses/LICENSE-2.0 8*ec63e07aSXin Li# 9*ec63e07aSXin Li# Unless required by applicable law or agreed to in writing, software 10*ec63e07aSXin Li# distributed under the License is distributed on an "AS IS" BASIS, 11*ec63e07aSXin Li# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*ec63e07aSXin Li# See the License for the specific language governing permissions and 13*ec63e07aSXin Li# limitations under the License. 14*ec63e07aSXin Li 15*ec63e07aSXin Li"""Starlark rules for projects using Sandboxed API.""" 16*ec63e07aSXin Li 17*ec63e07aSXin Liload("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts") 18*ec63e07aSXin Liload("//sandboxed_api/bazel:embed_data.bzl", "sapi_cc_embed_data") 19*ec63e07aSXin Liload( 20*ec63e07aSXin Li "//sandboxed_api/bazel:proto.bzl", 21*ec63e07aSXin Li _sapi_proto_library = "sapi_proto_library", 22*ec63e07aSXin Li) 23*ec63e07aSXin Liload("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain") 24*ec63e07aSXin Li 25*ec63e07aSXin Li# Reexport symbols 26*ec63e07aSXin Lisapi_proto_library = _sapi_proto_library 27*ec63e07aSXin Li 28*ec63e07aSXin Li# Helper functions 29*ec63e07aSXin Lidef append_arg(arguments, name, value): 30*ec63e07aSXin Li if value: 31*ec63e07aSXin Li arguments.append("{}".format(name)) 32*ec63e07aSXin Li arguments.append(value) 33*ec63e07aSXin Li 34*ec63e07aSXin Lidef append_all(arguments, name, values): 35*ec63e07aSXin Li if values: 36*ec63e07aSXin Li for v in values: 37*ec63e07aSXin Li append_arg(arguments, name, v) 38*ec63e07aSXin Li 39*ec63e07aSXin Lidef get_embed_dir(): 40*ec63e07aSXin Li return native.package_name() 41*ec63e07aSXin Li 42*ec63e07aSXin Lidef make_exec_label(label): 43*ec63e07aSXin Li return attr.label( 44*ec63e07aSXin Li default = label, 45*ec63e07aSXin Li cfg = "exec", 46*ec63e07aSXin Li allow_files = True, 47*ec63e07aSXin Li executable = True, 48*ec63e07aSXin Li ) 49*ec63e07aSXin Li 50*ec63e07aSXin Li# buildifier: disable=function-docstring 51*ec63e07aSXin Lidef select_generator(ctx): 52*ec63e07aSXin Li if ctx.attr.generator_version == 1: 53*ec63e07aSXin Li return ctx.executable._generator_v1 54*ec63e07aSXin Li return ctx.executable._generator_v2 55*ec63e07aSXin Li 56*ec63e07aSXin Lidef sort_deps(deps): 57*ec63e07aSXin Li """Sorts a list of dependencies. 58*ec63e07aSXin Li 59*ec63e07aSXin Li This does not convert absolute references targeting the current package 60*ec63e07aSXin Li into relative ones. 61*ec63e07aSXin Li 62*ec63e07aSXin Li Args: 63*ec63e07aSXin Li deps: List of labels to be sorted 64*ec63e07aSXin Li Returns: 65*ec63e07aSXin Li A sorted list of dependencies, with local deps (starting with ":") first. 66*ec63e07aSXin Li """ 67*ec63e07aSXin Li 68*ec63e07aSXin Li deps = depset(deps).to_list() 69*ec63e07aSXin Li colon_deps = [x for x in deps if x.startswith(":")] 70*ec63e07aSXin Li other_deps = [x for x in deps if not x.startswith(":")] 71*ec63e07aSXin Li return sorted(colon_deps) + sorted(other_deps) 72*ec63e07aSXin Li 73*ec63e07aSXin Lidef cc_library_virtual_includes(target): 74*ec63e07aSXin Li """Checks a target for virtual includes. 75*ec63e07aSXin Li 76*ec63e07aSXin Li Those can be created by the deprecated `cc_inc_library` rule, or by using 77*ec63e07aSXin Li a combination of `cc_library()`s `includes`, `include_prefix` and 78*ec63e07aSXin Li `strip_include_prefix` attributes. 79*ec63e07aSXin Li 80*ec63e07aSXin Li Args: 81*ec63e07aSXin Li target: The Target to analyze 82*ec63e07aSXin Li Returns: 83*ec63e07aSXin Li A depset with include paths generated by cc_inc_library targets. 84*ec63e07aSXin Li """ 85*ec63e07aSXin Li cc_ctx = target[CcInfo].compilation_context 86*ec63e07aSXin Li 87*ec63e07aSXin Li includes = [] 88*ec63e07aSXin Li for f in cc_ctx.headers.to_list(): 89*ec63e07aSXin Li p = f.path 90*ec63e07aSXin Li if not p.startswith("blaze-out") and not p.startswith("bazel-out"): 91*ec63e07aSXin Li continue 92*ec63e07aSXin Li for path_marker in ["/_virtual_includes/", "/_/"]: 93*ec63e07aSXin Li i = p.find(path_marker) 94*ec63e07aSXin Li if i == -1: 95*ec63e07aSXin Li continue 96*ec63e07aSXin Li includes.append(p[:i] + path_marker + 97*ec63e07aSXin Li p[i + len(path_marker):].split("/", 1)[0]) 98*ec63e07aSXin Li 99*ec63e07aSXin Li return depset(includes) 100*ec63e07aSXin Li 101*ec63e07aSXin Lidef _sapi_interface_impl(ctx): 102*ec63e07aSXin Li cpp_toolchain = find_cpp_toolchain(ctx) 103*ec63e07aSXin Li generator = select_generator(ctx) 104*ec63e07aSXin Li use_clang_generator = ctx.attr.generator_version == 2 105*ec63e07aSXin Li 106*ec63e07aSXin Li # TODO(szwl): warn if input_files is not set and we didn't find anything 107*ec63e07aSXin Li input_files_paths = [] 108*ec63e07aSXin Li input_files = [] 109*ec63e07aSXin Li 110*ec63e07aSXin Li args = [] 111*ec63e07aSXin Li append_arg(args, "--sapi_name", ctx.attr.lib_name) 112*ec63e07aSXin Li append_arg(args, "--sapi_out", ctx.outputs.out.path) 113*ec63e07aSXin Li append_arg(args, "--sapi_embed_dir", ctx.attr.embed_dir) 114*ec63e07aSXin Li append_arg(args, "--sapi_embed_name", ctx.attr.embed_name) 115*ec63e07aSXin Li append_arg(args, "--sapi_functions", ",".join(ctx.attr.functions)) 116*ec63e07aSXin Li append_arg(args, "--sapi_ns", ctx.attr.namespace) 117*ec63e07aSXin Li 118*ec63e07aSXin Li if ctx.attr.limit_scan_depth: 119*ec63e07aSXin Li args.append("--sapi_limit_scan_depth") 120*ec63e07aSXin Li 121*ec63e07aSXin Li # Parse provided files. 122*ec63e07aSXin Li 123*ec63e07aSXin Li # The parser doesn't need the entire set of transitive headers 124*ec63e07aSXin Li # here, just the top-level cc_library headers. 125*ec63e07aSXin Li # 126*ec63e07aSXin Li # Allow all headers through that contain the dependency's 127*ec63e07aSXin Li # package path. Including extra headers is harmless except that 128*ec63e07aSXin Li # we may hit Bazel's file-count limit, so be conservative and 129*ec63e07aSXin Li # pass a lot through that we don't strictly need. 130*ec63e07aSXin Li # 131*ec63e07aSXin Li extra_flags = [] 132*ec63e07aSXin Li cc_ctx = ctx.attr.lib[CcInfo].compilation_context 133*ec63e07aSXin Li 134*ec63e07aSXin Li # Append all headers as dependencies 135*ec63e07aSXin Li input_files += cc_ctx.headers.to_list() 136*ec63e07aSXin Li 137*ec63e07aSXin Li # Gather direct include paths as well as virtual ones 138*ec63e07aSXin Li quote_includes = (cc_ctx.quote_includes.to_list() + 139*ec63e07aSXin Li cc_library_virtual_includes(ctx.attr.lib).to_list()) 140*ec63e07aSXin Li 141*ec63e07aSXin Li if use_clang_generator: 142*ec63e07aSXin Li input_files += cpp_toolchain.all_files.to_list() 143*ec63e07aSXin Li 144*ec63e07aSXin Li # TODO(cblichmann): Get language standard from the toolchain 145*ec63e07aSXin Li extra_flags.append("--extra-arg=-std=c++17") 146*ec63e07aSXin Li 147*ec63e07aSXin Li # Disable warnings in parsed code 148*ec63e07aSXin Li extra_flags.append("--extra-arg=-Wno-everything") 149*ec63e07aSXin Li extra_flags += ["--extra-arg=-D{}".format(d) for d in cc_ctx.defines.to_list()] 150*ec63e07aSXin Li extra_flags += ["--extra-arg=-isystem{}".format(i) for i in cc_ctx.system_includes.to_list()] 151*ec63e07aSXin Li extra_flags += ["--extra-arg=-iquote{}".format(i) for i in quote_includes] 152*ec63e07aSXin Li extra_flags += ["--extra-arg=-isystem{}".format(d) for d in cpp_toolchain.built_in_include_directories] 153*ec63e07aSXin Li else: 154*ec63e07aSXin Li append_all(extra_flags, "-D", cc_ctx.defines.to_list()) 155*ec63e07aSXin Li append_all(extra_flags, "-isystem", cc_ctx.system_includes.to_list()) 156*ec63e07aSXin Li append_all(extra_flags, "-iquote", quote_includes) 157*ec63e07aSXin Li 158*ec63e07aSXin Li if ctx.attr.input_files: 159*ec63e07aSXin Li for f in ctx.files.input_files: 160*ec63e07aSXin Li input_files.append(f) 161*ec63e07aSXin Li input_files_paths.append(f.path) 162*ec63e07aSXin Li else: 163*ec63e07aSXin Li # Try to find files automatically 164*ec63e07aSXin Li for h in cc_ctx.direct_headers: 165*ec63e07aSXin Li if h.extension != "h" or "/PROTECTED/" in h.path: 166*ec63e07aSXin Li continue 167*ec63e07aSXin Li 168*ec63e07aSXin Li # Include only headers coming from the target 169*ec63e07aSXin Li # not ones that it depends on by comparing the label packages. 170*ec63e07aSXin Li if (h.owner.package == ctx.attr.lib.label.package): 171*ec63e07aSXin Li input_files_paths.append(h.path) 172*ec63e07aSXin Li 173*ec63e07aSXin Li if use_clang_generator: 174*ec63e07aSXin Li args += extra_flags + input_files_paths 175*ec63e07aSXin Li else: 176*ec63e07aSXin Li append_arg(args, "--sapi_in", ",".join(input_files_paths)) 177*ec63e07aSXin Li args += ["--"] + extra_flags 178*ec63e07aSXin Li 179*ec63e07aSXin Li progress_msg = ("Generating {} from {} header files." + 180*ec63e07aSXin Li "").format(ctx.outputs.out.short_path, len(input_files_paths)) 181*ec63e07aSXin Li ctx.actions.run( 182*ec63e07aSXin Li inputs = input_files, 183*ec63e07aSXin Li outputs = [ctx.outputs.out], 184*ec63e07aSXin Li arguments = args, 185*ec63e07aSXin Li mnemonic = "SapiInterfaceGen", 186*ec63e07aSXin Li progress_message = progress_msg, 187*ec63e07aSXin Li executable = generator, 188*ec63e07aSXin Li ) 189*ec63e07aSXin Li 190*ec63e07aSXin Li# Build rule that generates SAPI interface. 191*ec63e07aSXin Lisapi_interface = rule( 192*ec63e07aSXin Li implementation = _sapi_interface_impl, 193*ec63e07aSXin Li attrs = { 194*ec63e07aSXin Li "out": attr.output(mandatory = True), 195*ec63e07aSXin Li "embed_dir": attr.string(), 196*ec63e07aSXin Li "embed_name": attr.string(), 197*ec63e07aSXin Li "functions": attr.string_list( 198*ec63e07aSXin Li allow_empty = True, 199*ec63e07aSXin Li default = [], 200*ec63e07aSXin Li ), 201*ec63e07aSXin Li "input_files": attr.label_list( 202*ec63e07aSXin Li providers = [CcInfo], 203*ec63e07aSXin Li allow_files = True, 204*ec63e07aSXin Li ), 205*ec63e07aSXin Li "lib": attr.label( 206*ec63e07aSXin Li providers = [CcInfo], 207*ec63e07aSXin Li mandatory = True, 208*ec63e07aSXin Li ), 209*ec63e07aSXin Li "lib_name": attr.string(mandatory = True), 210*ec63e07aSXin Li "namespace": attr.string(), 211*ec63e07aSXin Li "limit_scan_depth": attr.bool(default = False), 212*ec63e07aSXin Li "api_version": attr.int( 213*ec63e07aSXin Li default = 1, 214*ec63e07aSXin Li values = [1], # Only a single version is defined right now 215*ec63e07aSXin Li ), 216*ec63e07aSXin Li "generator_version": attr.int( 217*ec63e07aSXin Li default = 1, 218*ec63e07aSXin Li values = [1, 2], 219*ec63e07aSXin Li ), 220*ec63e07aSXin Li "_generator_v1": make_exec_label( 221*ec63e07aSXin Li "//sandboxed_api/tools/generator2:sapi_generator", 222*ec63e07aSXin Li ), 223*ec63e07aSXin Li "_generator_v2": make_exec_label( 224*ec63e07aSXin Li # TODO(cblichmann): Add prebuilt version of Clang based generator 225*ec63e07aSXin Li "//sandboxed_api/tools/clang_generator:generator_tool", 226*ec63e07aSXin Li ), 227*ec63e07aSXin Li "_cc_toolchain": attr.label( 228*ec63e07aSXin Li default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), 229*ec63e07aSXin Li ), 230*ec63e07aSXin Li }, 231*ec63e07aSXin Li output_to_genfiles = True, 232*ec63e07aSXin Li toolchains = use_cpp_toolchain(), 233*ec63e07aSXin Li) 234*ec63e07aSXin Li 235*ec63e07aSXin Lidef sapi_library( 236*ec63e07aSXin Li name, 237*ec63e07aSXin Li lib, 238*ec63e07aSXin Li lib_name, 239*ec63e07aSXin Li malloc = "@bazel_tools//tools/cpp:malloc", 240*ec63e07aSXin Li namespace = "", 241*ec63e07aSXin Li api_version = 1, 242*ec63e07aSXin Li embed = True, 243*ec63e07aSXin Li add_default_deps = True, 244*ec63e07aSXin Li limit_scan_depth = False, 245*ec63e07aSXin Li srcs = [], 246*ec63e07aSXin Li data = [], 247*ec63e07aSXin Li hdrs = [], 248*ec63e07aSXin Li copts = sapi_platform_copts(), 249*ec63e07aSXin Li defines = [], 250*ec63e07aSXin Li functions = [], 251*ec63e07aSXin Li header = "", 252*ec63e07aSXin Li input_files = [], 253*ec63e07aSXin Li deps = [], 254*ec63e07aSXin Li tags = [], 255*ec63e07aSXin Li generator_version = 1, 256*ec63e07aSXin Li visibility = None, 257*ec63e07aSXin Li compatible_with = None, 258*ec63e07aSXin Li default_copts = []): 259*ec63e07aSXin Li """Provides the implementation of a Sandboxed API library. 260*ec63e07aSXin Li 261*ec63e07aSXin Li Args: 262*ec63e07aSXin Li name: Name of the sandboxed library 263*ec63e07aSXin Li lib: Label of the library target to sandbox 264*ec63e07aSXin Li lib_name: Name of the class which will proxy the library functions from 265*ec63e07aSXin Li the functions list 266*ec63e07aSXin Li malloc: Override the default dependency on malloc 267*ec63e07aSXin Li namespace: A C++ namespace identifier to place the API class into 268*ec63e07aSXin Li embed: Whether the SAPI library should be embedded inside the host code 269*ec63e07aSXin Li add_default_deps: Add SAPI dependencies to target (deprecated) 270*ec63e07aSXin Li limit_scan_depth: Limit include depth for header generator (deprecated) 271*ec63e07aSXin Li api_version: Which version of the Sandboxed API to generate. Currently, 272*ec63e07aSXin Li only version 1 is defined. 273*ec63e07aSXin Li srcs: Any additional sources to include with the sandboxed library 274*ec63e07aSXin Li data: To be used with srcs, any additional data files to make available 275*ec63e07aSXin Li to the sandboxed library. 276*ec63e07aSXin Li hdrs: Like srcs, any additional headers to include with the sandboxed 277*ec63e07aSXin Li library 278*ec63e07aSXin Li copts: Add these options to the C++ compilation command. See 279*ec63e07aSXin Li cc_library.copts. 280*ec63e07aSXin Li defines: List of defines to add to the compile line. See 281*ec63e07aSXin Li cc_library.defines. 282*ec63e07aSXin Li functions: A list for function to use from host code 283*ec63e07aSXin Li header: If set, do not generate a header, but use the specified one 284*ec63e07aSXin Li (deprecated). 285*ec63e07aSXin Li input_files: List of source files which the SAPI interface generator 286*ec63e07aSXin Li should scan for function declarations 287*ec63e07aSXin Li deps: Extra dependencies to add to the SAPI library 288*ec63e07aSXin Li tags: Extra tags to associate with the target 289*ec63e07aSXin Li generator_version: Which version the the interface generator to use 290*ec63e07aSXin Li (experimental). Version 1 uses the Python/libclang based `generator2`, 291*ec63e07aSXin Li version 2 uses the newer C++ implementation that uses the full clang 292*ec63e07aSXin Li compiler front-end for parsing. Both emit equivalent Sandboxed APIs. 293*ec63e07aSXin Li visibility: Target visibility 294*ec63e07aSXin Li compatible_with: The list of environments this target can be built for, 295*ec63e07aSXin Li in addition to default-supported environments. 296*ec63e07aSXin Li default_copts: List of package level default copts, an additional 297*ec63e07aSXin Li attribute since copts already has default value. 298*ec63e07aSXin Li """ 299*ec63e07aSXin Li 300*ec63e07aSXin Li common = { 301*ec63e07aSXin Li "tags": tags, 302*ec63e07aSXin Li } 303*ec63e07aSXin Li if visibility: 304*ec63e07aSXin Li common["visibility"] = visibility 305*ec63e07aSXin Li 306*ec63e07aSXin Li if compatible_with != None: 307*ec63e07aSXin Li common["compatible_with"] = compatible_with 308*ec63e07aSXin Li 309*ec63e07aSXin Li generated_header = name + ".sapi.h" 310*ec63e07aSXin Li 311*ec63e07aSXin Li # Reference (pull into the archive) required functions only. If the functions' 312*ec63e07aSXin Li # array is empty, pull in the whole archive (may not compile with MSAN). 313*ec63e07aSXin Li exported_funcs = ["-Wl,-u," + s for s in functions] 314*ec63e07aSXin Li if (not exported_funcs): 315*ec63e07aSXin Li exported_funcs = [ 316*ec63e07aSXin Li "-Wl,--whole-archive", 317*ec63e07aSXin Li "-Wl,--allow-multiple-definition", 318*ec63e07aSXin Li ] 319*ec63e07aSXin Li 320*ec63e07aSXin Li lib_hdrs = hdrs or [] 321*ec63e07aSXin Li if header: 322*ec63e07aSXin Li lib_hdrs += [header] 323*ec63e07aSXin Li else: 324*ec63e07aSXin Li lib_hdrs += [generated_header] 325*ec63e07aSXin Li 326*ec63e07aSXin Li default_deps = ["//sandboxed_api/sandbox2"] 327*ec63e07aSXin Li 328*ec63e07aSXin Li # Library that contains generated interface and sandboxed binary as a data 329*ec63e07aSXin Li # dependency. Add this as a dependency instead of original library. 330*ec63e07aSXin Li native.cc_library( 331*ec63e07aSXin Li name = name, 332*ec63e07aSXin Li srcs = srcs, 333*ec63e07aSXin Li data = [":" + name + ".bin"] + data, 334*ec63e07aSXin Li hdrs = lib_hdrs, 335*ec63e07aSXin Li copts = default_copts + copts, 336*ec63e07aSXin Li defines = defines, 337*ec63e07aSXin Li deps = sort_deps( 338*ec63e07aSXin Li [ 339*ec63e07aSXin Li "@com_google_absl//absl/base:core_headers", 340*ec63e07aSXin Li "@com_google_absl//absl/status", 341*ec63e07aSXin Li "@com_google_absl//absl/status:statusor", 342*ec63e07aSXin Li "//sandboxed_api:sapi", 343*ec63e07aSXin Li "//sandboxed_api/util:status", 344*ec63e07aSXin Li "//sandboxed_api:vars", 345*ec63e07aSXin Li ] + deps + 346*ec63e07aSXin Li ([":" + name + "_embed"] if embed else []) + 347*ec63e07aSXin Li (default_deps if add_default_deps else []), 348*ec63e07aSXin Li ), 349*ec63e07aSXin Li **common 350*ec63e07aSXin Li ) 351*ec63e07aSXin Li 352*ec63e07aSXin Li native.cc_binary( 353*ec63e07aSXin Li name = name + ".bin", 354*ec63e07aSXin Li linkopts = [ 355*ec63e07aSXin Li "-ldl", # For dlopen(), dlsym() 356*ec63e07aSXin Li # The sandboxing client must have access to all 357*ec63e07aSXin Li "-Wl,-E", # symbols used in the sandboxed library, so these 358*ec63e07aSXin Li ] + exported_funcs, # must be both referenced, and exported 359*ec63e07aSXin Li malloc = malloc, 360*ec63e07aSXin Li deps = [ 361*ec63e07aSXin Li ":" + name + ".lib", 362*ec63e07aSXin Li "//sandboxed_api:client", 363*ec63e07aSXin Li ], 364*ec63e07aSXin Li copts = default_copts, 365*ec63e07aSXin Li **common 366*ec63e07aSXin Li ) 367*ec63e07aSXin Li 368*ec63e07aSXin Li native.cc_library( 369*ec63e07aSXin Li name = name + ".lib", 370*ec63e07aSXin Li deps = [lib], 371*ec63e07aSXin Li alwayslink = 1, # All functions are linked into depending binaries 372*ec63e07aSXin Li copts = default_copts, 373*ec63e07aSXin Li **common 374*ec63e07aSXin Li ) 375*ec63e07aSXin Li 376*ec63e07aSXin Li embed_name = "" 377*ec63e07aSXin Li embed_dir = "" 378*ec63e07aSXin Li if embed: 379*ec63e07aSXin Li embed_name = name 380*ec63e07aSXin Li 381*ec63e07aSXin Li sapi_cc_embed_data( 382*ec63e07aSXin Li name = name + "_embed", 383*ec63e07aSXin Li srcs = [name + ".bin"], 384*ec63e07aSXin Li namespace = namespace, 385*ec63e07aSXin Li **common 386*ec63e07aSXin Li ) 387*ec63e07aSXin Li embed_dir = get_embed_dir() 388*ec63e07aSXin Li 389*ec63e07aSXin Li sapi_interface( 390*ec63e07aSXin Li name = name + ".interface", 391*ec63e07aSXin Li lib_name = lib_name, 392*ec63e07aSXin Li lib = lib, 393*ec63e07aSXin Li functions = functions, 394*ec63e07aSXin Li input_files = input_files, 395*ec63e07aSXin Li out = generated_header, 396*ec63e07aSXin Li embed_name = embed_name, 397*ec63e07aSXin Li embed_dir = embed_dir, 398*ec63e07aSXin Li namespace = namespace, 399*ec63e07aSXin Li api_version = api_version, 400*ec63e07aSXin Li generator_version = generator_version, 401*ec63e07aSXin Li limit_scan_depth = limit_scan_depth, 402*ec63e07aSXin Li **common 403*ec63e07aSXin Li ) 404