Coder Social home page Coder Social logo

bazelbuild / rules_swift Goto Github PK

View Code? Open in Web Editor NEW
303.0 24.0 133.0 3.8 MB

Bazel rules to build Swift on Apple and Linux platforms

License: Apache License 2.0

Shell 1.01% C++ 9.48% Starlark 86.19% Batchfile 0.03% Swift 3.29%
swift bazel bazel-rules

rules_swift's Introduction

Swift Rules for Bazel

Build status

This repository contains rules for Bazel that can be used to build Swift libraries, tests, and executables for macOS and Linux.

To build applications for all of Apple's platforms (macOS, iOS, tvOS, visionOS, and watchOS), they can be combined with the Apple Rules.

If you run into any problems with these rules, please file an issue!

Basic Examples

Create a simple CLI that can run on macOS, Linux, or Windows:

load("@build_bazel_rules_swift//swift:swift.bzl", "swift_binary")

swift_binary(
    name = "cli",
    srcs = ["CLI.swift"],
)

Create a single library target that can be used by other targets in your build:

load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")

swift_library(
    name = "MyLibrary",
    srcs = ["MyLibrary.swift"],
    tags = ["manual"],
)

Reference Documentation

Click here for the reference documentation for the rules and other definitions in this repository.

Quick Setup

1. Install Swift

Before getting started, make sure that you have a Swift toolchain installed.

Apple users: Install Xcode. If this is your first time installing it, make sure to open it once after installing so that the command line tools are correctly configured.

Linux users: Follow the instructions on the Swift download page to download and install the appropriate Swift toolchain for your platform. Take care to ensure that you have all of Swift's dependencies installed (such as ICU, Clang, and so forth), and also ensure that the Swift compiler is available on your system path.

2. Configure your workspace

Copy the WORKSPACE snippet from the releases page.

3. Additional configuration (Linux only)

The swift_binary and swift_test rules expect to use clang as the driver for linking, and they query the Bazel C++ API and CROSSTOOL to determine which arguments should be passed to the linker. By default, the C++ toolchain used by Bazel is gcc, so Swift users on Linux need to override this by setting the environment variable CC=clang when invoking Bazel.

This step is not necessary for macOS users because the Xcode toolchain always uses clang.

Building with Custom Toolchains

