rules_swift

repository·main·Indexed 43 days ago

https://github.com/bazelbuild/rules_swift

Swift Rules for Bazel provide rules to build Swift libraries, tests, and executables for macOS, Linux, and Windows. It supports all Apple platforms including iOS, tvOS, visionOS, and watchOS when used alongside Apple Rules. Key rules include `swift_library`, `swift_binary`, and `swift_library_group`. The project also provides the `BazelRunfiles` library for locating data dependencies at runtime and utilities for C/Objective-C interoperability via `create_swift_interop_info`.

Tokens
33.8K
Snippets
91
Records
129
Agent score
55%

What's inside rules_swift

  1. Configure Swift runtime selection

    main

    The SwiftToolchainInfo provider manages how the Swift runtime is linked.

    • Dynamic Runtime: Use dynamic_runtime_cc_info (a CcInfo provider) to select the ordinary dynamic Swift runtime. This is used by final links unless swift.static_stdlib is enabled.
    • Static Runtime: Use static_runtime_cc_info (a CcInfo provider) to select the static Swift runtime. This is used by final links when swift.static_stdlib is enabled.
  2. How toolchain targets are generated

    main

    When you define a toolchain with swift.toolchain(name = "<name>"), the extension generates a parent repository @<name> which exposes the following targets for each platform:

    • @<name>//:cc_toolchain_embedded_<platform>: The C/C++ toolchain used for the embedded Swift target platform.
    • @<name>//:swift_toolchain_embedded_<platform>: The Swift toolchain for the embedded target.
    • @<name>//:swift_toolchain_exec_<platform>: The Swift toolchain used as an exec toolchain (compiling tools that run on the build host).

    The actual extracted toolchain resides in the per-platform repository @<name>_<platform>.

  3. Use `swift_overlay` to extend C/Objective-C libraries with Swift APIs

    main

    The swift_overlay rule allows authors of C or Objective-C libraries to provide additional Swift-specific APIs that are automatically available when a Swift target depends on that library.

    Key Characteristics:

    • Conditional Compilation: The overlay is only compiled when Swift targets depend on the original library. Non-Swift clients will not trigger the build or link of the overlay, preserving performance and binary size.
    • Not for Mixed-Language Modules: It is not a general-purpose tool for mixed-language modules because it does not support generating Objective-C headers.
    • Module Integration: When a Swift target depends on the underlying C/Obj-C library, the overlay code is compiled into the same module. The consumer sees both the original APIs and the overlay APIs as a single combined module.
    • Coupling: There is a tight coupling between an overlay and its target. A swift_overlay target should only be referenced in the aspect_hints of a single objc_library or cc_library target.
    objc_library(
        name = "MyModule",
        srcs = ["MyModule.m"],
        hdrs = ["MyModule.h"],
        aspect_hints = [":MyModule_overlay"],
        deps = [...],
    )
    
    swift_overlay(
        name = "MyModule_overlay",
        srcs = ["MyModule.swift"],
        deps = [...],
    )
  4. Use a custom protobuf compiler with swift_proto_library

    main

    You can use an arbitrary non-protoc Swift protobuf compiler by implementing a custom rule that provides SwiftProtoCompilerInfo. This custom rule must be passed to the compilers attribute of a swift_proto_library target.

    The custom rule's implementation must include a function that accepts a list of ProtoInfo providers and produces a list of Swift source files from them, which the swift_proto_library rule will then compile.

  5. Combine multiple Swift modules using `swift_library_group`

    main

    Use the swift_library_group rule to bundle multiple Swift Bazel targets into a single dependency. This is analogous to providing multiple products in a single Swift Package Manager package. Any target that depends on the swift_library_group will have the Swift modules listed in its deps forwarded to it.

    # Conceptual usage pattern
    swift_library_group(
        name = "Helpers",
        deps = [
            ":TestHelpers",
            ":StringHelpers",
        ],
    )
  6. Configure swift_binary for WebAssembly (Reactor mode)

    main

    When targeting WebAssembly, setting linkshared = True in swift_binary produces a "reactor" module (<name>.wasm).

    This module:

    • Has no _start entry point.
    • Runs initializers via the exported _initialize function.
    • Requires you to explicitly export functions using -Xlinker --export=<symbol> or -Wl,--export=<symbol> flags within the linkopts attribute.
    swift_binary(
        name = "my_wasm_reactor",
        srcs = ["lib.swift"],
        linkshared = True,
        linkopts = ["-Xlinker", "--export=my_exported_function"],
    )
  7. Configure the Swift Proto Compiler

    main

    The swift_proto_compiler rule is a new abstraction used to configure how protos are generated. It encapsulates the protoc binary, the required protoc plugin binaries (like SwiftProtobuf or grpc-swift), and the specific options passed to those plugins.

    This rule is passed to the compilers attribute of a swift_proto_library target. This decoupling allows users to use custom versions of protoc or specific plugins without being tied to the internal dependencies of rules_swift.

  8. Internal Swift providers (Do not use)

    main

    The following providers are internal implementation details of the rules. Users should not rely on them or assume their structure is stable:

    • SwiftFeatureAllowlistInfo: Describes features and packages allowed to request or disable them.
    • SwiftPackageConfigurationInfo: Describes compiler configurations applied by default to specific packages.
  9. Group protos into modules in swift_proto_library

    main

    The swift_proto_library rule does not enforce a strict mapping between proto packages and Swift modules. You have flexibility in how you organize your modules:

    • Compile each proto into its own separate Swift module.
    • Group multiple protos into modules based on their proto packages.
    • Compile all protos into a single large Swift module.
  10. Handle missing dependencies in precompiled frameworks

    main

    When using explicit modules, precompiled frameworks are compiled in the context of their own target rather than the importing target. This requires that any dependencies the framework has (including other precompiled frameworks) are explicitly defined in its BUILD file.

    If you are using rules_swift_package_manager, you must manually map these dependencies in your MODULE.bazel using swift_deps.configure_package.

    swift_deps.configure_package(
        name = "swiftpkg_facebook_ios_sdk",
        target_deps = {
            "FBAEMKit": [":FBSDKCoreKit_Basics"],
            "FBSDKCoreKit": [
                ":FBAEMKit",
                ":FBSDKCoreKit_Basics",
            ],
            "FBSDKGamingServicesKit": [
                ":FBAEMKit",
                ":FBSDKCoreKit_Basics",
                ":FBSDKShareKit",
            ],
            "FBSDKLoginKit": [
                ":FBAEMKit",
                ":FBSDKCoreKit_Basics",
            ],
            "FBSDKShareKit": [
                ":FBAEMKit",
                ":FBSDKCoreKit_Basics",
                ":FBSDKShareKit",
            ],
        },
    )
  11. Use the Swift BazelRunfiles library to locate runfiles

    main

    The BazelRunfiles library allows Swift binaries and tests built with Bazel to locate their data dependencies (runfiles) at runtime.

    To use it:

    1. Add @rules_swift//swift/runfiles to the deps of your swift_binary or swift_test rule.
    2. Import BazelRunfiles in your Swift code.
    3. Use Runfiles.create() to initialize the lookup mechanism.
    4. Use rlocation(_:) with the workspace-relative path to find the file URL.

    Runfiles.create() automatically detects whether to use a manifest-based or directory-based lookup by inspecting environment variables like RUNFILES_MANIFEST_FILE and RUNFILES_DIR. If these are not set, it attempts to locate them relative to the executable's path (argv[0]).

    import BazelRunfiles
    
    do {
        let runfiles = try Runfiles.create()
        let fileURL = try runfiles.rlocation("my_workspace/path/to/my/data.txt")
        print("file: \(fileURL)")
    } catch {
         print("runfiles error: \(error)")
    }