summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);
}