1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 19:23:23 +01:00

Allow hooks with arguments.

llvm-svn: 62685
This commit is contained in:
Mikhail Glushenkov 2009-01-21 13:04:00 +00:00
parent 8ff90a156b
commit 9153777db7
3 changed files with 241 additions and 51 deletions

View File

@ -0,0 +1,16 @@
// Check that hooks with arguments work.
// RUN: tblgen -I $srcroot/include --gen-llvmc %s -o %t
// RUN: grep {Hook(const char\\* Arg0, const char\\* Arg1, const char\\* Arg2);} %t | count 1
// RUN: grep "/path" %t | count 1
// RUN: grep "VARIABLE" %t | count 1
// RUN: grep "/2path" %t | count 1
include "llvm/CompilerDriver/Common.td"
def dummy_tool : Tool<[
(cmd_line "$CALL(Hook, 'Arg1', 'Arg2', 'Arg3 Arg3Cont')/path arg1 $ENV(VARIABLE)/2path arg2 $INFILE"),
(in_language "dummy"),
(out_language "dummy")
]>;
def DummyGraph : CompilationGraph<[SimpleEdge<"root", "dummy_tool">]>;

View File

@ -560,16 +560,21 @@ Hooks and environment variables
------------------------------- -------------------------------
Normally, LLVMC executes programs from the system ``PATH``. Sometimes, Normally, LLVMC executes programs from the system ``PATH``. Sometimes,
this is not sufficient: for example, we may want to specify tool names this is not sufficient: for example, we may want to specify tool paths
in the configuration file. This can be achieved via the mechanism of or names in the configuration file. This can be easily achieved via
hooks - to write your own hooks, just add their definitions to the the hooks mechanism. To write your own hooks, just add their
``PluginMain.cpp`` or drop a ``.cpp`` file into the definitions to the ``PluginMain.cpp`` or drop a ``.cpp`` file into the
``$LLVMC_DIR/driver`` directory. Hooks should live in the ``hooks`` your plugin directory. Hooks should live in the ``hooks`` namespace
namespace and have the signature ``std::string hooks::MyHookName and have the signature ``const char* hooks::MyHookName ([const char*
(void)``. They can be used from the ``cmd_line`` tool property:: Arg0 [ const char* Arg2 [, ...]]])``. They can be used from the
``cmd_line`` tool property::
(cmd_line "$CALL(MyHook)/path/to/file -o $CALL(AnotherHook)") (cmd_line "$CALL(MyHook)/path/to/file -o $CALL(AnotherHook)")
To pass arguments to hooks, use the following syntax::
(cmd_line "$CALL(MyHook, 'Arg1', 'Arg2', 'Arg # 3')/path/to/file -o1 -o2")
It is also possible to use environment variables in the same manner:: It is also possible to use environment variables in the same manner::
(cmd_line "$ENV(VAR1)/path/to/file -o $ENV(VAR2)") (cmd_line "$ENV(VAR1)/path/to/file -o $ENV(VAR2)")

View File

