• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2021 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
16load("@soong_injection//android:constants.bzl", android_constants = "constants")
17load("@soong_injection//api_levels:platform_versions.bzl", "platform_versions")
18load("//build/bazel/rules:common.bzl", "strip_bp2build_label_suffix")
19load("//build/bazel/rules/common:api.bzl", "api")
20
21_bionic_targets = ["//bionic/libc", "//bionic/libdl", "//bionic/libm"]
22_static_bionic_targets = ["//bionic/libc:libc_bp2build_cc_library_static", "//bionic/libdl:libdl_bp2build_cc_library_static", "//bionic/libm:libm_bp2build_cc_library_static"]
23
24# When building a APEX, stub libraries of libc, libdl, libm should be used in linking.
25_bionic_stub_targets = [
26    "//bionic/libc:libc_stub_libs_current",
27    "//bionic/libdl:libdl_stub_libs_current",
28    "//bionic/libm:libm_stub_libs_current",
29]
30
31# The default system_dynamic_deps value for cc libraries. This value should be
32# used if no value for system_dynamic_deps is specified.
33system_dynamic_deps_defaults = select({
34    "//build/bazel/rules/apex:android-in_apex": _bionic_stub_targets,
35    "//build/bazel/rules/apex:android-non_apex": _bionic_targets,
36    "//build/bazel/rules/apex:linux_bionic-in_apex": _bionic_stub_targets,
37    "//build/bazel/rules/apex:linux_bionic-non_apex": _bionic_targets,
38    "//conditions:default": [],
39})
40
41system_static_deps_defaults = select({
42    "//build/bazel/rules/apex:android-in_apex": _bionic_stub_targets,
43    "//build/bazel/rules/apex:android-non_apex": _static_bionic_targets,
44    "//build/bazel/rules/apex:linux_bionic-in_apex": _bionic_stub_targets,
45    "//build/bazel/rules/apex:linux_bionic-non_apex": _static_bionic_targets,
46    "//conditions:default": [],
47})
48
49# List comes from here:
50# https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/cc.go;l=1441;drc=9fd9129b5728602a4768e8e8e695660b683c405e
51_bionic_libs = ["libc", "libm", "libdl", "libdl_android", "linker", "linkerconfig"]
52
53# Comes from here:
54# https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/cc.go;l=1450;drc=9fd9129b5728602a4768e8e8e695660b683c405e
55_bootstrap_libs = ["libclang_rt.hwasan"]
56
57future_version = 10000
58
59CcSanitizerLibraryInfo = provider(
60    "Denotes which sanitizer libraries to include",
61    fields = {
62        "propagate_ubsan_deps": ("True if any ubsan sanitizers are " +
63                                 "enabled on any transitive deps, or " +
64                                 "the current target. False otherwise"),
65    },
66)
67
68# Must be called from within a rule (not a macro) so that the features select
69# has been resolved.
70def get_sanitizer_lib_info(features, deps):
71    propagate_ubsan_deps = False
72    for feature in features:
73        if feature.startswith("ubsan_"):
74            propagate_ubsan_deps = True
75            break
76    if not propagate_ubsan_deps:
77        for dep in deps:
78            if (CcSanitizerLibraryInfo in dep and
79                dep[CcSanitizerLibraryInfo].propagate_ubsan_deps):
80                propagate_ubsan_deps = True
81                break
82    return CcSanitizerLibraryInfo(
83        propagate_ubsan_deps = propagate_ubsan_deps,
84    )
85
86def _sanitizer_deps_impl(ctx):
87    if (CcSanitizerLibraryInfo in ctx.attr.dep and
88        ctx.attr.dep[CcSanitizerLibraryInfo].propagate_ubsan_deps):
89        # To operate correctly with native cc_binary and cc_sharedLibrary,
90        # copy the linker inputs and ensure that this target is marked as the
91        # "owner". Otherwise, upstream targets may drop these linker inputs.
92        # See b/264894507.
93        libraries = [
94            lib
95            for input in ctx.attr._ubsan_library[CcInfo].linking_context.linker_inputs.to_list()
96            for lib in input.libraries
97        ]
98        new_linker_input = cc_common.create_linker_input(
99            owner = ctx.label,
100            libraries = depset(direct = libraries),
101        )
102        linking_context = cc_common.create_linking_context(
103            linker_inputs = depset(direct = [new_linker_input]),
104        )
105        return [CcInfo(linking_context = linking_context)]
106    return [CcInfo()]
107
108# This rule is essentially a workaround to be able to add dependencies
109# conditionally based on provider values
110sanitizer_deps = rule(
111    implementation = _sanitizer_deps_impl,
112    doc = "A rule that propagates given sanitizer dependencies if the " +
113          "proper conditions are met",
114    attrs = {
115        "dep": attr.label(
116            mandatory = True,
117            doc = "library to check for sanitizer dependency propagation",
118        ),
119        "_ubsan_library": attr.label(
120            default = "//prebuilts/clang/host/linux-x86:libclang_rt.ubsan_minimal",
121            doc = "The library target corresponding to the undefined " +
122                  "behavior sanitizer library to be used",
123        ),
124    },
125    provides = [CcInfo],
126)
127
128def sdk_version_feature_from_parsed_version(version):
129    return "sdk_version_" + str(version)
130
131def _create_sdk_version_features_map():
132    version_feature_map = {}
133    for level in api.api_levels.values():
134        version_feature_map["//build/bazel/rules/apex:min_sdk_version_" + str(level)] = [sdk_version_feature_from_parsed_version(level)]
135    version_feature_map["//conditions:default"] = [sdk_version_feature_from_parsed_version(future_version)]
136
137    return version_feature_map
138
139sdk_version_features = select(_create_sdk_version_features_map())
140
141def add_lists_defaulting_to_none(*args):
142    """Adds multiple lists, but is well behaved with a `None` default."""
143    combined = None
144    for arg in args:
145        if arg != None:
146            if combined == None:
147                combined = []
148            combined += arg
149
150    return combined
151
152# get_includes_paths expects a rule context, a list of directories, and
153# whether the directories are package-relative and returns a list of exec
154# root-relative paths. This handles the need to search for files both in the
155# source tree and generated files.
156def get_includes_paths(ctx, dirs, package_relative = True):
157    execution_relative_dirs = []
158    for rel_dir in dirs:
159        if rel_dir == ".":
160            rel_dir = ""
161        execution_rel_dir = rel_dir
162        if package_relative:
163            execution_rel_dir = ctx.label.package
164            if len(rel_dir) > 0:
165                execution_rel_dir = execution_rel_dir + "/" + rel_dir
166
167        # To allow this repo to be used as an external one.
168        repo_prefix_dir = execution_rel_dir
169        if ctx.label.workspace_root != "":
170            repo_prefix_dir = ctx.label.workspace_root + "/" + execution_rel_dir
171        execution_relative_dirs.append(repo_prefix_dir)
172
173        # to support generated files, we also need to export includes relatives to the bin directory
174        if not execution_rel_dir.startswith("/"):
175            execution_relative_dirs.append(ctx.bin_dir.path + "/" + execution_rel_dir)
176    return execution_relative_dirs
177
178def create_ccinfo_for_includes(
179        ctx,
180        hdrs = [],
181        includes = [],
182        absolute_includes = [],
183        system_includes = [],
184        deps = []):
185    # Create a compilation context using the string includes of this target.
186    compilation_context = cc_common.create_compilation_context(
187        headers = depset(hdrs),
188        includes = depset(
189            get_includes_paths(ctx, includes) +
190            get_includes_paths(ctx, absolute_includes, package_relative = False),
191        ),
192        system_includes = depset(get_includes_paths(ctx, system_includes)),
193    )
194
195    # Combine this target's compilation context with those of the deps; use only
196    # the compilation context of the combined CcInfo.
197    cc_infos = [dep[CcInfo] for dep in deps]
198    cc_infos.append(CcInfo(compilation_context = compilation_context))
199    combined_info = cc_common.merge_cc_infos(cc_infos = cc_infos)
200
201    return CcInfo(compilation_context = combined_info.compilation_context)
202
203def is_external_directory(package_name):
204    if package_name.startswith("external"):
205        return True
206    if package_name.startswith("hardware"):
207        paths = package_name.split("/")
208        if len(paths) < 2:
209            return True
210        secondary_path = paths[1]
211        if secondary_path in ["google", "interfaces", "ril"]:
212            return False
213        return not secondary_path.startswith("libhardware")
214    if package_name.startswith("vendor"):
215        paths = package_name.split("/")
216        if len(paths) < 2:
217            return True
218        secondary_path = paths[1]
219        return "google" not in secondary_path
220    return False
221
222# TODO: Move this to a common rule dir, instead of a cc rule dir. Nothing here
223# should be cc specific, except that the current callers are (only) cc rules.
224def parse_sdk_version(version):
225    if version == "apex_inherit":
226        # use the version determined by the transition value.
227        return sdk_version_features + [sdk_version_feature_from_parsed_version("apex_inherit")]
228
229    return [sdk_version_feature_from_parsed_version(parse_apex_sdk_version(version))]
230
231def parse_apex_sdk_version(version):
232    if version == "" or version == "current" or version == "10000":
233        return future_version
234    elif version in api.api_levels.keys():
235        return api.api_levels[version]
236    elif version.isdigit():
237        version = int(version)
238        if version in api.api_levels.values():
239            return version
240        elif version == platform_versions.platform_sdk_version:
241            # For internal branch states, support parsing a finalized version number
242            # that's also still in
243            # platform_versions.platform_version_active_codenames, but not api.api_levels.
244            #
245            # This happens a few months each year on internal branches where the
246            # internal master branch has a finalized API, but is not released yet,
247            # therefore the Platform_sdk_version is usually latest AOSP dessert
248            # version + 1. The generated api.api_levels map sets these to 9000 + i,
249            # where i is the index of the current/future version, so version is not
250            # in the api.api_levels.values() list, but it is a valid sdk version.
251            #
252            # See also b/234321488#comment2
253            return version
254    fail("Unknown sdk version: %s, could not be parsed as " % version +
255         "an integer and/or is not a recognized codename. Valid api levels are:" +
256         str(api.api_levels))
257
258CPP_EXTENSIONS = ["cc", "cpp", "c++"]
259
260C_EXTENSIONS = ["c"]
261
262_HEADER_EXTENSIONS = ["h", "hh", "hpp", "hxx", "h++", "inl", "inc", "ipp", "h.generic"]
263
264def get_non_header_srcs(input_srcs, exclude_srcs = [], source_extensions = None, header_extensions = _HEADER_EXTENSIONS):
265    """get_non_header_srcs returns a list of srcs that do not have header extensions and aren't in the exclude srcs list
266
267    Args:
268        input_srcs (list[File]): list of files to filter
269        exclude_srcs (list[File]): list of files that should be excluded from the returned list
270        source_extensions (list[str]): list of extensions that designate sources.
271            If None, all extensions are valid. Otherwise only source with these extensions are returned
272        header_extensions (list[str]): list of extensions that designate headers
273    Returns:
274        srcs, hdrs (list[File], list[File]): tuple of lists of files; srcs have non-header extension and are not excluded,
275            and hdrs are files with header extensions
276    """
277    srcs = []
278    hdrs = []
279    for s in input_srcs:
280        is_source = not source_extensions or s.extension in source_extensions
281        if s.extension in header_extensions:
282            hdrs.append(s)
283        elif is_source and s not in exclude_srcs:
284            srcs.append(s)
285    return srcs, hdrs
286
287def prefix_in_list(str, prefixes):
288    """returns the prefix if any element of prefixes is a prefix of path
289
290    Args:
291        str (str): the string to compare prefixes against
292        prefixes (list[str]): a list of prefixes to check against str
293    Returns:
294        prefix (str or None): the prefix (if any) that str starts with
295    """
296    for prefix in prefixes:
297        if str.startswith(prefix):
298            return prefix
299    return None
300
301_DISALLOWED_INCLUDE_DIRS = android_constants.NeverAllowNotInIncludeDir
302_PACKAGES_DISALLOWED_TO_SPECIFY_INCLUDE_DIRS = android_constants.NeverAllowNoUseIncludeDir
303
304def check_absolute_include_dirs_disabled(target_package, absolute_includes):
305    """checks that absolute include dirs are disabled for some directories
306
307    Args:
308        target_package (str): package of current target
309        absolute_includes (list[str]): list of absolute include directories
310    """
311    if len(absolute_includes) > 0:
312        disallowed_prefix = prefix_in_list(
313            target_package,
314            _PACKAGES_DISALLOWED_TO_SPECIFY_INCLUDE_DIRS,
315        )
316        if disallowed_prefix != None:
317            fail("include_dirs is deprecated, all usages of them in '" +
318                 disallowed_prefix + "' have been migrated to use alternate" +
319                 " mechanisms and so can no longer be used.")
320
321    for path in absolute_includes:
322        if path in _DISALLOWED_INCLUDE_DIRS:
323            fail("include_dirs is deprecated, all usages of '" + path + "' have" +
324                 " been migrated to use alternate mechanisms and so can no longer" +
325                 " be used.")
326
327def get_compilation_args(toolchain, feature_config, flags, compilation_ctx, action_name):
328    compilation_vars = cc_common.create_compile_variables(
329        cc_toolchain = toolchain,
330        feature_configuration = feature_config,
331        user_compile_flags = flags,
332        include_directories = compilation_ctx.includes,
333        quote_include_directories = compilation_ctx.quote_includes,
334        system_include_directories = compilation_ctx.system_includes,
335        framework_include_directories = compilation_ctx.framework_includes,
336    )
337
338    return cc_common.get_memory_inefficient_command_line(
339        feature_configuration = feature_config,
340        action_name = action_name,
341        variables = compilation_vars,
342    )
343
344def build_compilation_flags(ctx, deps, user_flags, action_name):
345    cc_toolchain = find_cpp_toolchain(ctx)
346
347    feature_config = cc_common.configure_features(
348        ctx = ctx,
349        cc_toolchain = cc_toolchain,
350        language = "c++",
351        requested_features = ctx.features,
352        unsupported_features = ctx.disabled_features,
353    )
354
355    cc_info = cc_common.merge_cc_infos(direct_cc_infos = [d[CcInfo] for d in deps])
356
357    compilation_flags = get_compilation_args(
358        toolchain = cc_toolchain,
359        feature_config = feature_config,
360        flags = user_flags,
361        compilation_ctx = cc_info.compilation_context,
362        action_name = action_name,
363    )
364
365    return cc_info.compilation_context, compilation_flags
366
367def is_bionic_lib(name):
368    return name in _bionic_libs
369
370def is_bootstrap_lib(name):
371    return name in _bootstrap_libs
372
373CcAndroidMkInfo = provider(
374    "Provides information to be passed to AndroidMk in Soong",
375    fields = {
376        "local_static_libs": "list of target names passed to LOCAL_STATIC_LIBRARIES AndroidMk variable",
377        "local_whole_static_libs": "list of target names passed to LOCAL_WHOLE_STATIC_LIBRARIES AndroidMk variable",
378        "local_shared_libs": "list of target names passed to LOCAL_SHARED_LIBRARIES AndroidMk variable",
379    },
380)
381
382def create_cc_androidmk_provider(*, static_deps, whole_archive_deps, dynamic_deps):
383    # Since this information is provided to Soong for mixed builds,
384    # we are just taking the Soong module name rather than the Bazel
385    # label.
386    # TODO(b/266197834) consider moving this logic to the mixed builds
387    # handler in Soong
388    local_static_libs = [
389        strip_bp2build_label_suffix(d.label.name)
390        for d in static_deps
391    ]
392    local_whole_static_libs = [
393        strip_bp2build_label_suffix(d.label.name)
394        for d in whole_archive_deps
395    ]
396    local_shared_libs = [
397        strip_bp2build_label_suffix(d.label.name)
398        for d in dynamic_deps
399    ]
400    return CcAndroidMkInfo(
401        local_static_libs = local_static_libs,
402        local_whole_static_libs = local_whole_static_libs,
403        local_shared_libs = local_shared_libs,
404    )
405
406def create_cc_prebuilt_library_info(ctx, lib_to_link):
407    "Create the CcInfo for a prebuilt_library_{shared,static}"
408
409    compilation_context = cc_common.create_compilation_context(
410        includes = depset(get_includes_paths(ctx, ctx.attr.export_includes)),
411        system_includes = depset(get_includes_paths(ctx, ctx.attr.export_system_includes)),
412    )
413    linker_input = cc_common.create_linker_input(
414        owner = ctx.label,
415        libraries = depset(direct = [lib_to_link] if lib_to_link != None else []),
416    )
417    linking_context = cc_common.create_linking_context(
418        linker_inputs = depset(direct = [linker_input]),
419    )
420    return CcInfo(
421        compilation_context = compilation_context,
422        linking_context = linking_context,
423    )
424