#include "SwiftExtractor.h" #include #include #include #include #include #include #include #include #include #include #include "swift/extractor/trap/TrapEntries.h" using namespace codeql; static void extractFile(const SwiftExtractorConfiguration& config, swift::SourceFile& file) { if (std::error_code ec = llvm::sys::fs::create_directories(config.trapDir)) { std::cerr << "Cannot create TRAP directory: " << ec.message() << "\n"; return; } if (std::error_code ec = llvm::sys::fs::create_directories(config.sourceArchiveDir)) { std::cerr << "Cannot create source archive directory: " << ec.message() << "\n"; return; } llvm::SmallString srcFilePath(file.getFilename()); llvm::sys::fs::make_absolute(srcFilePath); llvm::SmallString dstFilePath(config.sourceArchiveDir); llvm::sys::path::append(dstFilePath, srcFilePath); llvm::StringRef parent = llvm::sys::path::parent_path(dstFilePath); if (std::error_code ec = llvm::sys::fs::create_directories(parent)) { std::cerr << "Cannot create source archive destination directory '" << parent.str() << "': " << ec.message() << "\n"; return; } if (std::error_code ec = llvm::sys::fs::copy_file(srcFilePath, dstFilePath)) { std::cerr << "Cannot archive source file '" << srcFilePath.str().str() << "' -> '" << dstFilePath.str().str() << "': " << ec.message() << "\n"; return; } // The extractor can be called several times from different processes with // the same input file(s) // We are using PID to avoid concurrent access // TODO: find a more robust approach to avoid collisions? std::string tempTrapName = file.getFilename().str() + '.' + std::to_string(getpid()) + ".trap"; llvm::SmallString tempTrapPath(config.trapDir); llvm::sys::path::append(tempTrapPath, tempTrapName); llvm::StringRef trapParent = llvm::sys::path::parent_path(tempTrapPath); if (std::error_code ec = llvm::sys::fs::create_directories(trapParent)) { std::cerr << "Cannot create trap directory '" << trapParent.str() << "': " << ec.message() << "\n"; return; } std::ofstream trap(tempTrapPath.str().str()); if (!trap) { std::error_code ec; ec.assign(errno, std::generic_category()); std::cerr << "Cannot create temp trap file '" << tempTrapPath.str().str() << "': " << ec.message() << "\n"; return; } trap << "// extractor-args: "; for (auto opt : config.frontendOptions) { trap << std::quoted(opt) << " "; } trap << "\n\n"; TrapLabel label{}; trap << label << "=*\n"; trap << FilesTrap{label, srcFilePath.str().str()} << "\n"; // TODO: Pick a better name to avoid collisions std::string trapName = file.getFilename().str() + ".trap"; llvm::SmallString trapPath(config.trapDir); llvm::sys::path::append(trapPath, trapName); // TODO: The last process wins. Should we do better than that? if (std::error_code ec = llvm::sys::fs::rename(tempTrapPath, trapPath)) { std::cerr << "Cannot rename temp trap file '" << tempTrapPath.str().str() << "' -> '" << trapPath.str().str() << "': " << ec.message() << "\n"; } } void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, swift::CompilerInstance& compiler) { // Swift frontend can be called in several different modes, we are interested // only in the cases when either a primary or a main source file is present if (compiler.getPrimarySourceFiles().empty()) { swift::ModuleDecl* module = compiler.getMainModule(); if (!module->getFiles().empty() && module->getFiles().front()->getKind() == swift::FileUnitKind::Source) { // We can only call getMainSourceFile if the first file is of a Source kind swift::SourceFile& file = module->getMainSourceFile(); extractFile(config, file); } } else { for (auto s : compiler.getPrimarySourceFiles()) { extractFile(config, *s); } } }