@ -118,6 +118,15 @@ std::string EscapeVariableName(const std::string& Var) {
return ret; return ret;
} }
/// oneOf - Does the input string contain this character?
bool oneOf(const char* lst, char c) {
while (*lst) {
if (*lst++ == c)
return true;
}
return false;
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Back-end specific code /// Back-end specific code
@ -1041,39 +1050,157 @@ void EmitCaseConstructHandler(const Init* Dag, const char* IndentLevel,
} }
} }
/// TokenizeCmdline - converts from "$CALL(HookName, 'Arg1', 'Arg2')/path" to
/// ["$CALL(", "HookName", "Arg1", "Arg2", ")/path"] .
/// Helper function used by EmitCmdLineVecFill and.
void TokenizeCmdline(const std::string& CmdLine, StrVector& Out) {
const char* Delimiters = " \t\n\v\f\r";
enum TokenizerState
{ Normal, SpecialCommand, InsideSpecialCommand, InsideQuotationMarks }
cur_st = Normal;
Out.push_back("");
std::string::size_type B = CmdLine.find_first_not_of(Delimiters),
E = CmdLine.size();
if (B == std::string::npos)
throw "Empty command-line string!";
for (; B != E; ++B) {
char cur_ch = CmdLine[B];
switch (cur_st) {
case Normal:
if (cur_ch == '$') {
cur_st = SpecialCommand;
break;
}
if (oneOf(Delimiters, cur_ch)) {
// Skip whitespace
B = CmdLine.find_first_not_of(Delimiters, B);
if (B == std::string::npos) {
B = E-1;
continue;
}
--B;
Out.push_back("");
continue;
}
break;
case SpecialCommand:
if (oneOf(Delimiters, cur_ch)) {
cur_st = Normal;
Out.push_back("");
continue;
}
if (cur_ch == '(') {
Out.push_back("");
cur_st = InsideSpecialCommand;
continue;
}
break;
case InsideSpecialCommand:
if (oneOf(Delimiters, cur_ch)) {
continue;
}
if (cur_ch == '\'') {
cur_st = InsideQuotationMarks;
Out.push_back("");
continue;
}
if (cur_ch == ')') {
cur_st = Normal;
Out.push_back("");
}
if (cur_ch == ',') {
continue;
}
break;
case InsideQuotationMarks:
if (cur_ch == '\'') {
cur_st = InsideSpecialCommand;
continue;
}
break;
}
Out.back().push_back(cur_ch);
}
}
template <class I, class S>
void checkedIncrement(I& P, I E, S ErrorString) {
++P;
if (P == E)
throw ErrorString;
}
/// SubstituteSpecialCommands - Perform string substitution for $CALL /// SubstituteSpecialCommands - Perform string substitution for $CALL
/// and $ENV. Helper function used by EmitCmdLineVecFill(). /// and $ENV. Helper function used by EmitCmdLineVecFill().
std::string SubstituteSpecialCommands(const std::string& cmd) { StrVector::const_iterator SubstituteSpecialCommands
size_t cparen = cmd.find(")"); (StrVector::const_iterator Pos, StrVector::const_iterator End, std::ostream& O)
std::string ret; {
if (cmd.find("$CALL(") == 0) { const std::string& cmd = *Pos;
if (cmd.size() == 6)
if (cmd == "$CALL") {
checkedIncrement(Pos, End, "Syntax error in $CALL invocation!");
const std::string& CmdName = *Pos;
if (CmdName == ")")
throw std::string("$CALL invocation: empty argument list!"); throw std::string("$CALL invocation: empty argument list!");
ret += "hooks::"; O << "hooks::";
ret += std::string(cmd.begin() + 6, cmd.begin() + cparen); O << CmdName << "(";
ret += "()";
}
else if (cmd.find("$ENV(") == 0) {
if (cmd.size() == 5)
throw std::string("$ENV invocation: empty argument list!");
ret += "checkCString(std::getenv(\"";
ret += std::string(cmd.begin() + 5, cmd.begin() + cparen); bool firstIteration = true;
ret += "\"))"; while (true) {
checkedIncrement(Pos, End, "Syntax error in $CALL invocation!");
const std::string& Arg = *Pos;
assert(Arg.size() != 0);
if (Arg[0] == ')')
break;
if (firstIteration)
firstIteration = false;
else
O << ", ";
O << '"' << Arg << '"';
}
O << ')';
}
else if (cmd == "$ENV") {
checkedIncrement(Pos, End, "Syntax error in $ENV invocation!");
const std::string& EnvName = *Pos;
if (EnvName == ")")
throw "$ENV invocation: empty argument list!";
O << "checkCString(std::getenv(\"";
O << EnvName;
O << "\"))";
checkedIncrement(Pos, End, "Syntax error in $ENV invocation!");
} }
else { else {
throw "Unknown special command: " + cmd; throw "Unknown special command: " + cmd;
} }
if (cmd.begin() + cparen + 1 != cmd.end()) { const std::string& Leftover = *Pos;
ret += " + std::string(\""; assert(Leftover.at(0) == ')');
ret += (cmd.c_str() + cparen + 1); if (Leftover.size() != 1)
ret += "\")"; O << " + std::string(\"" << (Leftover.c_str() + 1) << "\")";
} O << ')';
return ret; return Pos;
} }
/// EmitCmdLineVecFill - Emit code that fills in the command line /// EmitCmdLineVecFill - Emit code that fills in the command line
@ -1082,14 +1209,28 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
bool IsJoin, const char* IndentLevel, bool IsJoin, const char* IndentLevel,
std::ostream& O) { std::ostream& O) {
StrVector StrVec; StrVector StrVec;
SplitString(InitPtrToString(CmdLine), StrVec); TokenizeCmdline(InitPtrToString(CmdLine), StrVec);
if (StrVec.empty()) if (StrVec.empty())
throw "Tool " + ToolName + " has empty command line!"; throw "Tool " + ToolName + " has empty command line!";
StrVector::const_iterator I = StrVec.begin(); StrVector::const_iterator I = StrVec.begin(), E = StrVec.end();
++I;
for (StrVector::const_iterator E = StrVec.end(); I != E; ++I) { // If there is a hook invocation on the place of the first command, skip it.
if (StrVec[0][0] == '$') {
while (I != E && (*I)[0] != ')' )
++I;
// Skip the ')' symbol.
++I;
}
else {
++I;
}
for (; I != E; ++I) {
const std::string& cmd = *I; const std::string& cmd = *I;
// std::cerr << cmd;
O << IndentLevel; O << IndentLevel;
if (cmd.at(0) == '$') { if (cmd.at(0) == '$') {
if (cmd == "$INFILE") { if (cmd == "$INFILE") {
@ -1105,7 +1246,8 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
O << "vec.push_back(out_file);\n"; O << "vec.push_back(out_file);\n";
} }
else { else {
O << "vec.push_back(" << SubstituteSpecialCommands(cmd); O << "vec.push_back(";
I = SubstituteSpecialCommands(I, E, O);
O << ");\n"; O << ");\n";
} }
} }
@ -1113,10 +1255,13 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
O << "vec.push_back(\"" << cmd << "\");\n"; O << "vec.push_back(\"" << cmd << "\");\n";
} }
} }
O << IndentLevel << "cmd = " O << IndentLevel << "cmd = ";
<< ((StrVec[0][0] == '$') ? SubstituteSpecialCommands(StrVec[0])
: "\"" + StrVec[0] + "\"") if (StrVec[0][0] == '$')
<< ";\n"; SubstituteSpecialCommands(StrVec.begin(), StrVec.end(), O);
else
O << '"' << StrVec[0] << '"';
O << ";\n";
} }
/// EmitCmdLineVecFillCallback - A function object wrapper around /// EmitCmdLineVecFillCallback - A function object wrapper around
@ -1650,22 +1795,39 @@ void EmitPopulateCompilationGraph (const RecordVector& EdgeVector,
/// $CALL(HookName) in the provided command line string. Helper /// $CALL(HookName) in the provided command line string. Helper
/// function used by FillInHookNames(). /// function used by FillInHookNames().
class ExtractHookNames { class ExtractHookNames {
llvm::StringSet<>& HookNames_; llvm::StringMap<unsigned>& HookNames_;
public: public:
ExtractHookNames(llvm::StringSet<>& HookNames) ExtractHookNames(llvm::StringMap<unsigned>& HookNames)
: HookNames_(HookNames_) {} : HookNames_(HookNames) {}
void operator()(const Init* CmdLine) { void operator()(const Init* CmdLine) {
StrVector cmds; StrVector cmds;
llvm::SplitString(InitPtrToString(CmdLine), cmds); TokenizeCmdline(InitPtrToString(CmdLine), cmds);
for (StrVector::const_iterator B = cmds.begin(), E = cmds.end(); for (StrVector::const_iterator B = cmds.begin(), E = cmds.end();
B != E; ++B) { B != E; ++B) {
const std::string& cmd = *B; const std::string& cmd = *B;
if (cmd.find("$CALL(") == 0) {
if (cmd.size() == 6) if (cmd == "$CALL") {
throw std::string("$CALL invocation: empty argument list!"); unsigned NumArgs = 0;
HookNames_.insert(std::string(cmd.begin() + 6, checkedIncrement(B, E, "Syntax error in $CALL invocation!");
cmd.begin() + cmd.find(")"))); const std::string& HookName = *B;
if (HookName.at(0) == ')')
throw "$CALL invoked with no arguments!";
while (++B != E && B->at(0) != ')') {
++NumArgs;
}
StringMap<unsigned>::const_iterator H = HookNames_.find(HookName);
if (H != HookNames_.end() && H->second != NumArgs)
throw "Overloading of hooks is not allowed. Overloaded hook: "
+ HookName;
else
HookNames_[HookName] = NumArgs;
} }
} }
} }
@ -1674,7 +1836,7 @@ public:
/// FillInHookNames - Actually extract the hook names from all command /// FillInHookNames - Actually extract the hook names from all command
/// line strings. Helper function used by EmitHookDeclarations(). /// line strings. Helper function used by EmitHookDeclarations().
void FillInHookNames(const ToolDescriptions& ToolDescs, void FillInHookNames(const ToolDescriptions& ToolDescs,
llvm::StringSet<>& HookNames) llvm::StringMap<unsigned>& HookNames)
{ {
// For all command lines: // For all command lines:
for (ToolDescriptions::const_iterator B = ToolDescs.begin(), for (ToolDescriptions::const_iterator B = ToolDescs.begin(),
@ -1695,16 +1857,23 @@ void FillInHookNames(const ToolDescriptions& ToolDescs,
/// property records and emit hook function declaration for each /// property records and emit hook function declaration for each
/// instance of $CALL(HookName). /// instance of $CALL(HookName).
void EmitHookDeclarations(const ToolDescriptions& ToolDescs, std::ostream& O) { void EmitHookDeclarations(const ToolDescriptions& ToolDescs, std::ostream& O) {
llvm::StringSet<> HookNames; llvm::StringMap<unsigned> HookNames;
FillInHookNames(ToolDescs, HookNames); FillInHookNames(ToolDescs, HookNames);
if (HookNames.empty()) if (HookNames.empty())
return; return;
O << "namespace hooks {\n"; O << "namespace hooks {\n";
for (StringSet<>::const_iterator B = HookNames.begin(), E = HookNames.end(); for (StringMap<unsigned>::const_iterator B = HookNames.begin(),
B != E; ++B) E = HookNames.end(); B != E; ++B) {
O << Indent1 << "std::string " << B->first() << "();\n"; O << Indent1 << "const char* " << B->first() << "(";
for (unsigned i = 0, j = B->second; i < j; ++i) {
O << "const char* Arg" << i << (i+1 == j ? "" : ", ");
}
O <<");\n";
}
O << "}\n\n"; O << "}\n\n";
} }