Skip to content

Commit aef4f79

Browse files
committed
Swift: turn extractor into a swift-frontend plugin
This change turns the extractor into a plugin to a slightly modified `swift-extractor` binary, providing only a `swift::FrontendObserver` instance. This makes it so that we inherit the behaviours of the released `swift-frontend`, among the known solved issues are: * work around the issue preventing the Linux extractor from compiling stdlib `.swiftinterface` files, which was preventing the extractor from working on top of earlier versions of the swift SDK * `@`-prefixed parameter files are now supported Under the hood the Swift build system has been tweaked so that calls to `performFrontend` in the driver executable use a `FrontendObserver` built from a dynamic library, and the extractor now provides an alternative frontend observer dynamic library. Some special treatment needed to retain the same features: * a new `finished` method was added to `swift::FrontendObserver` to implement the `file_is_successfully_extracted` predicate * part of the swift AST library (`Identifier`) needed to be extracted into a separate dynamic library as well, as behaviour was depending on the value of static pointers that therefore needed to be shared between `swift-frontend` and the extractor * the `CODEQL_EXTRACTOR_SWIFT_RUN_UNDER` functionality has been moved to the `extractor` bash wrapper, which also makes it work better to catch crashes happening before `main` is even entered (which can happen with tracer problems)
1 parent dec1e4d commit aef4f79

File tree

7 files changed

+57
-103
lines changed

7 files changed

+57
-103
lines changed

swift/extractor/BUILD.bazel

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ load("//misc/bazel/cmake:cmake.bzl", "generate_cmake")
33
load("//misc/bazel:pkg_runfiles.bzl", "pkg_runfiles")
44

