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