diff options
-rw-r--r-- | src/CMakeLists.txt | 15 | ||||
-rw-r--r-- | src/Options.h | 4 | ||||
-rw-r--r-- | src/RunClang.cxx | 285 | ||||
-rw-r--r-- | src/RunClang.h | 26 | ||||
-rw-r--r-- | src/castxml.cxx | 32 |
5 files changed, 356 insertions, 6 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0320e1..d4be67b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,19 @@ configure_file( "${CastXML_BINARY_DIR}/CMakeFiles/castxmlSourceDir.txt" @ONLY ) +set(clang_libs + clangFrontend + clangDriver + clangSerialization + clangParse + clangSema + clangAnalysis + clangEdit + clangAST + clangLex + clangBasic + ) + llvm_map_components_to_libraries(llvm_libs native option bitreader) add_executable(castxml @@ -27,10 +40,12 @@ add_executable(castxml Detect.cxx Detect.h Options.h + RunClang.cxx RunClang.h Utils.cxx Utils.h ) target_link_libraries(castxml cxsys + ${clang_libs} ${llvm_libs} ) set_property(SOURCE Utils.cxx APPEND PROPERTY COMPILE_DEFINITIONS diff --git a/src/Options.h b/src/Options.h index 84c1fa6..58ba3b5 100644 --- a/src/Options.h +++ b/src/Options.h @@ -22,9 +22,11 @@ struct Options { - Options(): GccXml(false), HaveCC(false) {} + Options(): PPOnly(false), GccXml(false), HaveCC(false) {} + bool PPOnly; bool GccXml; bool HaveCC; + std::string OutputFile; std::vector<std::string> Includes; std::string Predefines; std::string Triple; diff --git a/src/RunClang.cxx b/src/RunClang.cxx new file mode 100644 index 0000000..6db3f95 --- /dev/null +++ b/src/RunClang.cxx @@ -0,0 +1,285 @@ +/* + Copyright Kitware, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "RunClang.h" +#include "Options.h" + +#include "clang/AST/ASTConsumer.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/raw_ostream.h" + +#include <iostream> + +//---------------------------------------------------------------------------- +class ASTConsumer: public clang::ASTConsumer +{ + clang::CompilerInstance& CI; + llvm::raw_ostream& OS; +public: + ASTConsumer(clang::CompilerInstance& ci, llvm::raw_ostream& os): + CI(ci), OS(os) {} + + void HandleTranslationUnit(clang::ASTContext& ctx) { + // TODO: Run visitor on this translation unit. + } +}; + +//---------------------------------------------------------------------------- +template <class T> +class CastXMLPredefines: public T +{ +protected: + Options const& Opts; + + CastXMLPredefines(Options const& opts): Opts(opts) {} + std::string UpdatePredefines(std::string const& predefines) { + // Clang's InitializeStandardPredefinedMacros forces some + // predefines even when -undef is given. Filter them out. + // Also substitute our chosen predefines prior to those that came + // from the command line. + char const predef_start[] = "# 1 \"<built-in>\" 3\n"; + char const predef_end[] = "# 1 \"<command line>\" 1\n"; + std::string::size_type start = predefines.find(predef_start); + std::string::size_type end = predefines.find(predef_end); + if(start != std::string::npos && end != std::string::npos) { + return (predefines.substr(0, start+sizeof(predef_start)-1) + + this->Opts.Predefines + + predefines.substr(end)); + } else { + return predefines + this->Opts.Predefines; + } + } + + bool BeginSourceFileAction(clang::CompilerInstance& CI, + llvm::StringRef /*Filename*/) { + if(this->Opts.HaveCC) { + CI.getPreprocessor().setPredefines( + this->UpdatePredefines(CI.getPreprocessor().getPredefines())); + } + return true; + } +}; + +//---------------------------------------------------------------------------- +class CastXMLPrintPreprocessedAction: + public CastXMLPredefines<clang::PrintPreprocessedAction> +{ +public: + CastXMLPrintPreprocessedAction(Options const& opts): + CastXMLPredefines(opts) {} +}; + +//---------------------------------------------------------------------------- +class CastXMLSyntaxOnlyAction: + public CastXMLPredefines<clang::SyntaxOnlyAction> +{ + clang::ASTConsumer* CreateASTConsumer(clang::CompilerInstance &CI, + llvm::StringRef InFile) { + using llvm::sys::path::filename; + if(!this->Opts.GccXml) { + return clang::SyntaxOnlyAction::CreateASTConsumer(CI, InFile); + } else if(llvm::raw_ostream* OS = + CI.createDefaultOutputFile(false, filename(InFile), "xml")) { + return new ASTConsumer(CI, *OS); + } else { + return 0; + } + } +public: + CastXMLSyntaxOnlyAction(Options const& opts): + CastXMLPredefines(opts) {} +}; + +//---------------------------------------------------------------------------- +static clang::FrontendAction* +CreateFrontendAction(clang::CompilerInstance* CI, Options const& opts) +{ + clang::frontend::ActionKind action = + CI->getInvocation().getFrontendOpts().ProgramAction; + switch(action) { + case clang::frontend::PrintPreprocessedInput: + return new CastXMLPrintPreprocessedAction(opts); + case clang::frontend::ParseSyntaxOnly: + return new CastXMLSyntaxOnlyAction(opts); + default: + std::cerr << "error: unsupported action: " << int(action) << "\n"; + return 0; + } +} + +//---------------------------------------------------------------------------- +static bool runClangCI(clang::CompilerInstance* CI, Options const& opts) +{ + // Create a diagnostics engine for this compiler instance. + CI->createDiagnostics(); + if(!CI->hasDiagnostics()) { + return false; + } + + // We do not need function bodies. + CI->getFrontendOpts().SkipFunctionBodies = true; + + // Set frontend options we captured directly. + CI->getFrontendOpts().OutputFile = opts.OutputFile; + + // Construct our Clang front-end action. This dispatches + // handling of each input file with an action based on the + // flags provided (e.g. -E to preprocess-only). + llvm::OwningPtr<clang::FrontendAction> + action(CreateFrontendAction(CI, opts)); + if(action) { + return CI->ExecuteAction(*action); + } else { + return false; + } +} + +//---------------------------------------------------------------------------- +static llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> +runClangCreateDiagnostics(const char* const* argBeg, const char* const* argEnd) +{ + llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> + diagOpts(new clang::DiagnosticOptions); + llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> + diagID(new clang::DiagnosticIDs()); + llvm::OwningPtr<llvm::opt::OptTable> + opts(clang::driver::createDriverOptTable()); + unsigned missingArgIndex, missingArgCount; + llvm::OwningPtr<llvm::opt::InputArgList> + args(opts->ParseArgs(argBeg, argEnd, missingArgIndex, missingArgCount)); + clang::ParseDiagnosticArgs(*diagOpts, *args); + clang::TextDiagnosticPrinter* diagClient = + new clang::TextDiagnosticPrinter(llvm::errs(), &*diagOpts); + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> + diags(new clang::DiagnosticsEngine(diagID, &*diagOpts, diagClient)); + clang::ProcessWarningOptions(*diags, *diagOpts, /*ReportDiags=*/false); + return diags; +} + +//---------------------------------------------------------------------------- +static int runClangImpl(const char* const* argBeg, + const char* const* argEnd, + Options const& opts) +{ + // Construct a diagnostics engine for use while processing driver options. + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags = + runClangCreateDiagnostics(argBeg, argEnd); + + // Use the approach in clang::createInvocationFromCommandLine to + // get system compiler setting arguments from the Driver. + clang::driver::Driver d("clang", llvm::sys::getDefaultTargetTriple(), + "dummy.out", *diags); + llvm::SmallVector<const char *, 16> cArgs; + cArgs.push_back("<clang>"); + cArgs.insert(cArgs.end(), argBeg, argEnd); + + // Tell the driver not to generate any commands past syntax parsing. + if(opts.PPOnly) { + cArgs.push_back("-E"); + } else { + cArgs.push_back("-fsyntax-only"); + } + + // Ask the driver to build the compiler commands for us. + llvm::OwningPtr<clang::driver::Compilation> c(d.BuildCompilation(cArgs)); + + // For '-###' just print the jobs and exit early. + if(c->getArgs().hasArg(clang::driver::options::OPT__HASH_HASH_HASH)) { + c->getJobs().Print(llvm::errs(), "\n", true); + return 0; + } + + // Reject '-o' with multiple inputs. + if(!opts.OutputFile.empty() && c->getJobs().size() > 1) { + diags->Report(clang::diag::err_drv_output_argument_with_multiple_files); + return 1; + } + + // Run Clang for each compilation computed by the driver. + // This should be once per input source file. + bool result = true; + for(clang::driver::JobList::const_iterator i = c->getJobs().begin(), + e = c->getJobs().end(); i != e; ++i) { + clang::driver::Command* cmd = llvm::dyn_cast<clang::driver::Command>(*i); + if(cmd && strcmp(cmd->getCreator().getName(), "clang") == 0) { + // Invoke Clang with this set of arguments. + llvm::OwningPtr<clang::CompilerInstance> + CI(new clang::CompilerInstance()); + const char* const* cmdArgBeg = cmd->getArguments().data(); + const char* const* cmdArgEnd = cmdArgBeg + cmd->getArguments().size(); + if (clang::CompilerInvocation::CreateFromArgs + (CI->getInvocation(), cmdArgBeg, cmdArgEnd, *diags)) { + result = runClangCI(CI.get(), opts) && result; + } else { + result = false; + } + } else { + // Skip this unexpected job. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream msg(buf); + (*i)->Print(msg, "\n", true); + diags->Report(clang::diag::err_fe_expected_clang_command); + diags->Report(clang::diag::err_fe_expected_compiler_job) + << msg.str(); + result = false; + } + } + return result? 0:1; +} + +//---------------------------------------------------------------------------- +int runClang(const char* const* argBeg, + const char* const* argEnd, + Options const& opts) +{ + llvm::SmallVector<const char*, 32> args(argBeg, argEnd); + + if(opts.HaveCC) { + // Configure target to match that of given compiler. + if(!opts.Triple.empty()) { + args.push_back("-target"); + args.push_back(opts.Triple.c_str()); + } + + // Tell Clang driver not to add its header search paths. + args.push_back("-nostdinc"); + + // Add header search paths detected from given compiler. + for(std::vector<std::string>::const_iterator i = opts.Includes.begin(), + e = opts.Includes.end(); i != e; ++i) { + args.push_back("-isystem"); + args.push_back(i->c_str()); + } + + // Tell Clang not to add its predefines. + args.push_back("-undef"); + } + + return runClangImpl(args.data(), args.data() + args.size(), opts); +} diff --git a/src/RunClang.h b/src/RunClang.h new file mode 100644 index 0000000..4918137 --- /dev/null +++ b/src/RunClang.h @@ -0,0 +1,26 @@ +/* + Copyright Kitware, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#ifndef CASTXML_RUNCLANG_H +#define CASTXML_RUNCLANG_H + +struct Options; + +/// runClang - Run Clang with given user arguments and detected options. +int runClang(const char* const* argBeg, + const char* const* argEnd, + Options const& opts); + +#endif // CASTXML_RUNCLANG_H diff --git a/src/castxml.cxx b/src/castxml.cxx index 983a508..3766e5d 100644 --- a/src/castxml.cxx +++ b/src/castxml.cxx @@ -16,6 +16,7 @@ #include "Detect.h" #include "Options.h" +#include "RunClang.h" #include "Utils.h" #include "llvm/ADT/SmallVector.h" @@ -74,11 +75,33 @@ int main(int argc, const char* const * argv) ; return 1; } + } else if(strcmp(argv[i], "-E") == 0) { + opts.PPOnly = true; + } else if(strcmp(argv[i], "-o") == 0) { + if((i+1) < argc) { + opts.OutputFile = argv[++i]; + } else { + std::cerr << + "error: argument to '-o' is missing (expected 1 value)\n" + "\n" << + usage + ; + return 1; + } } else { clang_args.push_back(argv[i]); } } + if(opts.PPOnly && opts.GccXml) { + std::cerr << + "error: '--castxml-gccxml' and '-E' may not both be given\n" + "\n" << + usage + ; + return 1; + } + if(cc_id) { opts.HaveCC = true; if(cc_args.empty()) { @@ -96,11 +119,10 @@ int main(int argc, const char* const * argv) } } - std::cerr << opts.Predefines; - for(std::vector<std::string>::iterator i = opts.Includes.begin(), - e = opts.Includes.end(); i != e; ++i) { - std::cerr << *i << "\n"; + if(clang_args.empty()) { + return 0; } - return 0; + return runClang(clang_args.data(), clang_args.data() + clang_args.size(), + opts); } |