diff --git a/.bazelrc b/.bazelrc index afd4041ce..224f63e50 100644 --- a/.bazelrc +++ b/.bazelrc @@ -25,9 +25,10 @@ build --compilation_mode=dbg # Use 'worker' strategy for swift compilation build --strategy=SwiftCompile=worker -# This flips index_while_building_v2 - see docs/index_while_building.md for a +# This flips enable_global_index_store - see docs/index_while_building.md for a # detailed summary -build --features swift.index_while_building_v2 +build --features swift.use_global_index_store +build --features swift.index_while_building # Prevents leaking unexpected binaries via PATH to tests build --test_env=PATH=/usr/bin:/bin:/usr/sbin:/sbin diff --git a/.bazelversion b/.bazelversion index fcdb2e109..ee74734aa 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -4.0.0 +4.1.0 diff --git a/BUILD.bazel b/BUILD.bazel index ef1ec1ca8..96df5f81e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,6 +1,6 @@ # Pull buildifer.mac as an http_file, then depend on the file group to make an # executable -load("@build_bazel_rules_swift//swift/internal:feature_names.bzl", "SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2") +load("@build_bazel_rules_swift//swift/internal:feature_names.bzl", "SWIFT_FEATURE_USE_GLOBAL_INDEX_STORE") sh_binary( name = "buildifier", @@ -8,8 +8,16 @@ sh_binary( ) config_setting( - name = "index_while_building_v2", + name = "use_global_index_store", values = { - "features": SWIFT_FEATURE_INDEX_WHILE_BUILDING_V2, + "features": SWIFT_FEATURE_USE_GLOBAL_INDEX_STORE, }, ) + +load("//rules:swift_toolchain.bzl", _swift_toolchain="swift_toolchain") + +_swift_toolchain( + name = "swift_toolchain", + visibility = ["//visibility:public"], +) + diff --git a/rules/library.bzl b/rules/library.bzl index 823f3e0cd..9c8f3c1c2 100644 --- a/rules/library.bzl +++ b/rules/library.bzl @@ -5,7 +5,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//lib:sets.bzl", "sets") load("@bazel_skylib//lib:selects.bzl", "selects") load("@build_bazel_rules_apple//apple:apple.bzl", "apple_dynamic_framework_import", "apple_static_framework_import") -load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +load("//rules:swift_library.bzl", "swift_library") load("//rules:precompiled_apple_resource_bundle.bzl", "precompiled_apple_resource_bundle") load("//rules:hmap.bzl", "headermap") load("//rules/framework:vfs_overlay.bzl", "framework_vfs_overlay", VFS_OVERLAY_FRAMEWORK_SEARCH_PATH = "FRAMEWORK_SEARCH_PATH") @@ -678,7 +678,7 @@ def apple_library(name, library_tools = {}, export_private_headers = True, names additional_objc_copts.append("-I.") index_while_building_objc_copts = select({ - "@build_bazel_rules_ios//:index_while_building_v2": [ + "@build_bazel_rules_ios//:use_global_index_store": [ # Note: this won't work work for remote caching yet. It uses a # _different_ global index for objc than so that the BEP grep in # rules_ios picks this up. diff --git a/rules/repositories.bzl b/rules/repositories.bzl index 72daae83c..e7a25c26d 100644 --- a/rules/repositories.bzl +++ b/rules/repositories.bzl @@ -58,10 +58,10 @@ def rules_ios_dependencies(): _maybe( github_repo, name = "build_bazel_rules_swift", - ref = "14d26dcedf0290bd777f6fe83cde3586dc616513", + ref = "542a8bd2d70bb7b6a844a8602636e235e8343406", project = "bazel-ios", repo = "rules_swift", - sha256 = "8d87afbb43fa4f12ffd02c639bbc5a80eda0141bfaf74e4028d8f570d25d032c", + sha256 = "ce1ba86de697b74380b0735ea623757150a74f99adcdd6af7deeb0083ce00d62", ) _maybe( @@ -74,41 +74,6 @@ def rules_ios_dependencies(): sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", ) - # Note: it relies on `index-import` to import indexes. Longer term this - # dependency may be added by rules_swift - # This release is a build of this PR https://github.com/lyft/index-import/pull/53 - _maybe( - http_archive, - name = "build_bazel_rules_swift_index_import", - build_file_content = """\ -load("@bazel_skylib//rules:native_binary.bzl", "native_binary") - -native_binary( - name = "index_import", - src = "index-import", - out = "index-import", - visibility = ["//visibility:public"], -) - -native_binary( - name = "validate_index", - src = "validate-index", - out = "validate-index", - visibility = ["//visibility:public"], -) - -native_binary( - name = "absolute_unit", - src = "absolute-unit", - out = "absolute-unit", - visibility = ["//visibility:public"], -) -""", - canonical_id = "index-import-5.3.2.5", - urls = ["https://github.com/bazel-ios/index-import/releases/download/5.3.2.5/index-import.zip"], - sha256 = "79e9b2cd3e988155b86668c56d95705e1a4a7c7b6d702ff5ded3a18d1291a39a", - ) - _maybe( http_archive, name = "com_github_yonaskolb_xcodegen", diff --git a/rules/swift_library.bzl b/rules/swift_library.bzl new file mode 100644 index 000000000..7625c319f --- /dev/null +++ b/rules/swift_library.bzl @@ -0,0 +1,334 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# 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. + +# Note: this file was based on rules_swift. Longer term, we may consider calling +# swift_common.compile directly and get rid of the extra rule in our stack. + +"""Implementation of the `swift_library` rule.""" + +load("@build_bazel_rules_swift//swift/internal:attrs.bzl", "swift_deps_attr") +load( + "@build_bazel_rules_swift//swift/internal:build_settings.bzl", + "PerModuleSwiftCoptSettingInfo", + "additional_per_module_swiftcopts", +) +load( + "@build_bazel_rules_swift//swift/internal:compiling.bzl", + "new_objc_provider", + "output_groups_from_compilation_outputs", + "swift_library_output_map", +) +load( + "@build_bazel_rules_swift//swift/internal:feature_names.bzl", + "SWIFT_FEATURE_EMIT_SWIFTINTERFACE", + "SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION", + "SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS", +) +load("@build_bazel_rules_swift//swift/internal:linking.bzl", "create_linker_input") +load("@build_bazel_rules_swift//swift/internal:providers.bzl", "SwiftInfo", "SwiftToolchainInfo") +load("@build_bazel_rules_swift//swift/internal:swift_clang_module_aspect.bzl", "swift_clang_module_aspect") +load("@build_bazel_rules_swift//swift/internal:swift_common.bzl", "swift_common") +load( + "@build_bazel_rules_swift//swift/internal:utils.bzl", + "compact", + "create_cc_info", + "expand_locations", + "expand_make_variables", + "get_providers", +) +load("@bazel_skylib//lib:dicts.bzl", "dicts") +load("@bazel_skylib//lib:sets.bzl", "sets") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") + +def _maybe_parse_as_library_copts(srcs): + """Returns a list of compiler flags depending on `main.swift`'s presence. + + Builds on Apple platforms typically don't use `swift_binary`; they use + different linking logic (https://github.com/bazelbuild/rules_apple) to + produce fat binaries and bundles. This means that all such application code + will typically be in a `swift_library` target, and that includes a possible + custom main entry point. For this reason, we need to support the creation of + `swift_library` targets containing a `main.swift` file, which should *not* + pass the `-parse-as-library` flag to the compiler. + + Args: + srcs: A list of source files to check for the presence of `main.swift`. + + Returns: + An empty list if `main.swift` was present in `srcs`, or a list + containing a single element `"-parse-as-library"` if `main.swift` was + not present. + """ + use_parse_as_library = True + for src in srcs: + if src.basename == "main.swift": + use_parse_as_library = False + break + return ["-parse-as-library"] if use_parse_as_library else [] + +def _check_deps_are_disjoint(label, deps, private_deps): + """Checks that the given sets of dependencies are disjoint. + + If the same target is listed in both sets, the build will fail. + + Args: + label: The label of the target that will be printed in the failure + message if the sets are not disjoint. + deps: The list of public dependencies of the target. + private_deps: The list of private dependencies of the target. + """ + + # If either set is empty, we don't need to check. + if not deps or not private_deps: + return + + deps_set = sets.make([str(dep.label) for dep in deps]) + private_deps_set = sets.make([str(dep.label) for dep in private_deps]) + intersection = sets.to_list(sets.intersection(deps_set, private_deps_set)) + if intersection: + detail_msg = ["\n - {}".format(label) for label in intersection] + fail(("In target '{}', 'deps' and 'private_deps' must be disjoint, " + + "but the following targets were found in both: {}").format( + label, + detail_msg, + )) + +def _swift_library_impl(ctx): + additional_inputs = ctx.files.swiftc_inputs + + # These can't use additional_inputs since expand_locations needs targets, + # not files. + copts = expand_locations(ctx, ctx.attr.copts, ctx.attr.swiftc_inputs) + copts = expand_make_variables(ctx, copts, "copts") + linkopts = expand_locations(ctx, ctx.attr.linkopts, ctx.attr.swiftc_inputs) + linkopts = expand_make_variables(ctx, linkopts, "linkopts") + srcs = ctx.files.srcs + + module_copts = additional_per_module_swiftcopts( + ctx.label, + ctx.attr._per_module_swiftcopt[PerModuleSwiftCoptSettingInfo], + ) + copts.extend(module_copts) + + extra_features = [] + if ctx.attr._config_emit_swiftinterface[BuildSettingInfo].value: + extra_features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION) + extra_features.append(SWIFT_FEATURE_EMIT_SWIFTINTERFACE) + + module_name = ctx.attr.module_name + if not module_name: + module_name = swift_common.derive_module_name(ctx.label) + + swift_toolchain = ctx.attr._toolchain[SwiftToolchainInfo] + feature_configuration = swift_common.configure_features( + ctx = ctx, + requested_features = ctx.features + extra_features, + swift_toolchain = swift_toolchain, + unsupported_features = ctx.disabled_features, + ) + + if swift_common.is_enabled( + feature_configuration = feature_configuration, + feature_name = SWIFT_FEATURE_SUPPORTS_PRIVATE_DEPS, + ): + # The implicit deps can be added to the private deps; since they are + # added to the compilation of every library, they don't need to be + # propagated. However, it's not an error to list one of the implicit + # deps in "deps", either, so we need to make sure not to pass them in to + # `_check_deps_are_disjoint`. + deps = ctx.attr.deps + private_deps = ctx.attr.private_deps + _check_deps_are_disjoint(ctx.label, deps, private_deps) + elif ctx.attr.private_deps: + fail( + ("In target '{}', 'private_deps' cannot be used because this " + + "version of the Swift toolchain does not support " + + "implementation-only imports.").format(ctx.label), + attr = "private_deps", + ) + else: + deps = ctx.attr.deps + private_deps = [] + + if ctx.attr.generates_header: + generated_header_name = ( + ctx.attr.generated_header_name or + "{}-Swift.h".format(ctx.label.name) + ) + elif not ctx.attr.generated_header_name: + generated_header_name = None + else: + fail( + "'generated_header_name' may only be provided when " + + "'generates_header' is True.", + attr = "generated_header_name", + ) + + compilation_outputs = swift_common.compile( + actions = ctx.actions, + additional_inputs = additional_inputs, + bin_dir = ctx.bin_dir, + copts = _maybe_parse_as_library_copts(srcs) + copts, + defines = ctx.attr.defines, + deps = deps, + feature_configuration = feature_configuration, + generated_header_name = generated_header_name, + genfiles_dir = ctx.genfiles_dir, + module_name = module_name, + private_deps = private_deps, + srcs = srcs, + swift_toolchain = swift_toolchain, + target_name = ctx.label.name, + workspace_name = ctx.workspace_name, + ) + + # If a module was created for the generated header, propagate it as well so + # that it is passed as a module input to upstream compilation actions. + if compilation_outputs.generated_module_map: + clang_module = swift_common.create_clang_module( + compilation_context = cc_common.create_compilation_context( + headers = depset([compilation_outputs.generated_header]), + ), + module_map = compilation_outputs.generated_module_map, + precompiled_module = compilation_outputs.precompiled_module, + ) + else: + clang_module = None + + linker_input, library_to_link = create_linker_input( + actions = ctx.actions, + additional_inputs = additional_inputs, + alwayslink = ctx.attr.alwayslink, + cc_feature_configuration = swift_common.cc_feature_configuration( + feature_configuration = feature_configuration, + ), + compilation_outputs = compilation_outputs, + is_dynamic = False, + is_static = True, + library_name = ctx.label.name, + objects = compilation_outputs.object_files, + owner = ctx.label, + swift_toolchain = swift_toolchain, + user_link_flags = linkopts, + ) + + direct_output_files = compact([ + compilation_outputs.generated_header, + compilation_outputs.precompiled_module, + compilation_outputs.swiftdoc, + compilation_outputs.swiftinterface, + compilation_outputs.swiftmodule, + library_to_link.pic_static_library, + ]) + + implicit_deps_providers = swift_toolchain.implicit_deps_providers + providers = [ + DefaultInfo( + files = depset(direct_output_files), + runfiles = ctx.runfiles( + collect_data = True, + collect_default = True, + files = ctx.files.data, + ), + ), + OutputGroupInfo(**output_groups_from_compilation_outputs( + compilation_outputs = compilation_outputs, + )), + create_cc_info( + cc_infos = get_providers(deps, CcInfo), + compilation_outputs = compilation_outputs, + defines = ctx.attr.defines, + includes = [ctx.bin_dir.path], + linker_inputs = [linker_input], + private_cc_infos = ( + get_providers(private_deps, CcInfo) + + implicit_deps_providers.cc_infos + ), + ), + coverage_common.instrumented_files_info( + ctx, + dependency_attributes = ["deps", "private_deps"], + extensions = ["swift"], + source_attributes = ["srcs"], + ), + swift_common.create_swift_info( + modules = [ + swift_common.create_module( + name = module_name, + clang = clang_module, + swift = swift_common.create_swift_module( + defines = ctx.attr.defines, + swiftdoc = compilation_outputs.swiftdoc, + swiftinterface = compilation_outputs.swiftinterface, + swiftmodule = compilation_outputs.swiftmodule, + ), + ), + ], + # Note that private_deps are explicitly omitted here; they should + # not propagate. + swift_infos = get_providers(deps, SwiftInfo), + ), + ] + + # Propagate an `objc` provider if the toolchain supports Objective-C + # interop, which allows `objc_library` targets to import `swift_library` + # targets. + if swift_toolchain.supports_objc_interop: + providers.append(new_objc_provider( + # We must include private_deps here because some of the information + # propagated here is related to linking. + # TODO(allevato): This means we can't yet completely avoid + # propagating headers/module maps from impl-only Obj-C dependencies. + deps = deps + private_deps, + link_inputs = compilation_outputs.linker_inputs + additional_inputs, + linkopts = compilation_outputs.linker_flags + linkopts, + module_map = compilation_outputs.generated_module_map, + static_archives = compact([library_to_link.pic_static_library]), + swiftmodules = [compilation_outputs.swiftmodule], + objc_header = compilation_outputs.generated_header, + objc_providers = implicit_deps_providers.objc_infos, + )) + + return providers + +swift_library = rule( + attrs = dicts.add( + swift_common.library_rule_attrs(additional_deps_aspects = [ + swift_clang_module_aspect, + ]), + { + "private_deps": swift_deps_attr( + aspects = [swift_clang_module_aspect], + doc = """\ +A list of targets that are implementation-only dependencies of the target being +built. Libraries/linker flags from these dependencies will be propagated to +dependent for linking, but artifacts/flags required for compilation (such as +.swiftmodule files, C headers, and search paths) will not be propagated. +""", + ), + }, + # Note: updated to take our custom toolchain + { + "_toolchain" : attr.label( + default = Label("@build_bazel_rules_ios//:swift_toolchain"), + providers = [[SwiftToolchainInfo]], + ), + }), + doc = """\ +Compiles and links Swift code into a static library and Swift module. +""", + outputs = swift_library_output_map, + implementation = _swift_library_impl, + fragments = ["cpp"], +) diff --git a/rules/swift_toolchain.bzl b/rules/swift_toolchain.bzl new file mode 100644 index 000000000..0a2f720dd --- /dev/null +++ b/rules/swift_toolchain.bzl @@ -0,0 +1,43 @@ +load("@build_bazel_rules_swift//swift/internal:providers.bzl", "SwiftToolchainInfo") + +def _impl(ctx): + base = ctx.attr.toolchain[SwiftToolchainInfo] + return [ + SwiftToolchainInfo( + # Note: we'll need to change out the action_configs here. + action_configs = base.action_configs, + all_files = base.all_files, + cc_toolchain_info = base.cc_toolchain_info, + clang_implicit_deps_providers = base.clang_implicit_deps_providers, + cpu = base.cpu, + feature_allowlists = base.feature_allowlists, + generated_header_module_implicit_deps_providers = base.generated_header_module_implicit_deps_providers, + implicit_deps_providers = base.implicit_deps_providers, + linker_opts_producer = base.linker_opts_producer, + linker_supports_filelist = base.linker_supports_filelist, + object_format = base.object_format, + requested_features = base.requested_features, + supports_objc_interop = base.supports_objc_interop, + # Note: we'll need to change out the worker with ours here. + swift_worker = base.swift_worker, + system_name = base.system_name, + test_configuration = base.test_configuration, + tool_configs = base.tool_configs, + unsupported_features = base.unsupported_features, + ), + ] + +# This wraps the rules_swift toolchain: so we get either an Xcode or Linux base +swift_toolchain = rule( + implementation = _impl, + attrs = { + "toolchain": attr.label( + cfg = "host", + allow_files = True, + default = Label( + "@build_bazel_rules_swift_local_config//:toolchain", + ), + ) + }, +) +