Add support for user-provided hooks and environment variable reads to the cmd_line tool property.

Used like this: (cmd_line "$CALL(MyHook) --option -o $ENV(VARIABLE) $CALL(AnotherHook)")
Also works with case expressions.

Hook declarations are auto-generated, the definitions should be provided by the user
(just drop a .cpp file in the tools/llvmc2 directory).

Hooks should live in the "hooks" namespace and have type std::string hooks::Hook(void).

llvm-svn: 51732
This commit is contained in:
Mikhail Glushenkov 2008-05-30 06:12:24 +00:00
parent 550052d0e5
commit fc4ed7ad83
2 changed files with 114 additions and 16 deletions

View File

@ -18,6 +18,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include <cstdlib>
#include <stdexcept>
using namespace llvm;

View File

@ -845,12 +845,32 @@ void EmitOptionPropertyHandlingCode (const ToolOptionDescription& D,
O << Indent2 << "}\n";
}
/// SubstituteSpecialCommands - Perform string substitution for $CALL
/// and $ENV. Helper function used by EmitCmdLineVecFill().
std::string SubstituteSpecialCommands(const std::string& cmd) {
if (cmd.find("$CALL(") == 0) {
if (cmd.size() == 6)
throw std::string("$CALL invocation: empty argument list!");
return std::string("hooks::") + (cmd.c_str() + 6) + "()";
}
else if (cmd.find("$ENV(") == 0) {
if (cmd.size() == 5)
throw std::string("$ENV invocation: empty argument list!");
return std::string("std::getenv(\"") + (cmd.c_str() + 5) + "\")";
}
else {
throw "Unknown special command: " + cmd;
}
}
/// EmitCmdLineVecFill - Emit code that fills in the command line
/// vector. Helper function used by EmitGenerateActionMethod().
void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
bool Version, const char* IndentLevel,
std::ostream& O) {
StrVector StrVec;
SplitString(InitPtrToString(CmdLine), StrVec);
if (InitPtrToString(CmdLine).empty())
SplitString(InitPtrToString(CmdLine), StrVec, ") ");
if (StrVec.empty())
throw "Tool " + ToolName + " has empty command line!";
StrVector::const_iterator I = StrVec.begin();
@ -858,25 +878,36 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
for (StrVector::const_iterator E = StrVec.end(); I != E; ++I) {
const std::string& cmd = *I;
O << IndentLevel;
if (cmd == "$INFILE") {
if (Version)
O << "for (PathVector::const_iterator B = inFiles.begin()"
<< ", E = inFiles.end();\n"
<< IndentLevel << "B != E; ++B)\n"
<< IndentLevel << Indent1 << "vec.push_back(B->toString());\n";
else
O << "vec.push_back(inFile.toString());\n";
}
else if (cmd == "$OUTFILE") {
O << "vec.push_back(outFile.toString());\n";
if (cmd.at(0) == '$') {
if (cmd == "$INFILE") {
if (Version)
O << "for (PathVector::const_iterator B = inFiles.begin()"
<< ", E = inFiles.end();\n"
<< IndentLevel << "B != E; ++B)\n"
<< IndentLevel << Indent1 << "vec.push_back(B->toString());\n";
else
O << "vec.push_back(inFile.toString());\n";
}
else if (cmd == "$OUTFILE") {
O << "vec.push_back(outFile.toString());\n";
}
else {
O << "vec.push_back(" << SubstituteSpecialCommands(cmd) << ");\n";
}
}
else {
O << "vec.push_back(\"" << cmd << "\");\n";
}
}
O << IndentLevel << "ret = Action(\"" << StrVec.at(0) << "\", vec);\n";
O << IndentLevel << "ret = Action("
<< ((StrVec[0][0] == '$') ? SubstituteSpecialCommands(StrVec[0])
: "\"" + StrVec[0] + "\"")
<< ", vec);\n";
}
/// EmitCmdLineVecFillCallback - A function object wrapper around
/// EmitCmdLineVecFill(). Used by EmitGenerateActionMethod() as an
/// argument to EmitCaseConstructHandler().
class EmitCmdLineVecFillCallback {
bool Version;
const std::string& ToolName;
@ -1150,7 +1181,8 @@ void TypecheckGraph (Record* CompilationGraph,
}
}
// Helper function passed to EmitCaseConstructHandler by EmitEdgeClass.
/// IncDecWeight - Helper function passed to EmitCaseConstructHandler()
/// by EmitEdgeClass().
void IncDecWeight (const Init* i, const char* IndentLevel,
std::ostream& O) {
const DagInit& d = InitPtrToDagInitRef(i);
@ -1192,7 +1224,7 @@ void EmitEdgeClass (unsigned N, const std::string& Target,
<< Indent1 << "};\n\n};\n\n";
}
// Emit Edge* classes that represent graph edges.
/// EmitEdgeClasses - Emit Edge* classes that represent graph edges.
void EmitEdgeClasses (Record* CompilationGraph,
const GlobalOptionDescriptions& OptDescs,
std::ostream& O) {
@ -1256,6 +1288,68 @@ void EmitPopulateCompilationGraph (Record* CompilationGraph,
O << "}\n\n";
}
/// ExtractHookNames - Extract the hook names from all instances of
/// $CALL(HookName) in the provided command line string. Helper
/// function used by FillInHookNames().
void ExtractHookNames(const Init* CmdLine, StrVector& HookNames) {
StrVector cmds;
llvm::SplitString(InitPtrToString(CmdLine), cmds, ") ");
for (StrVector::const_iterator B = cmds.begin(), E = cmds.end();
B != E; ++B) {
const std::string& cmd = *B;
if (cmd.find("$CALL(") == 0) {
if (cmd.size() == 6)
throw std::string("$CALL invocation: empty argument list!");
HookNames.push_back(std::string(cmd.c_str() + 6));
}
}
}
/// FillInHookNames - Actually extract the hook names from all command
/// line strings. Helper function used by EmitHookDeclarations().
void FillInHookNames(const ToolPropertiesList& TPList,
StrVector& HookNames) {
for (ToolPropertiesList::const_iterator B = TPList.begin(),
E = TPList.end(); B != E; ++B) {
const ToolProperties& P = *(*B);
if (!P.CmdLine)
continue;
if (typeid(*P.CmdLine) == typeid(StringInit)) {
// This is a string.
ExtractHookNames(P.CmdLine, HookNames);
}
else {
// This is a 'case' construct.
const DagInit& d = InitPtrToDagInitRef(P.CmdLine);
bool even = false;
for (DagInit::const_arg_iterator B = d.arg_begin(), E = d.arg_end();
B != E; ++B) {
if (even)
ExtractHookNames(*B, HookNames);
even = !even;
}
}
}
}
/// EmitHookDeclarations - Parse CmdLine fields of all the tool
/// property records and emit hook function declaration for each
/// instance of $CALL(HookName).
void EmitHookDeclarations(const ToolPropertiesList& ToolProps,
std::ostream& O) {
StrVector HookNames;
FillInHookNames(ToolProps, HookNames);
if (HookNames.empty())
return;
std::sort(HookNames.begin(), HookNames.end());
StrVector::const_iterator E = std::unique(HookNames.begin(), HookNames.end());
O << "namespace hooks {\n";
for (StrVector::const_iterator B = HookNames.begin(); B != E; ++B)
O << Indent1 << "std::string " << *B << "();\n";
O << "}\n\n";
}
// End of anonymous namespace
}
@ -1279,6 +1373,9 @@ void LLVMCConfigurationEmitter::run (std::ostream &O) {
// Emit global option registration code.
EmitOptionDescriptions(opt_descs, O);
// Emit hook declarations.
EmitHookDeclarations(tool_props, O);
// Emit PopulateLanguageMap() function
// (a language map maps from file extensions to language names).
EmitPopulateLanguageMap(Records, O);