Skip to content

Commit f95b585

Browse files
authored
Merge pull request github#8788 from AlexDenisov/alexdenisov/swift-first-extractor-test
Swift: file extraction
2 parents dc96d55 + 272aa59 commit f95b585

File tree

16 files changed

+218
-22
lines changed

16 files changed

+218
-22
lines changed

.bazelrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
build --copt="-std=c++17"
1+
# -fno-rtti is required by LLVM/Swift
2+
build --repo_env=CC=clang --repo_env=CXX=clang++ --copt="-std=c++17" --copt="-fno-rtti"
23

34
try-import %workspace%/local.bazelrc

swift/codegen/prefix.dbscheme

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,3 @@ sourceLocationPrefix(
55
string prefix: string ref
66
);
77

8-
answer_to_life_the_universe_and_everything(
9-
int answer: int ref
10-
);

swift/extractor/BUILD.bazel

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ alias(
88

99
cc_binary(
1010
name = "extractor",
11-
srcs = ["main.cpp"],
11+
srcs = [
12+
"SwiftExtractor.h",
13+
"SwiftExtractor.cpp",
14+
"SwiftExtractorConfiguration.h",
15+
"main.cpp",
16+
],
1217
target_compatible_with = select({
1318
"@platforms//os:linux": [],
1419
"@platforms//os:macos": [],

swift/extractor/SwiftExtractor.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#include "SwiftExtractor.h"
2+
3+
#include <filesystem>
4+
#include <fstream>
5+
#include <iostream>
6+
#include <sstream>
7+
#include <memory>
8+
#include <unistd.h>
9+
10+
#include <swift/AST/SourceFile.h>
11+
#include <llvm/ADT/SmallString.h>
12+
#include <llvm/Support/FileSystem.h>
13+
#include <llvm/Support/Path.h>
14+
15+
using namespace codeql;
16+
17+
static void extractFile(const SwiftExtractorConfiguration& config, swift::SourceFile& file) {
18+
if (std::error_code ec = llvm::sys::fs::create_directories(config.trapDir)) {
19+
std::cerr << "Cannot create TRAP directory: " << ec.message() << "\n";
20+
return;
21+
}
22+
23+
if (std::error_code ec = llvm::sys::fs::create_directories(config.sourceArchiveDir)) {
24+
std::cerr << "Cannot create source archive directory: " << ec.message() << "\n";
25+
return;
26+
}
27+
28+
llvm::SmallString<PATH_MAX> srcFilePath(file.getFilename());
29+
llvm::sys::fs::make_absolute(srcFilePath);
30+
31+
llvm::SmallString<PATH_MAX> dstFilePath(config.sourceArchiveDir);
32+
llvm::sys::path::append(dstFilePath, srcFilePath);
33+
34+
llvm::StringRef parent = llvm::sys::path::parent_path(dstFilePath);
35+
if (std::error_code ec = llvm::sys::fs::create_directories(parent)) {
36+
std::cerr << "Cannot create source archive destination directory '" << parent.str()
37+
<< "': " << ec.message() << "\n";
38+
return;
39+
}
40+
41+
if (std::error_code ec = llvm::sys::fs::copy_file(srcFilePath, dstFilePath)) {
42+
std::cerr << "Cannot archive source file '" << srcFilePath.str().str() << "' -> '"
43+
<< dstFilePath.str().str() << "': " << ec.message() << "\n";
44+
return;
45+
}
46+
47+
// The extractor can be called several times from different processes with
48+
// the same input file(s)
49+
// We are using PID to avoid concurrent access
50+
// TODO: find a more robust approach to avoid collisions?
51+
std::string tempTrapName = file.getFilename().str() + '.' + std::to_string(getpid()) + ".trap";
52+
llvm::SmallString<PATH_MAX> tempTrapPath(config.trapDir);
53+
llvm::sys::path::append(tempTrapPath, tempTrapName);
54+
55+
std::ofstream trap(tempTrapPath.str().str());
56+
if (!trap) {
57+
std::error_code ec;
58+
ec.assign(errno, std::generic_category());
59+
std::cerr << "Cannot create temp trap file '" << tempTrapPath.str().str()
60+
<< "': " << ec.message() << "\n";
61+
return;
62+
}
63+
std::stringstream ss;
64+
for (auto opt : config.frontendOptions) {
65+
ss << std::quoted(opt) << " ";
66+
}
67+
ss << "\n";
68+
trap << "// extractor-args: " << ss.str();
69+
70+
trap << "#0=*\n";
71+
trap << "files(#0, " << std::quoted(srcFilePath.str().str()) << ")\n";
72+
73+
// TODO: Pick a better name to avoid collisions
74+
std::string trapName = file.getFilename().str() + ".trap";
75+
llvm::SmallString<PATH_MAX> trapPath(config.trapDir);
76+
llvm::sys::path::append(trapPath, trapName);
77+
78+
// TODO: The last process wins. Should we do better than that?
79+
if (std::error_code ec = llvm::sys::fs::rename(tempTrapPath, trapPath)) {
80+
std::cerr << "Cannot rename temp trap file '" << tempTrapPath.str().str() << "' -> '"
81+
<< trapPath.str().str() << "': " << ec.message() << "\n";
82+
}
83+
}
84+
85+
void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config,
86+
swift::CompilerInstance& compiler) {
87+
// Swift frontend can be called in several different modes, we are interested
88+
// only in the cases when either a primary or a main source file is present
89+
if (compiler.getPrimarySourceFiles().empty()) {
90+
swift::ModuleDecl* module = compiler.getMainModule();
91+
if (!module->getFiles().empty() &&
92+
module->getFiles().front()->getKind() == swift::FileUnitKind::Source) {
93+
// We can only call getMainSourceFile if the first file is of a Source kind
94+
swift::SourceFile& file = module->getMainSourceFile();
95+
extractFile(config, file);
96+
}
97+
} else {
98+
for (auto s : compiler.getPrimarySourceFiles()) {
99+
extractFile(config, *s);
100+
}
101+
}
102+
}

swift/extractor/SwiftExtractor.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef SWIFT_EXTRACTOR_H_
2+
#define SWIFT_EXTRACTOR_H_
3+
4+
#include "SwiftExtractorConfiguration.h"
5+
#include <swift/AST/SourceFile.h>
6+
#include <swift/Frontend/Frontend.h>
7+
#include <memory>
8+
9+
namespace codeql {
10+
void extractSwiftFiles(const SwiftExtractorConfiguration& config,
11+
swift::CompilerInstance& compiler);
12+
} // namespace codeql
13+
14+
#endif // SWIFT_EXTRACTOR_H_
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef SWIFT_EXTRACTOR_CONFIGURATION_H_
2+
#define SWIFT_EXTRACTOR_CONFIGURATION_H_
3+
4+
#include <string>
5+
#include <vector>
6+
7+
namespace codeql {
8+
struct SwiftExtractorConfiguration {
9+
// The location for storing TRAP files to be imported by CodeQL engine.
10+
std::string trapDir;
11+
// The location for storing extracted source files.
12+
std::string sourceArchiveDir;
13+
// The arguments passed to the extractor. Used for debugging.
14+
std::vector<std::string> frontendOptions;
15+
};
16+
} // namespace codeql
17+
18+
#endif // SWIFT_EXTRACTOR_CONFIGURATION_H_

swift/extractor/main.cpp

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,52 @@
33
#include <stdlib.h>
44

55
#include <swift/Basic/LLVMInitialize.h>
6+
#include <swift/FrontendTool/FrontendTool.h>
7+
8+
#include "SwiftExtractor.h"
9+
10+
using namespace std::string_literals;
11+
12+
// This is part of the swiftFrontendTool interface, we hook into the
13+
// compilation pipeline and extract files after the Swift frontend performed
14+
// semantic analysys
15+
class Observer : public swift::FrontendObserver {
16+
public:
17+
explicit Observer(const codeql::SwiftExtractorConfiguration& config) : config{config} {}
18+
19+
void performedSemanticAnalysis(swift::CompilerInstance& compiler) override {
20+
codeql::extractSwiftFiles(config, compiler);
21+
}
22+
23+
private:
24+
const codeql::SwiftExtractorConfiguration& config;
25+
};
26+
27+
static std::string getenv_or(const char* envvar, const std::string& def) {
28+
if (const char* var = getenv(envvar)) {
29+
return var;
30+
}
31+
return def;
32+
}
633

734
int main(int argc, char** argv) {
35+
if (argc == 1) {
36+
// TODO: print usage
37+
return 1;
38+
}
39+
// Required by Swift/LLVM
840
PROGRAM_START(argc, argv);
9-
if (auto trapDir = getenv("CODEQL_EXTRACTOR_SWIFT_TRAP_DIR")) {
10-
std::string file = trapDir;
11-
file += "/my_first.trap";
12-
if (std::ofstream out{file}) {
13-
out << "answer_to_life_the_universe_and_everything(42)\n";
14-
}
41+
INITIALIZE_LLVM();
42+
43+
codeql::SwiftExtractorConfiguration configuration{};
44+
configuration.trapDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_TRAP_DIR", ".");
45+
configuration.sourceArchiveDir = getenv_or("CODEQL_EXTRACTOR_SWIFT_SOURCE_ARCHIVE_DIR", ".");
46+
std::vector<const char*> args;
47+
for (int i = 1; i < argc; i++) {
48+
args.push_back(argv[i]);
1549
}
16-
return 0;
50+
std::copy(std::begin(args), std::end(args), std::back_inserter(configuration.frontendOptions));
51+
Observer observer(configuration);
52+
int frontend_rc = swift::performFrontend(args, "swift-extractor", (void*)main, &observer);
53+
return frontend_rc;
1754
}
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.File
32

4-
class File extends FileBase { }
3+
class File extends FileBase {
4+
/** toString */
5+
override string toString() { result = getAbsolutePath() }
6+
7+
/** Gets the name of this file. */
8+
override string getName() { files(this, result) }
9+
10+
/** Gets the absolute path of this file. */
11+
string getAbsolutePath() { result = getName() }
12+
13+
/** Gets the full name of this file. */
14+
string getFullName() { result = getAbsolutePath() }
15+
16+
/** Gets the URL of this file. */
17+
string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
18+
19+
/** Gets the base name of this file. */
20+
string getBaseName() {
21+
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
22+
}
23+
}

swift/ql/lib/swift.dbscheme

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ sourceLocationPrefix(
88
string prefix: string ref
99
);
1010

11-
answer_to_life_the_universe_and_everything(
12-
int answer: int ref
13-
);
1411

1512

1613
// from codegen/schema.yml

swift/ql/lib/swift.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/** Top-level import for the Swift language pack */
2+
3+
import codeql.swift.elements

0 commit comments

Comments
 (0)