diff options
Diffstat (limited to 'lib/Tooling/Tooling.cpp')
-rw-r--r-- | lib/Tooling/Tooling.cpp | 136 |
1 files changed, 75 insertions, 61 deletions
diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp index fa2374f..e93e0c9 100644 --- a/lib/Tooling/Tooling.cpp +++ b/lib/Tooling/Tooling.cpp @@ -12,13 +12,13 @@ // //===----------------------------------------------------------------------===// +#include "clang/Tooling/ArgumentsAdjusters.h" #include "clang/Tooling/Tooling.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "llvm/ADT/STLExtras.h" @@ -26,6 +26,13 @@ #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" +// For chdir, see the comment in ClangTool::run for more information. +#ifdef _WIN32 +# include <direct.h> +#else +# include <unistd.h> +#endif + namespace clang { namespace tooling { @@ -40,8 +47,8 @@ static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics, const char *BinaryName) { const std::string DefaultOutputName = "a.out"; clang::driver::Driver *CompilerDriver = new clang::driver::Driver( - BinaryName, llvm::sys::getDefaultTargetTriple(), - DefaultOutputName, false, *Diagnostics); + BinaryName, llvm::sys::getDefaultTargetTriple(), + DefaultOutputName, false, *Diagnostics); CompilerDriver->setTitle("clang_based_tool"); return CompilerDriver; } @@ -108,29 +115,26 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, return Invocation.run(); } -/// \brief Returns the absolute path of 'File', by prepending it with -/// 'BaseDirectory' if 'File' is not absolute. -/// -/// Otherwise returns 'File'. -/// If 'File' starts with "./", the returned path will not contain the "./". -/// Otherwise, the returned path will contain the literal path-concatenation of -/// 'BaseDirectory' and 'File'. -/// -/// \param File Either an absolute or relative path. -/// \param BaseDirectory An absolute path. -static std::string getAbsolutePath( - StringRef File, StringRef BaseDirectory) { - assert(llvm::sys::path::is_absolute(BaseDirectory)); +std::string getAbsolutePath(StringRef File) { + llvm::SmallString<1024> BaseDirectory; + if (const char *PWD = ::getenv("PWD")) + BaseDirectory = PWD; + else + llvm::sys::fs::current_path(BaseDirectory); + SmallString<1024> PathStorage; if (llvm::sys::path::is_absolute(File)) { - return File; + llvm::sys::path::native(File, PathStorage); + return PathStorage.str(); } StringRef RelativePath(File); + // FIXME: Should '.\\' be accepted on Win32? if (RelativePath.startswith("./")) { RelativePath = RelativePath.substr(strlen("./")); } llvm::SmallString<1024> AbsolutePath(BaseDirectory); llvm::sys::path::append(AbsolutePath, RelativePath); - return AbsolutePath.str(); + llvm::sys::path::native(Twine(AbsolutePath), PathStorage); + return PathStorage.str(); } ToolInvocation::ToolInvocation( @@ -140,7 +144,9 @@ ToolInvocation::ToolInvocation( } void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { - MappedFileContents[FilePath] = Content; + SmallString<1024> PathStorage; + llvm::sys::path::native(FilePath, PathStorage); + MappedFileContents[PathStorage] = Content; } bool ToolInvocation::run() { @@ -167,20 +173,15 @@ bool ToolInvocation::run() { } llvm::OwningPtr<clang::CompilerInvocation> Invocation( newInvocation(&Diagnostics, *CC1Args)); - return runInvocation(BinaryName, Compilation.get(), - Invocation.take(), *CC1Args, ToolAction.take()); + return runInvocation(BinaryName, Compilation.get(), Invocation.take(), + *CC1Args); } -// Exists solely for the purpose of lookup of the resource path. -static int StaticSymbol; - bool ToolInvocation::runInvocation( const char *BinaryName, clang::driver::Compilation *Compilation, clang::CompilerInvocation *Invocation, - const clang::driver::ArgStringList &CC1Args, - clang::FrontendAction *ToolAction) { - llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction); + const clang::driver::ArgStringList &CC1Args) { // Show the invocation, with -v. if (Invocation->getHeaderSearchOpts().Verbose) { llvm::errs() << "clang Invocation:\n"; @@ -194,6 +195,11 @@ bool ToolInvocation::runInvocation( Compiler.setFileManager(Files); // FIXME: What about LangOpts? + // ToolAction can have lifetime requirements for Compiler or its members, and + // we need to ensure it's deleted earlier than Compiler. So we pass it to an + // OwningPtr declared after the Compiler variable. + llvm::OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take()); + // Create the compilers actual diagnostics engine. Compiler.createDiagnostics(CC1Args.size(), const_cast<char**>(CC1Args.data())); @@ -203,18 +209,10 @@ bool ToolInvocation::runInvocation( Compiler.createSourceManager(*Files); addFileMappingsTo(Compiler.getSourceManager()); - // Infer the builtin include path if unspecified. - if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes && - Compiler.getHeaderSearchOpts().ResourceDir.empty()) { - // This just needs to be some symbol in the binary. - void *const SymbolAddr = &StaticSymbol; - Compiler.getHeaderSearchOpts().ResourceDir = - clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr); - } - - const bool Success = Compiler.ExecuteAction(*ToolAction); + const bool Success = Compiler.ExecuteAction(*ScopedToolAction); Compiler.resetAndLeakFileManager(); + Files->clearStatCaches(); return Success; } @@ -228,35 +226,23 @@ void ToolInvocation::addFileMappingsTo(SourceManager &Sources) { // FIXME: figure out what '0' stands for. const FileEntry *FromFile = Files->getVirtualFile( It->getKey(), Input->getBufferSize(), 0); - // FIXME: figure out memory management ('true'). - Sources.overrideFileContents(FromFile, Input, true); + Sources.overrideFileContents(FromFile, Input); } } ClangTool::ClangTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths) - : Files((FileSystemOptions())) { - llvm::SmallString<1024> BaseDirectory; - if (const char *PWD = ::getenv("PWD")) - BaseDirectory = PWD; - else - llvm::sys::fs::current_path(BaseDirectory); + : Files((FileSystemOptions())), + ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) { for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { - llvm::SmallString<1024> File(getAbsolutePath( - SourcePaths[I], BaseDirectory)); + llvm::SmallString<1024> File(getAbsolutePath(SourcePaths[I])); - std::vector<CompileCommand> CompileCommands = + std::vector<CompileCommand> CompileCommandsForFile = Compilations.getCompileCommands(File.str()); - if (!CompileCommands.empty()) { - for (int I = 0, E = CompileCommands.size(); I != E; ++I) { - CompileCommand &Command = CompileCommands[I]; - if (!Command.Directory.empty()) { - // FIXME: What should happen if CommandLine includes -working-directory - // as well? - Command.CommandLine.push_back( - "-working-directory=" + Command.Directory); - } - CommandLines.push_back(std::make_pair(File.str(), Command.CommandLine)); + if (!CompileCommandsForFile.empty()) { + for (int I = 0, E = CompileCommandsForFile.size(); I != E; ++I) { + CompileCommands.push_back(std::make_pair(File.str(), + CompileCommandsForFile[I])); } } else { // FIXME: There are two use cases here: doing a fuzzy @@ -273,11 +259,39 @@ void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { MappedFileContents.push_back(std::make_pair(FilePath, Content)); } +void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { + ArgsAdjuster.reset(Adjuster); +} + int ClangTool::run(FrontendActionFactory *ActionFactory) { + // Exists solely for the purpose of lookup of the resource path. + // This just needs to be some symbol in the binary. + static int StaticSymbol; + // The driver detects the builtin header path based on the path of the + // executable. + // FIXME: On linux, GetMainExecutable is independent of the value of the + // first argument, thus allowing ClangTool and runToolOnCode to just + // pass in made-up names here. Make sure this works on other platforms. + std::string MainExecutable = + llvm::sys::Path::GetMainExecutable("clang_tool", &StaticSymbol).str(); + bool ProcessingFailed = false; - for (unsigned I = 0; I < CommandLines.size(); ++I) { - std::string File = CommandLines[I].first; - std::vector<std::string> &CommandLine = CommandLines[I].second; + for (unsigned I = 0; I < CompileCommands.size(); ++I) { + std::string File = CompileCommands[I].first; + // FIXME: chdir is thread hostile; on the other hand, creating the same + // behavior as chdir is complex: chdir resolves the path once, thus + // guaranteeing that all subsequent relative path operations work + // on the same path the original chdir resulted in. This makes a difference + // for example on network filesystems, where symlinks might be switched + // during runtime of the tool. Fixing this depends on having a file system + // abstraction that allows openat() style interactions. + if (chdir(CompileCommands[I].second.Directory.c_str())) + llvm::report_fatal_error("Cannot chdir into \"" + + CompileCommands[I].second.Directory + "\n!"); + std::vector<std::string> CommandLine = + ArgsAdjuster->Adjust(CompileCommands[I].second.CommandLine); + assert(!CommandLine.empty()); + CommandLine[0] = MainExecutable; llvm::outs() << "Processing: " << File << ".\n"; ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files); for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { |