summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2014-01-10 18:24:56 (GMT)
committerBrad King <brad.king@kitware.com>2014-02-26 16:54:30 (GMT)
commitf2efefcde133208c355f235baf5dee4d4287e466 (patch)
treef75b6cb2a49fe587cdfc4c0871a23c2ceebb5c52 /src
parent0c62ed33c937ccb101fcb6f30306626da1c312e5 (diff)
downloadCastXML-f2efefcde133208c355f235baf5dee4d4287e466.zip
CastXML-f2efefcde133208c355f235baf5dee4d4287e466.tar.gz
CastXML-f2efefcde133208c355f235baf5dee4d4287e466.tar.bz2
Add API to run Clang internally
Use the Clang Driver API to construct an invocation of Clang like the main "clang" compiler tool would for syntax-only or preprocess-only actions. Add two front-end actions, one for the '-E' preprocess-only option and one for our syntax-only AST traversal. Implement a syntax-only action that falls back to the Clang syntax-only action unless xml output is requested. Leave xml output unimplemented for now. Capture the '-o <output-file>' option ourselves since the Clang Driver API will ignore it when using a syntax-only action. Forward it to the compiler invocation frontend options just before constructing the frontend action implementation. When '--castxml-cc-<id>' is given tell the driver to suppress its own standard include path detection. Give the driver options to configure it to match the detected target triple and header search path. Tell it to suppress its builtin definitions. Hook in to the front-end action for each input source file to put the detected predefines into the preprocessor configuration.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt15
-rw-r--r--src/Options.h4
-rw-r--r--src/RunClang.cxx285
-rw-r--r--src/RunClang.h26
-rw-r--r--src/castxml.cxx32
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);
}