Skip to content

Commit bf1b32f

Browse files
committed
Swift: rework file redirection
The hash map mechanism that was already in use for reading swiftmodule files on macOS is now in use also on Linux. The output replacing mechanism has been also reworked so that: * frontend module emission modes have the remapping done directly in the internal frontend options instead of painstakingly modifying input flags (this requires a patch on the swift headers though) * object emission mode is silenced to be just a type checking pass, thus producing no output files * all other passes but some debugging and version related ones become noops The open file read redirection uses a global weak pointer instance to maximize robustness in the face of possibly multi-threaded calls to open happening while `main` is exiting. Possibly overkill, but better safe than sorry.
1 parent 944adfe commit bf1b32f

18 files changed

+263
-333
lines changed

swift/extractor/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ swift_cc_binary(
99
]),
1010
visibility = ["//swift:__pkg__"],
1111
deps = [
12+
"//swift/extractor/config",
1213
"//swift/extractor/infra",
1314
"//swift/extractor/invocation",
1415
"//swift/extractor/remapping",

swift/extractor/SwiftExtractor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22

3-
#include "swift/extractor/SwiftExtractorConfiguration.h"
3+
#include "swift/extractor/config/SwiftExtractorConfiguration.h"
44
#include <swift/AST/SourceFile.h>
55
#include <swift/Frontend/Frontend.h>
66
#include <memory>

swift/extractor/TargetTrapFile.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ std::optional<TargetFile> createTargetTrapFile(const SwiftExtractorConfiguration
1111
for (const auto& opt : configuration.frontendOptions) {
1212
*ret << " " << std::quoted(opt) << " \\\n";
1313
}
14-
*ret << "\n*/\n"
15-
"/* swift-frontend-args:\n";
16-
for (const auto& opt : configuration.patchedFrontendOptions) {
17-
*ret << " " << std::quoted(opt) << " \\\n";
18-
}
1914
*ret << "\n*/\n";
2015
}
2116
return ret;

swift/extractor/TargetTrapFile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include "swift/extractor/infra/file/TargetFile.h"
4-
#include "swift/extractor/SwiftExtractorConfiguration.h"
4+
#include "swift/extractor/config/SwiftExtractorConfiguration.h"
55

66
namespace codeql {
77

swift/extractor/config/BUILD.bazel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
load("//swift:rules.bzl", "swift_cc_library")
2+
3+
swift_cc_library(
4+
name = "config",
5+
srcs = glob(["*.cpp"]),
6+
hdrs = glob(["*.h"]),
7+
visibility = ["//swift:__subpackages__"],
8+
)

swift/extractor/SwiftExtractorConfiguration.h renamed to swift/extractor/config/SwiftExtractorConfiguration.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ struct SwiftExtractorConfiguration {
1717
std::filesystem::path scratchDir;
1818

1919
// The original arguments passed to the extractor. Used for debugging.
20-
std::vector<std::string> frontendOptions;
21-
// The patched arguments passed to the swift::performFrontend/ Used for debugging.
22-
std::vector<std::string> patchedFrontendOptions;
20+
std::vector<const char*> frontendOptions;
2321

2422
// A temporary directory that contains TRAP files before they are moved into their final
2523
// destination.

swift/extractor/main.cpp

Lines changed: 66 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,58 @@
1212

1313
#include "swift/extractor/SwiftExtractor.h"
1414
#include "swift/extractor/TargetTrapFile.h"
15-
#include "swift/extractor/remapping/SwiftOutputRewrite.h"
16-
#include "swift/extractor/remapping/SwiftOpenInterception.h"
15+
#include "swift/extractor/remapping/SwiftFileInterception.h"
1716
#include "swift/extractor/invocation/SwiftDiagnosticsConsumer.h"
1817
#include "swift/extractor/trap/TrapDomain.h"
1918

2019
using namespace std::string_literals;
2120

21+
static void lockOutputSwiftModuleTraps(const codeql::SwiftExtractorConfiguration& config,
22+
const swift::CompilerInstance& compiler) {
23+
std::filesystem::path output = compiler.getInvocation().getOutputFilename();
24+
if (output.extension() == ".swiftmodule") {
25+
if (auto target = codeql::createTargetTrapFile(config, output)) {
26+
*target << "// trap file deliberately empty\n"
27+
"// this swiftmodule was created during the build, so its entities must have"
28+
" been extracted directly from source files";
29+
}
30+
}
31+
}
32+
33+
static void modifyFrontendOptions(swift::FrontendOptions& options) {
34+
using Action = swift::FrontendOptions::ActionType;
35+
switch (options.RequestedAction) {
36+
case Action::EmitModuleOnly:
37+
case Action::MergeModules:
38+
case Action::CompileModuleFromInterface:
39+
// for module emission actions, we redirect the output to our internal artifact storage
40+
{
41+
swift::SupplementaryOutputPaths paths;
42+
paths.ModuleOutputPath =
43+
codeql::redirect(options.InputsAndOutputs.getSingleOutputFilename()).string();
44+
options.InputsAndOutputs.setMainAndSupplementaryOutputs(std::vector{paths.ModuleOutputPath},
45+
std::vector{paths});
46+
return;
47+
}
48+
case Action::EmitObject:
49+
// for object emission, we do a type check pass instead, muting output but getting the sema
50+
// phase to run in order to extract everything
51+
options.RequestedAction = Action::Typecheck;
52+
return;
53+
case Action::PrintVersion:
54+
case Action::DumpAST:
55+
case Action::PrintAST:
56+
case Action::PrintASTDecl:
57+
// these actions are nice to have on the extractor for debugging, so we preserve them. Also,
58+
// version printing is used by CI to match up the correct compiler version
59+
return;
60+
default:
61+
// otherwise, do nothing (the closest action to doing nothing is printing the version)
62+
options.RequestedAction = Action::PrintVersion;
63+
break;
64+
}
65+
}
66+
2267
// This is part of the swiftFrontendTool interface, we hook into the
2368
// compilation pipeline and extract files after the Swift frontend performed
2469
// semantic analysis
@@ -28,8 +73,13 @@ class Observer : public swift::FrontendObserver {
2873
codeql::SwiftDiagnosticsConsumer& diagConsumer)
2974
: config{config}, diagConsumer{diagConsumer} {}
3075

76+
void parsedArgs(swift::CompilerInvocation& invocation) override {
77+
modifyFrontendOptions(invocation.getFrontendOptions());
78+
}
79+
3180
void configuredCompiler(swift::CompilerInstance& instance) override {
3281
instance.addDiagnosticConsumer(&diagConsumer);
82+
lockOutputSwiftModuleTraps(config, instance);
3383
}
3484

3585
void performedSemanticAnalysis(swift::CompilerInstance& compiler) override {
@@ -48,19 +98,6 @@ static std::string getenv_or(const char* envvar, const std::string& def) {
4898
return def;
4999
}
50100

51-
static void lockOutputSwiftModuleTraps(const codeql::SwiftExtractorConfiguration& config,
52-
const codeql::PathRemapping& remapping) {
53-
for (const auto& [oldPath, newPath] : remapping) {
54-
if (oldPath.extension() == ".swiftmodule") {
55-
if (auto target = codeql::createTargetTrapFile(config, oldPath)) {
56-
*target << "// trap file deliberately empty\n"
57-
"// this swiftmodule was created during the build, so its entities must have"
58-
" been extracted directly from source files";
59-
}
60-
}
61-
}
62-
}
63-
64101
static bool checkRunUnderFilter(int argc, char* const* argv) {
65102
auto runUnderFilter = getenv("CODEQL_EXTRACTOR_SWIFT_RUN_UNDER_FILTER");
66103
if (runUnderFilter == nullptr) {
@@ -106,7 +143,7 @@ static void checkWhetherToRunUnderTool(int argc, char* const* argv) {
106143

107144
// Creates a target file that should store per-invocation info, e.g. compilation args,
108145
// compilations, diagnostics, etc.
109-
codeql::TargetFile invocationTargetFile(codeql::SwiftExtractorConfiguration& configuration) {
146+
codeql::TargetFile invocationTargetFile(const codeql::SwiftExtractorConfiguration& configuration) {
110147
auto timestamp = std::chrono::system_clock::now().time_since_epoch().count();
111148
auto filename = std::to_string(timestamp) + '-' + std::to_string(getpid());
112149
auto target = std::filesystem::path("invocations") / std::filesystem::path(filename);
@@ -118,6 +155,15 @@ codeql::TargetFile invocationTargetFile(codeql::SwiftExtractorConfiguration& con
118155
return std::move(maybeFile.value());
119156
}
120157

158+
codeql::SwiftExtractorConfiguration configure(int argc, char** argv) {
159+
codeql::SwiftExtractorConfiguration configuration{};
160+
configuration.trapDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_TRAP_DIR", ".");
161+
configuration.sourceArchiveDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SOURCE_ARCHIVE_DIR", ".");
162+
configuration.scratchDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SCRATCH_DIR", ".");
163+
configuration.frontendOptions.assign(argv + 1, argv + argc);
164+
return configuration;
165+
}
166+
121167
int main(int argc, char** argv) {
122168
checkWhetherToRunUnderTool(argc, argv);
123169

@@ -130,36 +176,16 @@ int main(int argc, char** argv) {
130176
PROGRAM_START(argc, argv);
131177
INITIALIZE_LLVM();
132178

133-
codeql::SwiftExtractorConfiguration configuration{};
134-
configuration.trapDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_TRAP_DIR", ".");
135-
configuration.sourceArchiveDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SOURCE_ARCHIVE_DIR", ".");
136-
configuration.scratchDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SCRATCH_DIR", ".");
137-
138-
codeql::initRemapping(configuration.getTempArtifactDir());
139-
140-
configuration.frontendOptions.reserve(argc - 1);
141-
for (int i = 1; i < argc; i++) {
142-
configuration.frontendOptions.push_back(argv[i]);
143-
}
144-
configuration.patchedFrontendOptions = configuration.frontendOptions;
145-
146-
auto remapping = codeql::rewriteOutputsInPlace(configuration.getTempArtifactDir(),
147-
configuration.patchedFrontendOptions);
148-
codeql::ensureDirectoriesForNewPathsExist(remapping);
149-
lockOutputSwiftModuleTraps(configuration, remapping);
179+
const auto configuration = configure(argc, argv);
150180

151-
std::vector<const char*> args;
152-
for (auto& arg : configuration.patchedFrontendOptions) {
153-
args.push_back(arg.c_str());
154-
}
181+
auto openInterception = codeql::setupFileInterception(configuration.getTempArtifactDir());
155182

156183
auto invocationTrapFile = invocationTargetFile(configuration);
157184
codeql::TrapDomain invocationDomain(invocationTrapFile);
158185
codeql::SwiftDiagnosticsConsumer diagConsumer(invocationDomain);
159186
Observer observer(configuration, diagConsumer);
160-
int frontend_rc = swift::performFrontend(args, "swift-extractor", (void*)main, &observer);
161-
162-
codeql::finalizeRemapping(remapping);
187+
int frontend_rc = swift::performFrontend(configuration.frontendOptions, "swift-extractor",
188+
(void*)main, &observer);
163189

164190
return frontend_rc;
165191
}

swift/extractor/remapping/BUILD.bazel

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,11 @@ load("//swift:rules.bzl", "swift_cc_library")
22

33
swift_cc_library(
44
name = "remapping",
5-
srcs = ["SwiftOutputRewrite.cpp"] + select({
6-
"@platforms//os:linux": [
7-
"SwiftOpenInterception.Linux.cpp",
8-
],
9-
"@platforms//os:macos": [
10-
"SwiftOpenInterception.macOS.cpp",
11-
],
12-
}),
5+
srcs = glob(["*.cpp"]),
136
hdrs = glob(["*.h"]),
147
visibility = ["//swift:__subpackages__"],
158
deps = [
16-
"//swift/third_party/swift-llvm-support",
179
"//swift/extractor/infra/file",
18-
] + select({
19-
"@platforms//os:linux": [],
20-
"@platforms//os:macos": [
21-
"@fishhook//:fishhook",
22-
],
23-
}),
10+
"//swift/third_party/swift-llvm-support",
11+
],
2412
)

0 commit comments

Comments
 (0)