From bfae4cd1ea728aacdb6633eef5927e66501477be Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 12 Mar 2021 09:39:48 -0800 Subject: [PATCH] Add remotely debuggable Swift document (#576) --- README.md | 5 ++ doc/debuggable_remote_swift.md | 112 +++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 doc/debuggable_remote_swift.md diff --git a/README.md b/README.md index 4baf51954..c697a7e23 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/doc/debuggable_remote_swift.md b/doc/debuggable_remote_swift.md new file mode 100644 index 000000000..24e6d4ccb --- /dev/null +++ b/doc/debuggable_remote_swift.md @@ -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.