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
2019using 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-
64101static 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+
121167int 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}
0 commit comments