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