macOS hosts: You can build with a custom Swift toolchain (downloaded from https://swift.org/download) instead of Xcode's default. To do so, pass the following flag to Bazel:

--action_env=TOOLCHAINS=toolchain.id

Where toolchain.id is the value of the CFBundleIdentifier key in the toolchain's Info.plist file.

To list the available toolchains and their bundle identifiers, you can run:

bazel run @build_bazel_rules_swift//tools/dump_toolchains

Linux hosts: At this time, Bazel uses whichever swift executable is encountered first on your PATH.

Supporting debugging

To make cacheable builds work correctly with debugging see this doc.

Swift Package Manager Support

To download, build, and reference external Swift packages as Bazel targets, check out rules_swift_package_manager.

Supported bazel versions

rules_apple and rules_swift are often affected by changes in bazel itself. This means you generally need to update these rules as you update bazel.

You can also see the supported bazel versions in the notes for each release on the releases page.

Besides these constraint this repo follows semver as best as we can since the 1.0.0 release.

Bazel release Minimum supported rules version Final supported rules version
8.x (most recent rolling) 0.27.0 current
7.x 0.27.0 current
6.x 0.27.0 current
5.x 0.25.0 1.14.0
4.x 0.19.0 0.24.0
3.x 0.14.0 0.18.0

rules_swift's People

Contributors

alexeagle avatar allevato avatar attilathefun avatar balestrapatrick avatar brentleyjones avatar cgrindel avatar chiragramani avatar compnerd avatar davidgoldman avatar dierksen avatar federicoasara avatar github-actions[bot] avatar googlewalt avatar jerrymarino avatar jszumski avatar kastiglione avatar keith avatar lberki avatar luispadron avatar mattrobmattrob avatar maxwelle avatar omarzl avatar philwo avatar segiddins avatar sergiocampama avatar steeve avatar swiple-rules-gardener avatar thii avatar thomasvl avatar tymurmustafaiev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rules_swift's Issues

Determine if protoc build artifacts can be cached by Travis

A significant amount of the time spent running checks on Travis is from compiling protoc from source for use by the swift_proto_library rule. We don't need to build this every time we run our tests—we should only be doing it if it changes (which we have control over, because we lock in a version in our repository rule).

Determine if Travis's caching capabilities would work for us here, and use them if so.

Using swift_c_module in a Swift framework

First off, I'm aware there's a roadmap item for Framework support in rules_apple and that right now it's not a supported use case for these tools. However, I'm striving to use the tools that are available to hack one together regardless.

Creating a pure Swift framework seems to be "some-assembly-required" right now, but is possible. I can take a .swiftmodule and .swiftdoc from the outputs of a swift_library target, put them in the Modules/ folder of a framework, and import it successfully into an independent XCode project.

However, if that swift_library depends on a swift_c_module, things get tricky. If you stay inside Bazel, things are nice and lovely because the '-fmodule-map-file=blah' linker argument gets passed along through the dependency chain, presumably via the SwiftClangModuleInfo provider, and your import MyCLibrary statements just work. However, if you try to use the above strategy to pack up a framework by hand, it'll fail to import in user projects with the error:

UserApplication.swift:10:8: error: missing required module 'MyCLibrary'
import MySwiftLibrary

Some inspection of the .swiftmodule using llvm-bcanalyzer --dump reveals (part of) the problem:

<INPUT_BLOCK NumWords=52 BlockCodeSize=4>
  <SEARCH_PATH abbrevid=9 op0=1 op1=0/> blob data = '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'Foundation'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'Swift'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'SwiftOnoneSupport'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'MyCLibrary'
</INPUT_BLOCK>

Namely, the Swift module declares an import dependency on MyCLibrary, but there's no SEARCH_PATH that will find the module map.

I've tried everything I can think of to declare the existence of MyCLibrary through a module.map or module.modulemap file in the resulting framework but nothing clues the "end-user" XCode project in to look for the module in the same framework.

It feels like there should be a way to incorporate the C-header/module declaration stuff in the module.map into the .swiftmodule the same way the symbols from the depended cc_library target end up incorporated into the .a, but I haven't seen anything that indicates this is possible inside or outside of Bazel.

Is there anything I can do to avoid infecting "end-user" projects with additional build settings that make my framework harder to use and expose implementation details? Or am I just doomed to make my users add an extra import path in their build settings to get this Swift/C interop detail?

Rules are not compatible with Bazel 0.14.x

As the C++ Skylark APIs are currently in flux, some of the checks that we do to access the default linking options for the C++ toolchain that the Swift toolchain depends on are fragile.

In particular, the if statement here doesn't pass in 0.13.0, but it does pass in 0.14.0 because the link_options_do_not_use method we're using until the new C++ APIs are available now exists. But, cc_common.mostly_static_linking_options doesn't, so it still fails.

Support macOS CLIs with Xcode 10.2

As of Xcode 10.2 beta 4, the swift_static directory inside Xcode has disappeared. When you attempt to use --static-swift-stdlib with Swift Package Manager this seems to be a very intentional change:

% swift build --static-swift-stdlib
warning: Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by default starting with macOS Mojave 10.14.4 beta 3. For macOS Mojave 10.14.3 and earlier, there's an optional Swift library package that can be downloaded from "More Downloads" for Apple Developers at https://developer.apple.com/download/more/

In order to continue supporting older OS versions with CLIs, we'll need to add the rpath to the Swift libraries inside Xcode (or the system location for 10.14.4 and up). This change will also affect rules_apple which uses the static functionality for macOS CLIs and dylibs.

Fail more gracefully if `swiftc` is not found

If swiftc is not found on the system path, the build will fail with the following terrible diagnostic:

ERROR: /<REDACTED>/examples/xplatform/hello_world/BUILD:5:1: every rule of type swift_binary implicitly depends upon the target '@build_bazel_rules_swift_local_config//:toolchain', but this target could not be found because of: no such package '@build_bazel_rules_swift_local_config//': Traceback (most recent call last):
	File "/<REDACTED>/external/build_bazel_rules_swift/swift/repositories.bzl", line 75
		_create_linux_toolchain(repository_ctx)
	File "/<REDACTED>/external/build_bazel_rules_swift/swift/repositories.bzl", line 24, in _create_linux_toolchain
		path_to_swiftc.dirname
object of type 'NoneType' has no field 'dirname'

We should print something more useful for the user.

swift_binary doesn't respect some linking-related options of objc_library

If a swift_binary depends (directly or transitively) on an objc_library, and if that objc_library uses certain linking-related attributes like sdk_frameworks, weak_sdk_frameworks, or dylibs (and possibly others), then swift_binary ignores those and doesn't pass them to the linker. (Likewise for swift_test.)

The linking logic for these rules should inspect the relevant keys in Objc provider to make sure that they get passed, as apple_binary does.

Support -incremental mode

We may be able to do this with a Bazel persistent worker, which would let us non-hermetically collect the outputs of a library's previous compilations and feed them back in as inputs to future compilations, so that the driver can detect whether they have changed.

However, this is more complicated than simply flipping on a flag—we'll need to design and build a local server that is used as the worker process.

Get the path to clang explicitly instead of deriving it from the C++ toolchain

Bazel has some logic in it to determine which C++ toolchain the user wants to use based on the CC environment variable and friends. We currently have a hack in our linking logic to replace that value with "clang" (with the intention of loading it from the PATH) if it's something that isn't clang (like gcc).

This is fragile, and even though on my local dev machine it didn't cause any problems, this was preventing the link action from finding clang on Travis.

A better way to handle this would be to have the local config repository rule simply ask which clang and pass that executable into the toolchain. Then we'll always have the correct absolute path (and we can fail early if clang is unavailable).

Update Travis to Xcode 10.x

We need to do this, but the last time someone tried we got these mysterious errors that I haven't been able to duplicate locally:

#63

Improve interoperability with C libraries

Right now, users have to write an explicit swift_c_module target to have Swift code depend on C code in a cc_library.

We might be able to just use an aspect to generate a module map from the library's public headers, using the same (but awful) auto-mangled module name that we use for swift_library targets based on their path in the workspace. Then Swift code would just add a dependency directly to the cc_library and import it.

Add proper unit tests

Right now the only we have to test the rules is to build the contents of the examples directory. Our experience from rules_apple is that the integration shell tests are both slow and flaky, so we'd like to avoid using that approach (except, perhaps, in some isolated cases).

Instead, lets use the action-based analysis-time testing APIs in Skylark to verify the behavior of the rules without actually executing the actions. This should be significantly faster.

Foo.modulemaps/module.map deleted during build

When building our app with whole module optimization for all modules, we're seeing this error intermittently:

<unknown>:0: error: cannot open file '/private/var/tmp/.../__main__/bazel-out/darwin-fastbuild/bin/Modules/Foo/Foo.modulemaps/module.modulemap': No such file or directory

Using watchman we were able to see that this file was created during the build, and then deleted, assumably before it was attempted to be accessed by the dependent modules.

Our modules seem to be correctly defined as dependencies of others, but we'll still see this in the case where Foo depends on Bar, Bar's module.modulemap will be gone when building Foo.

We haven't been able to reproduce this when we pass -j 1

swift_c_module "does not have mandatory providers: 'cc'" with bazel 0.22

With the bazel 0.22rc2 pre-release (from here), with this WORKSPACE:

http_archive(
    name = "StringsGenPathKit",
    build_file = "//bazel/third_party:PathKit.BUILD",
    sha256 = "463c5a14204991e9b82c397f426be79756d8c305555e11c572e88ec2e2ab374b",
    strip_prefix = "PathKit-0.9.1",
    url = "https://github.com/kylef/PathKit/archive/0.9.1.tar.gz",
)

http_archive(
    name = "StringsGenYams",
    build_file = "//bazel/third_party:Yams.BUILD",
    patch_cmds = [
        """
echo '
module CYaml {
    umbrella header "CYaml.h"
    export *
}
' > Sources/CYaml/include/Yams.modulemap
    """,
    ],
    sha256 = "c58ed6520b35a83b3998f076b61134c5456d9349006ec1300744db903b3086f4",
    strip_prefix = "Yams-0.7.0",
    url = "https://github.com/jpsim/Yams/archive/0.7.0.tar.gz",
)

And this in the BUILD file:

load(
    "@build_bazel_rules_swift//swift:swift.bzl",
    "swift_c_module",
    "swift_library",
)

cc_library(
    name = "CYamlLib",
    srcs = glob([
        "Sources/CYaml/src/*.c",
        "Sources/CYaml/src/*.h",
    ]),
    hdrs = glob([
        "Sources/CYaml/include/*.h",
    ]),
    includes = ["Sources/CYaml/include"],
    linkstatic = True,
)

swift_c_module(
    name = "CYaml",
    module_map = "Sources/CYaml/include/Yams.modulemap",
    deps = [":CYamlLib"],
)

swift_library(
    name = "Yams",
    srcs = glob(["Sources/Yams/*.swift"]),
    cc_libs = [":CYamlLib"],
    copts = ["-whole-module-optimization"],
    defines = ["SWIFT_PACKAGE"],
    visibility = ["//visibility:public"],
    deps = [":CYaml"],
)

I end up with this build error:

ERROR: /private/var/tmp/_bazel_ksmiley/c8513c989333be3c89713e16206869b2/external/StringsGenYams/BUILD.bazel:23:12: in deps attribute of swift_c_module rule @StringsGenYams//:CYaml: '@StringsGenYams//:CYamlLib' does not have mandatory providers: 'cc'
ERROR: Analysis of target '//Modules/PassengerCore:PassengerCore' failed; build aborted: Analysis of target '@StringsGenYams//:CYaml' failed; build aborted

I'm not sure what change in bazel has caused this yet

Running swift_test on macOS

Currently if you try to run the swift_test example on macOS like this:

bazel test //examples/xplatform/xctest:xctest

You get this error:

ERROR: /Users/ksmiley/dev/rules_swift/examples/xplatform/xctest/BUILD:5:1: SwiftLinkExecutable examples/xplatform/xctest/xctest failed (Exit 1)
ld: warning: directory not found for option '-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/iphonesimulator'
ld: warning: Auto-Linking library not found for -lswiftMetal
ld: warning: Auto-Linking library not found for -lswiftCoreImage
ld: warning: Auto-Linking library not found for -lswiftDispatch
...
many missing symbols errors

This is because bazel is defaulting to the iPhoneSimulator platform instead of macOS. It seems like this is because the platform_type is never being propagated since we don't have a platform specific rule here.

You can work around this using this undocumented option: https://github.com/bazelbuild/bazel/blob/b853e930228e4e8039c8df2bfac1e55de00230b0/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java#L194-L204 like this:

bazel test //examples/xplatform/xctest:xctest --apple_platform_type macos

When you run this it appears everything works correctly:

INFO: Build options have changed, discarding analysis cache.
INFO: Analysed target //examples/xplatform/xctest:xctest (0 packages loaded).
INFO: Found 1 test target...
Target //examples/xplatform/xctest:xctest up-to-date:
  bazel-bin/examples/xplatform/xctest/xctest
INFO: Elapsed time: 2.561s, Critical Path: 2.27s
INFO: 3 processes: 3 darwin-sandbox.
INFO: Build completed successfully, 4 total actions
//examples/xplatform/xctest:xctest                                       PASSED in 0.2s

Executed 1 out of 1 test: 1 test passes.
INFO: Build completed successfully, 4 total actions

But upon further investigation:

% cat bazel-out/darwin-fastbuild/testlogs/examples/xplatform/xctest/xctest/test.xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite name="examples/xplatform/xctest/xctest" tests="1" failures="0" errors="0">
    <testcase name="examples/xplatform/xctest/xctest" status="run" duration="0" time="0"></testcase>
    <system-out><![CDATA[]]></system-out>
  </testsuite>
</testsuites>

You can see that no tests are actually being run. You can verify this by changing this line

to XCTAssertEqual(value, 3), and you'll see the tests still pass.

I assume this rule is supposed to work on macOS as well as Linux for swift tests, so it looks like the test discovery part of this needs updating.

swift_library cannot see c headers

I'm trying to build the SwiftGRPC library but I'm running into the following problem where the build fails because bazel cannot find the identifiers in the c header file. Here's my configs below:

WORKSPACE

http_archive(
    name = "SwiftProtobuf",
    url = "https://github.com/apple/swift-protobuf/archive/1.4.0.zip",
    strip_prefix = "swift-protobuf-1.4.0",
    build_file = "SwiftProtobuf/BUILD.bazel")

http_archive(
    name = "SwiftGRPC",
    url = "https://github.com/grpc/grpc-swift/archive/0.8.0.zip",
    strip_prefix = "grpc-swift-0.8.0",
    build_file = "SwiftGRPC/BUILD.bazel")

SwiftProtobuf/BUILD.bazel

load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")

swift_library(
  name = "SwiftProtobuf",
  module_name = "SwiftProtobuf",
  srcs = glob(["Sources/SwiftProtobuf/**/*.swift"]),
  visibility = ["//visibility:public"],
)

SwiftGRPC/BUILD.bazel

load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_c_module")

cc_library(
  name = "CgRPCLib",
  srcs = glob([
    "Sources/CgRPC/**/*.[ch]",
    ]),
  hdrs = ["Sources/CgRPC/shim/cgrpc.h"],
)

swift_c_module(
  name = "CgRPC",
  deps = [":CgRPCLib"],
  module_map = "Sources/CgRPC/include/module.modulemap",
)

swift_library(
  name = "SwiftGRPC",
  deps = [
    "@SwiftProtobuf//:SwiftProtobuf",
    ":CgRPC",
  ],
  srcs = glob(["Sources/SwiftGRPC/**/*.swift"]),
)

Actually run the tests in a swift_test target on Darwin

Running bazel test on a swift_test target simply launches the executable. On Linux, this is fine—XCTMain is invoked and the tests run. On macOS, this doesn't actually run anything; the main program is empty, so the executable always succeeds. On macOS, we need to wrap the binary in a script that invokes the xctest helper with the binary instead.

Note that most users of the Swift rules on Apple platforms should prefer to write their tests using the {ios,macos,tvos}_unit_test rules from rules_apple, but swift_test should still be made to work correctly for basic tests meant to run cross-platform on Linux and macOS.

SwiftProtobuf does not generate .pb.swift for pure extension protos, causing build to fail

SwiftProtobuf does not generate any file when passed with a proto file that defines only extensions. While this is well and good, the issue rises when the proto rule declares it, and thus because the file is not generated by the protoc plugin, fails because the .pb.swift is not created.

One such file is https://github.com/gogo/protobuf/blob/master/gogoproto/gogo.proto.

Perhaps the simplest way would be to wrap the plugin and make it generate a file if none was created?

Compiling Swift module failed (Segmentation fault): bazel_xcode_wrapper failed when building against Xcode 10

Bazel building a swift_library that contains a class that's exposed to ObjC fails from the command line when building against Xcode 10 (or 10.1) with the following error:

"Compiling Swift module failed (Segmentation fault): bazel_xcode_wrapper failed: error executing command."

Don't see this issue when building/testing against Xcode 9* on the command line or via the Xcode 10* UI.

Bazel commands and targets used to repro the issue:
bazel build PromiseTestHelpers
https://github.com/google/promises/blob/master/BUILD#L35

bazel test Tests
https://github.com/google/promises/blob/master/BUILD#L85

Swift class that's exposed to ObjC:
https://github.com/google/promises/blob/master/Sources/PromisesTestHelpers/PromisesTestInteroperability.swift#L19

Travis-CI build failure (with full error log):
https://travis-ci.org/google/promises/builds/474583180?utm_source=github_status&utm_medium=notification

Support alternate toolchains

Once we migrate to the proper toolchains API (#3), we should have a way to let the user easily build using a different toolchain than the default for their platform. For example, using a development snapshot or a toolchain locally built from source.

Depending on a framework in the root of the WORKSPACE fails

If you have a pre-compiled framework vended by apple_static_framework_import in the root of your WORKSPACE, if you depend on it from a swift_library rule you will get a strange build failure:

<unknown>:0: error: unknown argument: '-disable-autolink-framework'

because rules_swift ends up passing -F without a path since paths.dirname resolves to an empty string on a single component:

-Ibazel-out/ios-x86_64-min10.0-applebin_ios-ios_x86_64-dbg/bin
-F
-Xfrontend
-disable-autolink-framework
-Xfrontend
RxSwift

I'm not sure this is worth solving but if so we need to change

map_each = paths.dirname,
to a function that returns paths.dirname or ..

Support static frameworks with swift_import

Currently swift_import supports vendoring static .a files to be imported by other modules. We currently vendor our 3rd party dependencies in static frameworks, which are created with this layout:

Foo.framework
├── Foo
├── Foo.bundle
│   └── Images.xcassets
└── Modules
    └── Foo.swiftmodule
        ├── arm.swiftdoc
        ├── arm.swiftmodule
        ├── arm64.swiftdoc
        ├── arm64.swiftmodule
        ├── i386.swiftdoc
        ├── i386.swiftmodule
        ├── x86_64.swiftdoc
        └── x86_64.swiftmodule

This allows us to move around this single directory, which encapsulates all requirements to include this library.

The objc_import rule includes the resources part of this, but also not the framework binary part.

Currently if you try and reference this binary, you receive this error:

ERROR: /path:10:14: in archives attribute of swift_import rule //Foo:Foo: source file '//Foo:Foo' is misplaced here (expected a)

Would it be reasonable to support resources and framework binaries with swift_import? Otherwise is there a better way to include this library layout?

Runtime error on iOS 12.2

I'm getting the following error in the console after updating my device to 12.2 (or using the 12.2 simulator.) Do I need to change my build files? It was working fine on 12.1

This copy of libswiftCore.dylib requires an OS version prior to 12.2.0.
2019-04-09 09:25:11.677514-0700[61813:6155466] This copy of libswiftCore.dylib requires an OS version prior to 12.2.0.

Support fully-static linking

Right now, we only support kind-of-static linking: the swift_library targets that the binary depends on are statically linked into the final binary, but the Swift runtime libraries are dynamically linked. This is fine (and in fact, desired) on Apple's mobile platforms (iOS, tvOS, watchOS), but on Linux (and probably macOS), we should offer static linking against the runtime as well.

One interesting catch: Linux toolchains do not come with a static version of libdispatch, which would seem to mean you can't do a whole lot of interesting stuff in a statically linked environment. So this might reduce the usefulness and urgency of this feature.

swift_import not linked with ios_unit_test

Currently if you define a swift_import rule such as:

swift_import(
  name = "Foo",
  archives = [
    ":libFoo.a",
  ],
  swiftmodules = [
    ":Foo.swiftmodule",
  ],
)

And then you include that from a swift_library with another swift_library for tests:

swift_library(
  name = "Bar",
  srcs = glob(["*.swift"]),
  deps = [":Foo"],
)

swift_library(
  name = "BarTestsBinary",
  srcs = glob(["Tests/*.swift"]),
  deps = [":Bar"],
)

And finally you try to create a ios_unit_test bundle for this:

ios_unit_test(
  name = "BarTests",
  deps = [":BarTestsBinary"],
)

When you run bazel test //:BarTests you end up getting a linker error because the symbols from Foo are not defined. If you re-run this with --linkopt -v, you can see the ld invocation doesn't contain -Lpath/to/Foo or -lFoo. I noticed here that it seems like we might want to be propagating those flags here, but after making that change locally, it still doesn't appear that ios_unit_test is reading these.

Is this the correct configuration or should I be using different rules for this?

Figure out if CC=clang requirement can be removed

As of Bazel 0.16.0, we're starting to use the Skylark C++ API to interact more with the C++ toolchain (for example, to link binaries). But the default C++ compiler for Bazel is gcc on Linux unless it's overridden by the CC environment variable. Since Swift requires using Clang to link, users add CC=clang to their environment to link Swift binaries, otherwise the C++ toolchain will try to report flags to pass to gcc that Clang doesn't support.

It would be nice if we could remove this somehow, but that would require some other way of telling Bazel to use Clang instead of gcc by default.

Add support for response files

Swift 4.2 should contain swiftlang/swift#16362 (and a previous companion PR) that will allow the Swift driver to accept response files when the command line gets too long. We will need to update the swift_toolchain and xcode_swift_toolchain rules to propagate that knowledge based on Xcode/Swift compiler version so that Args objects can take advantage of it.

Propagate Transitive Swift Sources in ObjcProvider

ObjcProvider has a source field that propagates transitive source files.
Here: https://docs.bazel.build/versions/master/skylark/lib/ObjcProvider.html#source

In swift_library it is not set and I couldn't find transitive source files anywhere else either. Having transitive source files is a bit counterintuitive but we need it at our codegen script happens in swift_binary.

So what do you think of setting ObjcProvider.source field for swift_library as well? It should be a small change.

If this doesn't suite the project, I think we can also find easy workarounds without updating rules_swift.

README.md has broken link to docs

The link for attributes docs for the rules is broken. It's currently https://github.com/bazelbuild/rules_swift/tree/master/doc/index.md, I'm not sure what it should be.

Configure CI

Set up CI to build all the examples on both Linux and Apple platforms. Once we have proper unit tests (#1), run those as well.

How to use bridging headers?

I have a project that uses [project]-Bridging-Header.h in XCode to import some headers. Those headers seem to become implicitly available to all Swift source files. Therefore most of them miss imports like import Foundation, import UIKit.

The good thing to do would probably be to add all imports, but I want to try and compile the project as is, without changes, is that possible?

How can one use bridging headers?

-pie linker argument on Linux

Currently rules_swift doesn't pass -pie when linking swift_binarys and swift_tests on Linux. This results in difficult to debug run time crashes in some cases. For example using PathKit with this BUILD file:

load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")

swift_library(
    name = "PathKit",
    srcs = ["Sources/PathKit.swift"],
    visibility = ["//visibility:public"],
)

when you try to compile another swift_library that depends on it with this code:

import PathKit
print(Path("/"))

It crashes at runtime with a stack trace similar to this:

(lldb) bt
* thread #1, name = 'PackageTes', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
  * frame #0: 0x0000000000000000
    frame #1: 0x00007ffff7a81a84 libswiftCore.so`Swift._print_unlocked<A, B where B: Swift.TextOutputStream>(A, inout B) -> () + 804
    frame #2: 0x00007ffff7c2895b libswiftCore.so`merged function signature specialization <Arg[1] = Exploded, Arg[2] = Exploded> of generic specialization <Swift._Stdout> of Swift._print<A where A: Swift.TextOutputStream>(_: Swift.Array<Any>, separator: Swift.String, terminator: Swift.String, to: inout A) -> () + 251
    frame #3: 0x00007ffff7c29235 libswiftCore.so`merged function signature specialization <Arg[1] = Exploded, Arg[2] = Exploded> of Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () + 261
    frame #4: 0x00007ffff7b2b7c0 libswiftCore.so`Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () + 16
    frame #5: 0x0000000000406c48 PackageTests`main at LinuxMain.swift:4
    frame #6: 0x00007ffff5ceeb97 libc.so.6`__libc_start_main + 231
    frame #7: 0x000000000040689a PackageTests`_start + 42

By using --linkopt=-pie, this works as expected. Is there a reason this isn't a default value?

Explore Swift Package Manager integration

It would be awesome to be able to painlessly import existing SwiftPM packages without requiring the user to hand-write BUILD files corresponding to those targets.

Let's investigate how we could write a repository rule that does this. Such a rule could clone the repositories the user is interested in, then either:

  1. Build them using SwiftPM and serve up the build artifacts as if they were swift_library targets
  2. Generate actual BUILD targets that correspond to the SwiftPM targets and build them using our rules.

Each of these approaches has risks: building using SwiftPM means that the user has less control over how the targets get compiled, and we have to make sure that the build artifacts are available in a way that we can use them. Building with generated BUILD targets using our rules is complex because it requires parsing the manifests (or, more likely, grabbing the JSON dump of them) and reconstructing appropriate targets that work identically to the SwiftPM ones.

But, if we can make this work, it significantly lowers the barrier of entry for someone using Bazel to adopt our Swift rules while still benefiting from third-party libraries out there.

swift_library resources aren't propagated to ObjC rules

Currently if you have a configuration like this:

objc_bundle_library(
    name = "FooResources",
    asset_catalogs = glob([
      "Resources/Foo.xcassets/**",
    ])
)

swift_library(
  name = "Foo",
  srcs = glob(["Sources/*.swift"]),
  resources = [
    ":FooResources",
  ],
)

swift_library(
  name = "FooTestsBinary",
  srcs = glob(["Tests/*.swift"]),
  deps = [":Foo"],
)

ios_unit_test(
  name = "FooTests",
  deps = [":FooTestsBinary"],
)

The final .xctest bundle produced does not contain FooResources.bundle. It looks like this is caused by no post processing being done in the swift_library rule to propagated providers including these resources. It seems like some of this logic might have gotten lost since bazelbuild/rules_apple#40

incompatible_depset_is_not_iterable

Tests fail with using the Bazel flag --incompatible_depset_is_not_iterable: https://buildkite.com/bazel/bazelisk-plus-incompatible-flags/builds/88#b668c661-dea6-44d2-888b-71014aebbad6

File "/private/var/tmp/_bazel_buildkite/04c8fd297745e703ba30ca5e4f6bfcd4/external/build_bazel_rules_swift/swift/internal/linking.bzl", line 96, in depset
--
  | [objc_provider_framework_name(fdir) for fdir in objc.dynamic_framework_dir]
  | type 'depset' is not iterable. Use the `to_list()` method to get a list. Use --incompatible_depset_is_not_iterable=false to temporarily disable this check.

Can you take a look?

Test fixtures on Linux

Currently if you have a simple Swift package like this:

Package.swift
Tests
├── Fixtures
│   └── foo.json
├── SwiftExampleTests
│   └── FooTests.swift
└── main.swift

And you have a test that loads your test fixture, you'd likely do something like this:

private let fixturePath = URL(fileURLWithPath: #file)
    .deletingLastPathComponent()
    .deletingLastPathComponent()
    .appendingPathComponent("Fixtures/foo.json")

final class FooTests: XCTestCase {
    func testBar() throws {
        let data = try Data(contentsOf: fixturePath)
        let json = try JSONSerialization.jsonObject(with: data) as? [String: String]
        XCTAssertEqual(json, ["foo": "bar"])
    }
}

In bazel if you wanted to do this on macOS (because of this issue), you might use the macos_unit_test rule, which would pull in structured_resources from a Swift library target. Meaning you could do something like this:

swift_library(
  name = "SwiftExampleTestsBinary",
  srcs = ["Tests/SwiftExampleTests/FooTests.swift", "Tests/main.swift"],
  structured_resources = glob(["Tests/Fixtures/**"]),
)

And change your fixture loading code accordingly, since now the resources would live in SwiftExampleTests.xctest/Contents/Resources/Tests/Fixtures/ at runtime.

On Linux, since the swift_test rule ignores the structured_resources attributes of its dependencies, there doesn't seem to be a way to load your tests.

Is there a recommended way of loading fixtures on Linux? Otherwise is there a way to access the original source paths so you could achieve something like this?

Here's an example project which passes with swift test on macOS and Linux, and fails with bazel on both SwiftExample.zip

File paths with spaces aren't handled by params files

After this commit to use params files for swiftc, our builds started failing since we have source files with spaces in the name.

The core issue here is unrelated to bazel (and filed here) assuming that this should be supported, but otherwise (and in the meantime) we might want to fix this in bazel to quote or escape the file paths in this file.

Enable WMO by default

Considering rules_swift doesn't support incremental compilation (related issue: bazelbuild/rules_apple#21) it seems like we might be best off enabling WMO by default. It looks like this solution still works, and it's simple to add this to your .bazelrc file, but as far as I know there shouldn't be any downsides if we're already not getting the benefits of compiling incrementally.

Thoughts?

Expected layout for swiftdocs

Currently swift_import there are swiftmodules and swiftdocs attribute. If you're using this rule to provide a multi-architecture library, Xcode encourages this layout:

Foo.swiftmodule
├── arm.swiftdoc
├── arm.swiftmodule
├── arm64.swiftdoc
├── arm64.swiftmodule
├── i386.swiftdoc
├── i386.swiftmodule
├── x86_64.swiftdoc
└── x86_64.swiftmodule

Since the naming of these files affects the ability to import this target, I was previously using:

swift_import(
  name = "Foo",
  archives = ["libFoo.a"],
  swiftmodules = ["Foo.swiftmodule"],
)

Now that this rule expects you to pass docs separately (which I guess wasn't working before, although that is what I was expecting at the time) what is the bazel recommended layout for this?

incompatible_no_support_tools_in_action_inputs

Based on Bazel CI (https://buildkite.com/bazel/bazelisk-plus-incompatible-flags/builds/79#5dc907bb-5efb-4038-8842-db9dcaef2141), the code doesn't work with the Bazel flag incompatible_no_support_tools_in_action_inputs.

Can you take a look?

File "/home/bazel/.cache/bazel/_bazel_bazel/bc7fc2decf10979b84156288d05dd932/external/build_bazel_rules_swift/swift/internal/swift_grpc_library.bzl", line 120, in _register_grpcswift_generate_action
--
  | actions.run(arguments = [mkdir_args, protoc_...], <5 more arguments>)
  | Found tool(s) 'bazel-out/host/bin/external/build_bazel_rules_swift/tools/mkdir_and_run/mkdir_and_run', 'bazel-out/host/bin/external/com_google_protobuf/protoc', 'bazel-out/host/bin/external/com_github_grpc_grpc_swift/protoc-gen-swiftgrpc' in inputs. A tool is an input with executable=True set. All tools should be passed using the 'tools' argument instead of 'inputs' in order to make their runfiles available to the action. This safety check will not be performed once the action is modified to take a 'tools' argument. To temporarily disable this check, set --incompatible_no_support_tools_in_action_inputs=false.

swift_c_module nested cc_library not propagated to other rules

If you try to build a repo like this one that has this source layout:

Sources
├── CYaml
│   ├── include
│   │   ├── CYaml.h
│   │   └── yaml.h
│   └── src
│       ├── api.c
│       ├── emitter.c
│       ├── parser.c
│       ├── reader.c
│       ├── scanner.c
│       ├── writer.c
│       └── yaml_private.h
└── Yams
    ├── Constructor.swift
    ├── Decoder.swift
    ├── Emitter.swift
    ├── Encoder.swift
    ├── Mark.swift
    ├── Node.Mapping.swift
    ├── Node.Scalar.swift
    ├── Node.Sequence.swift
    ├── Node.swift
    ├── Parser.swift
    ├── Representer.swift
    ├── Resolver.swift
    ├── String+Yams.swift
    ├── Tag.swift
    ├── YamlError.swift
    ├── Yams.h
    └── shim.swift

And a config like this:

load(
    "@build_bazel_rules_apple//apple:macos.bzl",
    "macos_unit_test",
)

load(
    "@build_bazel_rules_swift//swift:swift.bzl",
    "swift_c_module",
    "swift_library",
    "swift_test",
)

cc_library(
  name = "CYamlLib",
  srcs = glob([
    "Sources/CYaml/src/*.c",
    "Sources/CYaml/src/*.h",
  ]),
  hdrs = glob([
    "Sources/CYaml/include/*.h",
  ]),
  includes = ["Sources/CYaml/include"],
  linkstatic = True,
)

swift_c_module(
  name = "CYaml",
  deps = [":CYamlLib"],
  module_map = "Sources/CYaml/include/foo.modulemap",
)

swift_library(
  name = "Yams",
  deps = [":CYaml"],
  srcs = glob(["Sources/Yams/*.swift"]),
  defines = ["SWIFT_PACKAGE"],
  module_name = "Yams",
)

swift_library(
  name = "YamsTestsBinary",
  srcs = glob(["Tests/YamsTests/*.swift"]),
  deps = [":Yams", ":CYaml"],
)

macos_unit_test(
  name = "YamsTests",
  deps = [":YamsTestsBinary"],
)

The foo.modulemap with this contents:

module CYaml {
    umbrella header "CYaml.h"
    export *
}

When you try to build the tests with:

bazel build //:YamsTests

You end up with some undefined symbol errors. This is because the YamsTests target doesn't have access to the underlying CYamlLib target. If you add that as a direct dependency of the YamsTests target like this:

  deps = [":YamsTestsBinary", ":CYamlLib"],

It links correctly. Would it reasonable to propagate these dependencies so that these other apple rules could access them?

(note if you actually run these tests, the fixtures are not included in this build file so they will not actually pass)

Support building shared libraries (.dylib/.so)

We should provide a way to build a .dylib/.so from Swift code instead of just an executable binary. cc_binary does this via the linkshared attribute (and requiring a specific name for the target). Investigate whether the same technique for swift_binary makes sense, or if we should use another approach.

Imported well known types fails to compile

When importing well know types from google protobuf the compilation fails.
It looks like the well know types files are not filtered out as they should be by:

def _filter_out_well_known_types(srcs):

because the path is prefixed by ../com_google_protobuf/ (see debug output):

bazel build -s --verbose_failures //:someproto_swift_proto
DEBUG: /private/var/tmp/_bazel_aamand/c0c24d6962ccc7935d8d8f222862d21c/external/build_bazel_rules_swift/swift/internal/swift_protoc_gen_aspect.bzl:51:3: WNT ["../com_google_protobuf/google/protobuf/duration.proto"]
DEBUG: /private/var/tmp/_bazel_aamand/c0c24d6962ccc7935d8d8f222862d21c/external/build_bazel_rules_swift/swift/internal/swift_protoc_gen_aspect.bzl:51:3: WNT ["some.proto"]
INFO: Analysed target //:someproto_swift_proto (2 packages loaded).
INFO: Found 1 target...
SUBCOMMAND: # @com_google_protobuf//:duration_proto [action 'Generating Swift sources for @com_google_protobuf//:duration_proto']
(cd /private/var/tmp/_bazel_aamand/c0c24d6962ccc7935d8d8f222862d21c/execroot/bazelswiftproto && \
  exec env - \
  bazel-out/host/bin/external/build_bazel_rules_swift/tools/mkdir_and_run/mkdir_and_run bazel-out/darwin-fastbuild/bin bazel-out/host/bin/external/com_google_protobuf/protoc '--plugin=protoc-gen-swift=bazel-out/host/bin/external/com_github_apple_swift_swift_protobuf/ProtoCompilerPlugin' '--swift_out=bazel-out/darwin-fastbuild/bin' '--swift_opt=FileNaming=FullPath' '--swift_opt=Visibility=Public' '--swift_opt=ProtoPathModuleMappings=bazel-out/darwin-fastbuild/bin/external/com_google_protobuf/duration_proto.protoc_gen_swift_modules.asciipb' --descriptor_set_in bazel-out/darwin-fastbuild/genfiles/external/com_google_protobuf/duration_proto-descriptor-set.proto.bin bazel-out/darwin-fastbuild/genfiles/external/com_google_protobuf/google/protobuf/duration.proto)
ERROR: /private/var/tmp/_bazel_aamand/c0c24d6962ccc7935d8d8f222862d21c/external/com_google_protobuf/BUILD:245:2: Generating Swift sources for @com_google_protobuf//:duration_proto failed (Exit 1): mkdir_and_run failed: error executing command
  (cd /private/var/tmp/_bazel_aamand/c0c24d6962ccc7935d8d8f222862d21c/execroot/bazelswiftproto && \
  exec env - \
  bazel-out/host/bin/external/build_bazel_rules_swift/tools/mkdir_and_run/mkdir_and_run bazel-out/darwin-fastbuild/bin bazel-out/host/bin/external/com_google_protobuf/protoc '--plugin=protoc-gen-swift=bazel-out/host/bin/external/com_github_apple_swift_swift_protobuf/ProtoCompilerPlugin' '--swift_out=bazel-out/darwin-fastbuild/bin' '--swift_opt=FileNaming=FullPath' '--swift_opt=Visibility=Public' '--swift_opt=ProtoPathModuleMappings=bazel-out/darwin-fastbuild/bin/external/com_google_protobuf/duration_proto.protoc_gen_swift_modules.asciipb' --descriptor_set_in bazel-out/darwin-fastbuild/genfiles/external/com_google_protobuf/duration_proto-descriptor-set.proto.bin bazel-out/darwin-fastbuild/genfiles/external/com_google_protobuf/google/protobuf/duration.proto)

Support -enable-batch-mode in Swift 4.2

Batch mode compilation was added to Swift 4.2, which provides speed-ups for non-incremental builds that are closer to the performance that people see when they use whole-module optimization.

Unlike WMO, batch mode is something that makes sense to turn on unconditionally by default, so we should detect if a toolchain uses Swift 4.2 and, if so, add it to the command line.

Auto-download Linux toolchains when possible

The repository rule on Linux should detect if the Swift toolchain is not already on the path; if that's the case, and if the user is running one of the Ubuntu versions for which swift.org hosts prebuilt toolchains, the rule should download that repository and use the toolchain from there.

Although this doesn't prevent the user from having to install clang, ICU, and some other components externally, it still improves ramp-up time, and makes it easy to switch between toolchains.

Add support for `-debug-prefix-map`

Once swiftlang/swift#17665 makes it into a Swift release, we need to support the flag by having invocations of swiftc remap the Bazel execroot to ".". Then, users who spawn lldb at the root of their workspace will have their source files found as expected.

cc_library macOS target verison

Currently with this BUILD file #52 the underlying cc_library CYamlLib is built with macOS version of the current machine, even when --macos_minimum_os=10.13 is passed. This flag ends up correctly affecting the final swift_binary and then produces a warning because the it's linking a library that was built for an old OS version.

I'm not sure how this minimum version interacts with cc_library, but seeing as this is built for macOS and Linux what is the recommendation for how to pass the correct target?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.