236 lines
8.5 KiB
Python
236 lines
8.5 KiB
Python
# Copyright (C) 2022 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Bazel rules for exporting API contributions of CC libraries"""
|
|
|
|
load("@bazel_skylib//lib:paths.bzl", "paths")
|
|
load("@bazel_skylib//lib:sets.bzl", "sets")
|
|
load("//build/bazel/rules/cc:cc_constants.bzl", "constants")
|
|
load(":api_surface.bzl", "MODULE_LIB_API", "PUBLIC_API", "VENDOR_API")
|
|
|
|
"""A Bazel provider that encapsulates the headers presented to an API surface"""
|
|
CcApiHeaderInfo = provider(
|
|
fields = {
|
|
"name": "Name identifying the header files",
|
|
"root": "Directory containing the header files, relative to workspace root. This will become the -I parameter in consuming API domains. This defaults to the current Bazel package",
|
|
"headers": "The header (.h) files presented by the library to an API surface",
|
|
"system": "bool, This will determine whether the include path will be -I or -isystem",
|
|
"arch": "Target arch of devices that use these header files to compile. The default is empty, which means that it is arch-agnostic",
|
|
},
|
|
)
|
|
|
|
def _cc_api_header_impl(ctx):
|
|
"""Implementation for the cc_api_headers rule.
|
|
This rule does not have any build actions, but returns a `CcApiHeaderInfo` provider object"""
|
|
headers_filepath = [header.path for header in ctx.files.hdrs]
|
|
root = paths.dirname(ctx.build_file_path)
|
|
if ctx.attr.include_dir:
|
|
root = paths.join(root, ctx.attr.include_dir)
|
|
info = CcApiHeaderInfo(
|
|
name = ctx.label.name,
|
|
root = root,
|
|
headers = headers_filepath,
|
|
system = ctx.attr.system,
|
|
arch = ctx.attr.arch,
|
|
)
|
|
|
|
# TODO: Use depset for CcApiHeaderInfoList to optimize merges in `_cc_api_contribution_impl`
|
|
return [
|
|
info,
|
|
CcApiHeaderInfoList(
|
|
headers_list = [info],
|
|
),
|
|
]
|
|
|
|
"""A bazel rule that encapsulates the header contributions of a CC library to an API surface
|
|
This rule does not contain the API symbolfile (.map.txt). The API symbolfile is part of the cc_api_contribution rule
|
|
This layering is necessary since the symbols present in a single .map.txt file can be defined in different include directories
|
|
e.g.
|
|
├── Android.bp
|
|
├── BUILD
|
|
├── include <-- cc_api_headers
|
|
├── include_other <-- cc_api_headers
|
|
├── libfoo.map.txt
|
|
"""
|
|
cc_api_headers = rule(
|
|
implementation = _cc_api_header_impl,
|
|
attrs = {
|
|
"include_dir": attr.string(
|
|
mandatory = False,
|
|
doc = "Directory containing the header files, relative to the Bazel package. This relative path will be joined with the Bazel package path to become the -I parameter in the consuming API domain",
|
|
),
|
|
"hdrs": attr.label_list(
|
|
mandatory = True,
|
|
allow_files = constants.hdr_dot_exts,
|
|
doc = "List of .h files presented to the API surface. Glob patterns are allowed",
|
|
),
|
|
"system": attr.bool(
|
|
default = False,
|
|
doc = "Boolean to indicate whether these are system headers",
|
|
),
|
|
"arch": attr.string(
|
|
mandatory = False,
|
|
values = ["arm", "arm64", "x86", "x86_64"],
|
|
doc = "Arch of the target device. The default is empty, which means that the headers are arch-agnostic",
|
|
),
|
|
},
|
|
)
|
|
|
|
"""List container for multiple CcApiHeaderInfo providers"""
|
|
CcApiHeaderInfoList = provider(
|
|
fields = {
|
|
"headers_list": "List of CcApiHeaderInfo providers presented by a target",
|
|
},
|
|
)
|
|
|
|
def _cc_api_library_headers_impl(ctx):
|
|
hdrs_info = []
|
|
for hdr in ctx.attr.hdrs:
|
|
for hdr_info in hdr[CcApiHeaderInfoList].headers_list:
|
|
hdrs_info.append(hdr_info)
|
|
|
|
return [
|
|
CcApiHeaderInfoList(
|
|
headers_list = hdrs_info,
|
|
),
|
|
]
|
|
|
|
_cc_api_library_headers = rule(
|
|
implementation = _cc_api_library_headers_impl,
|
|
attrs = {
|
|
"hdrs": attr.label_list(
|
|
mandatory = True,
|
|
providers = [CcApiHeaderInfoList],
|
|
),
|
|
},
|
|
)
|
|
|
|
# Internal header library targets created by cc_api_library_headers macro
|
|
# Bazel does not allow target name to end with `/`
|
|
def _header_target_name(name, include_dir):
|
|
return name + "_" + paths.normalize(include_dir)
|
|
|
|
def cc_api_library_headers(
|
|
name,
|
|
hdrs = [], # @unused
|
|
export_includes = [],
|
|
export_system_includes = [],
|
|
arch = None,
|
|
deps = [],
|
|
**kwargs):
|
|
header_deps = []
|
|
for include in export_includes:
|
|
_name = _header_target_name(name, include)
|
|
|
|
# export_include = "." causes the following error in glob
|
|
# Error in glob: segment '.' not permitted
|
|
# Normalize path before globbing
|
|
fragments = [include, "**/*.h"]
|
|
normpath = paths.normalize(paths.join(*fragments))
|
|
|
|
cc_api_headers(
|
|
name = _name,
|
|
include_dir = include,
|
|
hdrs = native.glob([normpath]),
|
|
system = False,
|
|
arch = arch,
|
|
)
|
|
header_deps.append(_name)
|
|
|
|
for system_include in export_system_includes:
|
|
_name = _header_target_name(name, system_include)
|
|
cc_api_headers(
|
|
name = _name,
|
|
include_dir = system_include,
|
|
hdrs = native.glob([paths.join(system_include, "**/*.h")]),
|
|
system = True,
|
|
arch = arch,
|
|
)
|
|
header_deps.append(_name)
|
|
|
|
# deps should be exported
|
|
header_deps.extend(deps)
|
|
|
|
_cc_api_library_headers(
|
|
name = name,
|
|
hdrs = header_deps,
|
|
**kwargs
|
|
)
|
|
|
|
"""A Bazel provider that encapsulates the contributions of a CC library to an API surface"""
|
|
CcApiContributionInfo = provider(
|
|
fields = {
|
|
"name": "Name of the cc library",
|
|
"api": "Path of map.txt describing the stable APIs of the library. Path is relative to workspace root",
|
|
"headers": "metadata of the header files of the cc library",
|
|
"api_surfaces": "API surface(s) this library contributes to",
|
|
},
|
|
)
|
|
|
|
VALID_CC_API_SURFACES = [
|
|
PUBLIC_API,
|
|
MODULE_LIB_API, # API surface provided by platform and mainline modules to other mainline modules
|
|
VENDOR_API,
|
|
]
|
|
|
|
def _validate_api_surfaces(api_surfaces):
|
|
for api_surface in api_surfaces:
|
|
if api_surface not in VALID_CC_API_SURFACES:
|
|
fail(api_surface, " is not a valid API surface. Acceptable values: ", VALID_CC_API_SURFACES)
|
|
|
|
def _cc_api_contribution_impl(ctx):
|
|
"""Implemenation for the cc_api_contribution rule
|
|
This rule does not have any build actions, but returns a `CcApiContributionInfo` provider object"""
|
|
api_filepath = ctx.file.api.path
|
|
hdrs_info = sets.make()
|
|
for hdr in ctx.attr.hdrs:
|
|
for hdr_info in hdr[CcApiHeaderInfoList].headers_list:
|
|
sets.insert(hdrs_info, hdr_info)
|
|
|
|
name = ctx.attr.library_name or ctx.label.name
|
|
_validate_api_surfaces(ctx.attr.api_surfaces)
|
|
|
|
return [
|
|
CcApiContributionInfo(
|
|
name = name,
|
|
api = api_filepath,
|
|
headers = sets.to_list(hdrs_info),
|
|
api_surfaces = ctx.attr.api_surfaces,
|
|
),
|
|
]
|
|
|
|
cc_api_contribution = rule(
|
|
implementation = _cc_api_contribution_impl,
|
|
attrs = {
|
|
"library_name": attr.string(
|
|
mandatory = False,
|
|
doc = "Name of the library. This can be different from `name` to prevent name collision with the implementation of the library in the same Bazel package. Defaults to label.name",
|
|
),
|
|
"api": attr.label(
|
|
mandatory = True,
|
|
allow_single_file = [".map.txt", ".map"],
|
|
doc = ".map.txt file of the library",
|
|
),
|
|
"hdrs": attr.label_list(
|
|
mandatory = False,
|
|
providers = [CcApiHeaderInfoList],
|
|
doc = "Header contributions of the cc library. This should return a `CcApiHeaderInfo` provider",
|
|
),
|
|
"api_surfaces": attr.string_list(
|
|
doc = "API surface(s) this library contributes to. See VALID_CC_API_SURFACES in cc_api_contribution.bzl for valid values for API surfaces",
|
|
default = ["publicapi"],
|
|
),
|
|
},
|
|
)
|