Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remotely debuggable Swift document #576

Merged
merged 2 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ 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 remote builds

To make remote builds work correctly with debugging and general
reproducibility see [this doc](doc/debuggable_remote_swift.md)

## Future Work

- Support for building and linking to shared libraries (`.dylib`/`.so`) written
Expand Down
112 changes: 112 additions & 0 deletions doc/debuggable_remote_swift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Debuggable Remotely Built Swift

This is a guide to using remotely built Swift modules in local debug builds.

At the time of writing, `lldb` depends on debugging options embedded in `.swiftmodule` files. These options include paths that are only valid on the build host. For local builds, this all just works, but for remote builds, it doesn't.

The solution is two parts:

1. Use `-no-serialize-debugging-options` globally, to prevent embedded paths
2. Use `-serialize-debugging-options` locally in one empty module

Globally disabling debugging options makes those `.swiftmodule`s usable on any machine. Locally enabling debugging options for one module provides lldb with enough info to make debugging work.

An lldb bug has been filed here: https://bugs.swift.org/browse/SR-11485

### Disable Debugging Options Globally

To globally disable debugging options, use the `swift.cacheable_swiftmodules` feature in rules_swift. For example, your `.bazelrc` could look like this:

```
build --features=swift.cacheable_swiftmodules
```

What this does is ensure all modules are built explicitly with `-no-serialize-debugging-options`. It has to be explicit because `swiftc` enables `-serialize-debugging-options` in some cases.

### Add Debug Build Config

In a `BUILD` file - in this example, the root `BUILD` file, define the following [`config_setting`](https://docs.bazel.build/versions/master/be/general.html#config_setting). This will allow targets to conditionally depend on the locally built debugging module.

```python
config_setting(
name = "debug",
values = {
"compilation_mode": "dbg",
},
)
```

### Define Local Debug Target

In the `BUILD` file of your choice, define a `swift_library` with:

* A single empty source file
* Enables `-serialize-debugging-options`
* Built locally, not remote - using the `no-remote` tag

Here is one way to define the `BUILD` file, using a [`genrule`](https://docs.bazel.build/versions/master/be/general.html#genrule) to create the empty swift file.

```python
genrule(
name = "empty",
outs = ["empty.swift"],
cmd = "touch $(OUTS)",
)

swift_library(
name = "_LocalDebugOptions",
srcs = [":empty"],
copts = [
"-Xfrontend",
"-serialize-debugging-options",
],
module_name = "_LocalDebugOptions",
tags = ["no-remote"],
visibility = ["//visibility:public"],
)
```

### Update Top Level Test Targets

Finally, for each top-level test target (`ios_unit_test`, `ios_ui_test`*, etc), conditionally add the local debugging module to the deps. This is done via the [debug config](#add-debug-build-config). In the past this also had to be done for `ios_application` targets but that has since been fixed in lldb.

```python
debug_deps = select({
"//:debug": ["//some/path:_LocalDebugOptions"],
"//conditions:default": [],
})

ios_unit_test(
name = "...",
deps = debug_deps + [
# ...
],
# ...
)
```

##### Note about `ios_unit_test`

When using a test host, the debugging module must be added to the test host target only, not the unit test target. _However_, for tests without a test host, the debugging module must be added to the unit test target.

### LLDB Settings

Additional settings may be required, depending on your build setup. For example, an Xcode Run Script may look like:

```
echo "settings set target.sdk-path $SDKROOT"
echo "settings set target.swift-framework-search-paths $FRAMEWORK_SEARCH_PATHS"
```

Other settings you can try customizing are:

* `target.clang-module-search-paths`
* `target.debug-file-search-paths`
* `target.sdk-path`
* `target.swift-extra-clang-flags`
* `target.swift-framework-search-paths`
* `target.swift-module-search-paths`
* `target.use-all-compiler-flags`
* `symbols.clang-modules-cache-path`

These settings would be written to some project specific lldbinit file which you can include directly in Xcode's scheme.