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:
parent
8ff90a156b
commit
9153777db7
16
test/LLVMC/HookWithArguments.td
Normal file
16
test/LLVMC/HookWithArguments.td
Normal 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">]>;
|
@ -560,16 +560,21 @@ Hooks and environment variables
|
||||
-------------------------------
|
||||
|
||||
Normally, LLVMC executes programs from the system ``PATH``. Sometimes,
|
||||
this is not sufficient: for example, we may want to specify tool names
|
||||
in the configuration file. This can be achieved via the mechanism of
|
||||
hooks - to write your own hooks, just add their definitions to the
|
||||
``PluginMain.cpp`` or drop a ``.cpp`` file into the
|
||||
``$LLVMC_DIR/driver`` directory. Hooks should live in the ``hooks``
|
||||
namespace and have the signature ``std::string hooks::MyHookName
|
||||
(void)``. They can be used from the ``cmd_line`` tool property::
|
||||
this is not sufficient: for example, we may want to specify tool paths
|
||||
or names in the configuration file. This can be easily achieved via
|
||||
the hooks mechanism. To write your own hooks, just add their
|
||||
definitions to the ``PluginMain.cpp`` or drop a ``.cpp`` file into the
|
||||
your plugin directory. Hooks should live in the ``hooks`` namespace
|
||||
and have the signature ``const char* hooks::MyHookName ([const char*
|
||||
Arg0 [ const char* Arg2 [, ...]]])``. They can be used from the
|
||||
``cmd_line`` tool property::
|
||||
|
||||
(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::
|
||||
|
||||
(cmd_line "$ENV(VAR1)/path/to/file -o $ENV(VAR2)")
|
||||
|
@ -118,6 +118,15 @@ std::string EscapeVariableName(const std::string& Var) {
|
||||
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
|
||||
|
||||
@ -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
|
||||
/// and $ENV. Helper function used by EmitCmdLineVecFill().
|
||||
std::string SubstituteSpecialCommands(const std::string& cmd) {
|
||||
size_t cparen = cmd.find(")");
|
||||
std::string ret;
|
||||
StrVector::const_iterator SubstituteSpecialCommands
|
||||
(StrVector::const_iterator Pos, StrVector::const_iterator End, std::ostream& O)
|
||||
{
|
||||
|
||||
if (cmd.find("$CALL(") == 0) {
|
||||
if (cmd.size() == 6)
|
||||
const std::string& cmd = *Pos;
|
||||
|
||||
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!");
|
||||
|
||||
ret += "hooks::";
|
||||
ret += std::string(cmd.begin() + 6, cmd.begin() + cparen);
|
||||
ret += "()";
|
||||
}
|
||||
else if (cmd.find("$ENV(") == 0) {
|
||||
if (cmd.size() == 5)
|
||||
throw std::string("$ENV invocation: empty argument list!");
|
||||
O << "hooks::";
|
||||
O << CmdName << "(";
|
||||
|
||||
ret += "checkCString(std::getenv(\"";
|
||||
ret += std::string(cmd.begin() + 5, cmd.begin() + cparen);
|
||||
ret += "\"))";
|
||||
|
||||
bool firstIteration = true;
|
||||
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 {
|
||||
throw "Unknown special command: " + cmd;
|
||||
}
|
||||
|
||||
if (cmd.begin() + cparen + 1 != cmd.end()) {
|
||||
ret += " + std::string(\"";
|
||||
ret += (cmd.c_str() + cparen + 1);
|
||||
ret += "\")";
|
||||
}
|
||||
const std::string& Leftover = *Pos;
|
||||
assert(Leftover.at(0) == ')');
|
||||
if (Leftover.size() != 1)
|
||||
O << " + std::string(\"" << (Leftover.c_str() + 1) << "\")";
|
||||
O << ')';
|
||||
|
||||
return ret;
|
||||
return Pos;
|
||||
}
|
||||
|
||||
/// 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,
|
||||
std::ostream& O) {
|
||||
StrVector StrVec;
|
||||
SplitString(InitPtrToString(CmdLine), StrVec);
|
||||
TokenizeCmdline(InitPtrToString(CmdLine), StrVec);
|
||||
|
||||
if (StrVec.empty())
|
||||
throw "Tool " + ToolName + " has empty command line!";
|
||||
|
||||
StrVector::const_iterator I = StrVec.begin();
|
||||
++I;
|
||||
for (StrVector::const_iterator E = StrVec.end(); I != E; ++I) {
|
||||
StrVector::const_iterator I = StrVec.begin(), E = StrVec.end();
|
||||
|
||||
// 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;
|
||||
// std::cerr << cmd;
|
||||
O << IndentLevel;
|
||||
if (cmd.at(0) == '$') {
|
||||
if (cmd == "$INFILE") {
|
||||
@ -1105,7 +1246,8 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
|
||||
O << "vec.push_back(out_file);\n";
|
||||
}
|
||||
else {
|
||||
O << "vec.push_back(" << SubstituteSpecialCommands(cmd);
|
||||
O << "vec.push_back(";
|
||||
I = SubstituteSpecialCommands(I, E, O);
|
||||
O << ");\n";
|
||||
}
|
||||
}
|
||||
@ -1113,10 +1255,13 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName,
|
||||
O << "vec.push_back(\"" << cmd << "\");\n";
|
||||
}
|
||||
}
|
||||
O << IndentLevel << "cmd = "
|
||||
<< ((StrVec[0][0] == '$') ? SubstituteSpecialCommands(StrVec[0])
|
||||
: "\"" + StrVec[0] + "\"")
|
||||
<< ";\n";
|
||||
O << IndentLevel << "cmd = ";
|
||||
|
||||
if (StrVec[0][0] == '$')
|
||||
SubstituteSpecialCommands(StrVec.begin(), StrVec.end(), O);
|
||||
else
|
||||
O << '"' << StrVec[0] << '"';
|
||||
O << ";\n";
|
||||
}
|
||||
|
||||
/// EmitCmdLineVecFillCallback - A function object wrapper around
|
||||
@ -1650,22 +1795,39 @@ void EmitPopulateCompilationGraph (const RecordVector& EdgeVector,
|
||||
/// $CALL(HookName) in the provided command line string. Helper
|
||||
/// function used by FillInHookNames().
|
||||
class ExtractHookNames {
|
||||
llvm::StringSet<>& HookNames_;
|
||||
llvm::StringMap<unsigned>& HookNames_;
|
||||
public:
|
||||
ExtractHookNames(llvm::StringSet<>& HookNames)
|
||||
: HookNames_(HookNames_) {}
|
||||
ExtractHookNames(llvm::StringMap<unsigned>& HookNames)
|
||||
: HookNames_(HookNames) {}
|
||||
|
||||
void operator()(const Init* CmdLine) {
|
||||
StrVector cmds;
|
||||
llvm::SplitString(InitPtrToString(CmdLine), cmds);
|
||||
TokenizeCmdline(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_.insert(std::string(cmd.begin() + 6,
|
||||
cmd.begin() + cmd.find(")")));
|
||||
|
||||
if (cmd == "$CALL") {
|
||||
unsigned NumArgs = 0;
|
||||
checkedIncrement(B, E, "Syntax error in $CALL invocation!");
|
||||
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
|
||||
/// line strings. Helper function used by EmitHookDeclarations().
|
||||
void FillInHookNames(const ToolDescriptions& ToolDescs,
|
||||
llvm::StringSet<>& HookNames)
|
||||
llvm::StringMap<unsigned>& HookNames)
|
||||
{
|
||||
// For all command lines:
|
||||
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
|
||||
/// instance of $CALL(HookName).
|
||||
void EmitHookDeclarations(const ToolDescriptions& ToolDescs, std::ostream& O) {
|
||||
llvm::StringSet<> HookNames;
|
||||
llvm::StringMap<unsigned> HookNames;
|
||||
|
||||
FillInHookNames(ToolDescs, HookNames);
|
||||
if (HookNames.empty())
|
||||
return;
|
||||
|
||||
O << "namespace hooks {\n";
|
||||
for (StringSet<>::const_iterator B = HookNames.begin(), E = HookNames.end();
|
||||
B != E; ++B)
|
||||
O << Indent1 << "std::string " << B->first() << "();\n";
|
||||
for (StringMap<unsigned>::const_iterator B = HookNames.begin(),
|
||||
E = HookNames.end(); B != E; ++B) {
|
||||
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";
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user