55
swift_cc_binary(
6-
name = "extractor.real",
6+
name = "swiftFrontendObserver",
77
srcs = glob([
88
"*.h",
99
"*.cpp",
1010
]),
11+
linkshared = True,
1112
deps = [
1213
"//swift/extractor/config",
1314
"//swift/extractor/infra",
@@ -21,19 +22,27 @@ swift_cc_binary(
2122

2223
generate_cmake(
2324
name = "cmake",
24-
targets = [":extractor.real"],
25+
targets = [":swiftFrontendObserver"],
2526
visibility = ["//visibility:public"],
2627
)
2728

2829
sh_binary(
2930
name = "extractor",
3031
srcs = ["extractor.sh"],
31-
data = [":extractor.real"],
32+
data = [
33+
":swiftFrontendObserver",
34+
"//swift/third_party/swift-llvm-support:swift-frontend",
35+
],
3236
)
3337

3438
pkg_runfiles(
3539
name = "pkg",
3640
srcs = [":extractor"],
3741
excludes = ["extractor.sh"], # script gets copied as "extractor", no need for the original .sh file
42+
renames = select({
43+
# workaround for https://github.com/bazelbuild/bazel/issues/11082 (wrongly marked as closed)
44+
"@platforms//os:macos": {":swiftFrontendObserver": "libswiftFrontendObserver.dylib"},
45+
"//conditions:default": {},
46+
}),
3847
visibility = ["//swift:__pkg__"],
3948
)

swift/extractor/extractor.sh

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
#!/bin/bash
22

3+
EXE_DIR="$(dirname "$0")"
4+
35
if [[ "$(uname)" == Darwin ]]; then
4-
export DYLD_LIBRARY_PATH=$(dirname "$0")
6+
export DYLD_LIBRARY_PATH="$EXE_DIR"
57
else
6-
export LD_LIBRARY_PATH=$(dirname "$0")
8+
export LD_LIBRARY_PATH="$EXE_DIR"
9+
fi
10+
11+
TOOL="$CODEQL_EXTRACTOR_SWIFT_RUN_UNDER"
12+
13+
if [[ -n "$CODEQL_EXTRACTOR_SWIFT_RUN_UNDER_FILTER" ]]; then
14+
if [[ ! "$*" =~ $CODEQL_EXTRACTOR_SWIFT_RUN_UNDER_FILTER ]]; then
15+
TOOL=
16+
fi
717
fi
818

9-
exec -a "$0" "$0.real" "$@"
19+
exec -a swift-frontend $TOOL "$EXE_DIR/swift-frontend" "$@"

swift/extractor/main.cpp

Lines changed: 13 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#include <chrono>
77

88
#include <swift/Basic/LLVMInitialize.h>
9-
#include <swift/FrontendTool/FrontendTool.h>
9+
#include <swift/DriverTool/DriverTool.h>
10+
#include <swift/DriverTool/FrontendObserver.h>
1011
#include <swift/Basic/InitializeSwiftModules.h>
1112

1213
#include "swift/extractor/SwiftExtractor.h"
@@ -92,7 +93,8 @@ class Observer : public swift::FrontendObserver {
9293
codeql::extractExtractLazyDeclarations(state, compiler);
9394
}
9495

95-
void markSuccessfullyExtractedFiles() {
96+
void finished(int status) override {
97+
if (status != 0) return;
9698
codeql::SwiftLocationExtractor locExtractor{invocationTrap};
9799
for (const auto& src : state.sourceFiles) {
98100
auto fileLabel = locExtractor.emitFile(src);
@@ -102,6 +104,8 @@ class Observer : public swift::FrontendObserver {
102104

103105
private:
104106
codeql::SwiftExtractorState state;
107+
std::shared_ptr<codeql::FileInterceptor> openInterception{
108+
codeql::setupFileInterception(state.configuration)};
105109
codeql::TrapDomain invocationTrap{invocationTrapDomain(state)};
106110
codeql::SwiftDiagnosticsConsumer diagConsumer{invocationTrap};
107111
};
@@ -113,49 +117,6 @@ static std::string getenv_or(const char* envvar, const std::string& def) {
113117
return def;
114118
}
115119

116-
static bool checkRunUnderFilter(int argc, char* const* argv) {
117-
auto runUnderFilter = getenv("CODEQL_EXTRACTOR_SWIFT_RUN_UNDER_FILTER");
118-
if (runUnderFilter == nullptr) {
119-
return true;
120-
}
121-
std::string call = argv[0];
122-
for (auto i = 1; i < argc; ++i) {
123-
call += ' ';
124-
call += argv[i];
125-
}
126-
std::regex filter{runUnderFilter, std::regex_constants::basic | std::regex_constants::nosubs};
127-
return std::regex_search(call, filter);
128-
}
129-
130-
// if `CODEQL_EXTRACTOR_SWIFT_RUN_UNDER` env variable is set, and either
131-
// * `CODEQL_EXTRACTOR_SWIFT_RUN_UNDER_FILTER` is not set, or
132-
// * it is set to a regexp matching any substring of the extractor call
133-
// then the running process is substituted with the command (and possibly
134-
// options) stated in `CODEQL_EXTRACTOR_SWIFT_RUN_UNDER`, followed by `argv`.
135-
// Before calling `exec`, `CODEQL_EXTRACTOR_SWIFT_RUN_UNDER` is unset to avoid
136-
// unpleasant loops.
137-
// An example usage is to run the extractor under `gdbserver :1234` when the
138-
// arguments match a given source file.
139-
static void checkWhetherToRunUnderTool(int argc, char* const* argv) {
140-
assert(argc > 0);
141-
142-
auto runUnder = getenv("CODEQL_EXTRACTOR_SWIFT_RUN_UNDER");
143-
if (runUnder == nullptr || !checkRunUnderFilter(argc, argv)) {
144-
return;
145-
}
146-
std::vector<char*> args;
147-
// split RUN_UNDER value by spaces to get args vector
148-
for (auto word = std::strtok(runUnder, " "); word != nullptr; word = std::strtok(nullptr, " ")) {
149-
args.push_back(word);
150-
}
151-
// append process args, including extractor executable path
152-
args.insert(args.end(), argv, argv + argc);
153-
args.push_back(nullptr);
154-
// avoid looping on this function
155-
unsetenv("CODEQL_EXTRACTOR_SWIFT_RUN_UNDER");
156-
execvp(args[0], args.data());
157-
}
158-
159120
// Creates a target file that should store per-invocation info, e.g. compilation args,
160121
// compilations, diagnostics, etc.
161122
codeql::TrapDomain invocationTrapDomain(codeql::SwiftExtractorState& state) {
@@ -170,39 +131,19 @@ codeql::TrapDomain invocationTrapDomain(codeql::SwiftExtractorState& state) {
170131
return std::move(maybeDomain.value());
171132
}
172133

173-
codeql::SwiftExtractorConfiguration configure(int argc, char** argv) {
134+
codeql::SwiftExtractorConfiguration configure(llvm::ArrayRef<const char*> argv) {
174135
codeql::SwiftExtractorConfiguration configuration{};
175136
configuration.trapDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_TRAP_DIR", ".");
176137
configuration.sourceArchiveDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SOURCE_ARCHIVE_DIR", ".");
177138
configuration.scratchDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SCRATCH_DIR", ".");
178-
configuration.frontendOptions.assign(argv + 1, argv + argc);
139+
configuration.frontendOptions.assign(argv.begin() + 1, argv.end());
179140
return configuration;
180141
}
181142

182-
int main(int argc, char** argv) {
183-
checkWhetherToRunUnderTool(argc, argv);
184-
185-
if (argc == 1) {
186-
// TODO: print usage
187-
return 1;
188-
}
189-
190-
// Required by Swift/LLVM
191-
PROGRAM_START(argc, argv);
192-
INITIALIZE_LLVM();
193-
initializeSwiftModules();
194-
195-
const auto configuration = configure(argc, argv);
196-
197-
auto openInterception = codeql::setupFileInterception(configuration);
198-
199-
Observer observer(configuration);
200-
int frontend_rc = swift::performFrontend(configuration.frontendOptions, "swift-extractor",
201-
(void*)main, &observer);
202-
203-
if (frontend_rc == 0) {
204-
observer.markSuccessfullyExtractedFiles();
205-
}
143+
namespace swift {
206144

207-
return frontend_rc;
145+
FrontendObserver* getFrontendObserver(llvm::ArrayRef<const char*> argv) {
146+
static Observer observer{configure(argv)};
147+
return &observer;
208148
}
149+
} // namespace swift
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
| run_under: $CODEQL_EXTRACTOR_SWIFT_ROOT/tools/$CODEQL_PLATFORM/extractor -sdk $CODEQL_EXTRACTOR_SWIFT_ROOT/qltest/$CODEQL_PLATFORM/sdk -c -primary-file filtered_in.swift |
2-
| run_under: $CODEQL_EXTRACTOR_SWIFT_ROOT/tools/$CODEQL_PLATFORM/extractor -sdk $CODEQL_EXTRACTOR_SWIFT_ROOT/qltest/$CODEQL_PLATFORM/sdk -c -primary-file unfiltered.swift |
1+
| run_under: $CODEQL_EXTRACTOR_SWIFT_ROOT/tools/$CODEQL_PLATFORM/swift-frontend -sdk $CODEQL_EXTRACTOR_SWIFT_ROOT/qltest/$CODEQL_PLATFORM/sdk -c -primary-file filtered_in.swift |
2+
| run_under: $CODEQL_EXTRACTOR_SWIFT_ROOT/tools/$CODEQL_PLATFORM/swift-frontend -sdk $CODEQL_EXTRACTOR_SWIFT_ROOT/qltest/$CODEQL_PLATFORM/sdk -c -primary-file unfiltered.swift |

swift/third_party/load.bzl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
22

3-
_swift_prebuilt_version = "swift-5.7.3-RELEASE.142"
3+
_swift_prebuilt_version = "swift-5.7.3-RELEASE.150"
44
_swift_sha_map = {
5-
"Linux-X64": "398d8de54c8775c939dff95ed5bb0e04a9308a1982b4c1900cd4a5d01223f63b",
6-
"macOS-ARM64": "397dd67ea99b9c9455794c6eb0f1664b6179fe542c7c1d3010314a3e8a905ae4",
7-
"macOS-X64": "4b9d8e4e89f16a7c1e7edc7893aa189b37d5b4412be724a86ef59c49d11a6f75",
5+
"Linux-X64": "8465b5ad6b34c723786ae56478ece1a3a778c812c4d0a0521236a12c6544f57d",
6+
"macOS-ARM64": "38b84b24366841fc2b7c5fe081d87310d8e62051884147e155bc74a929a770b7",
7+
"macOS-X64": "1be86d88e8a9be530a8c48ba73cd000daaae9d0435ae1db9ff3a368444639b99",
88
}
99

1010
_swift_arch_map = {

swift/third_party/swift-llvm-support/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ _arch_override = {
1717
for arch in ("linux", "darwin_x86_64", "darwin_arm64")
1818
}),
1919
)
20-
for name in ("swift-llvm-support", "swift-test-sdk")
20+
for name in ("swift-llvm-support", "swift-test-sdk", "swift-frontend")
2121
]

swift/third_party/swift-llvm-support/BUILD.swift-llvm-support.bazel

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,16 @@ load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
22

33
cc_library(
44
name = "swift-llvm-support",
5-
srcs = [
6-
"libCodeQLSwiftFrontendTool.a",
7-
] + select({
8-
"@platforms//os:linux": [
9-
"libCodeQLSwiftFrontendTool.so",
10-
"libswiftCore.so",
11-
],
12-
"@platforms//os:macos": [
13-
"libCodeQLSwiftFrontendTool.dylib",
14-
"libswiftCore.dylib",
15-
"libswiftCompatibility50.a",
16-
"libswiftCompatibility51.a",
17-
"libswiftCompatibilityConcurrency.a",
18-
"libswiftCompatibilityDynamicReplacements.a",
19-
],
20-
}),
21-
hdrs = glob(["include/**/*", "stdlib/**/*" ]),
5+
srcs = glob([
6+
"lib*.a",
7+
"lib*.so",
8+
"lib*.dylib",
9+
]),
10+
hdrs = glob([
11+
"include/**/*",
12+
"stdlib/**/*",
13+
]),
14+
includes = ["include"],
2215
linkopts = [
2316
"-lm",
2417
"-lz",
@@ -34,7 +27,6 @@ cc_library(
3427
],
3528
"//conditions:default": [],
3629
}),
37-
includes = [ "include" ],
3830
visibility = ["//visibility:public"],
3931
)
4032

@@ -46,3 +38,5 @@ pkg_files(
4638
strip_prefix = strip_prefix.from_pkg(),
4739
visibility = ["//visibility:public"],
4840
)
41+
42+
exports_files(["swift-frontend"])

0 commit comments

Comments
 (0)