Ninja: extract dependencies for .rc files with msvc tools

rc.exe doesn't support /showIncludes.
Because .rc files also #include stuff we can
misuse cl.exe to get the included files.

Done one the fly by cmcldeps.
This commit is contained in:
Peter Kuemmel 2012-06-13 17:14:16 +02:00
parent 43200c145d
commit 54a388beaa
2 changed files with 82 additions and 25 deletions

View File

@ -339,7 +339,7 @@ cmNinjaTargetGenerator
bool useClDeps = false; bool useClDeps = false;
std::string clDepsBinary; std::string clDepsBinary;
std::string clShowPrefix; std::string clShowPrefix;
if (lang == "C" || lang == "CXX") if (lang == "C" || lang == "CXX" || lang == "RC")
{ {
const char* depsPtr = mf->GetDefinition("CMAKE_CMCLDEPS_EXECUTABLE"); const char* depsPtr = mf->GetDefinition("CMAKE_CMCLDEPS_EXECUTABLE");
const char* showPtr = mf->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX"); const char* showPtr = mf->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX");
@ -352,8 +352,9 @@ cmNinjaTargetGenerator
if (projectName != "CMAKE_TRY_COMPILE") if (projectName != "CMAKE_TRY_COMPILE")
{ {
useClDeps = true; useClDeps = true;
clDepsBinary = depsPtr; std::string qu = "\"";
clShowPrefix = showPtr; clDepsBinary = qu + depsPtr + qu;
clShowPrefix = qu + showPtr + qu;
vars.DependencyFile = "$DEP_FILE"; vars.DependencyFile = "$DEP_FILE";
} }
} }
@ -392,8 +393,10 @@ cmNinjaTargetGenerator
if(useClDeps) if(useClDeps)
{ {
cmdLine = "\"" + clDepsBinary + "\" $in \"$DEP_FILE\" $out \"" std::string cl = mf->GetDefinition("CMAKE_C_COMPILER");
+ clShowPrefix + "\" " + cmdLine; cl = "\"" + cl + "\" ";
cmdLine = clDepsBinary + " " + lang + " $in \"$DEP_FILE\" $out "
+ clShowPrefix + " " + cl + cmdLine;
} }
// Write the rule for compiling file of the given language. // Write the rule for compiling file of the given language.

View File

@ -535,14 +535,17 @@ static string getArg(string& cmdline) {
} }
static void parseCommandLine(LPTSTR wincmdline, static void parseCommandLine(LPTSTR wincmdline,
string& lang,
string& srcfile, string& srcfile,
string& dfile, string& dfile,
string& objfile, string& objfile,
string& prefix, string& prefix,
string& clpath, string& clpath,
string& binpath,
string& rest) { string& rest) {
string cmdline(wincmdline); string cmdline(wincmdline);
/* self */ getArg(cmdline); /* self */ getArg(cmdline);
lang = getArg(cmdline);
srcfile = getArg(cmdline); srcfile = getArg(cmdline);
std::string::size_type pos = srcfile.rfind("\\"); std::string::size_type pos = srcfile.rfind("\\");
if (pos != string::npos) { if (pos != string::npos) {
@ -554,12 +557,16 @@ static void parseCommandLine(LPTSTR wincmdline,
objfile = getArg(cmdline); objfile = getArg(cmdline);
prefix = getArg(cmdline); prefix = getArg(cmdline);
clpath = getArg(cmdline); clpath = getArg(cmdline);
binpath = getArg(cmdline);
rest = trimLeadingSpace(cmdline); rest = trimLeadingSpace(cmdline);
} }
static void outputDepFile(const string& dfile, const string& objfile, static void outputDepFile(const string& dfile, const string& objfile,
vector<string>& incs) { vector<string>& incs) {
if (dfile.empty())
return;
// strip duplicates // strip duplicates
sort(incs.begin(), incs.end()); sort(incs.begin(), incs.end());
incs.erase(unique(incs.begin(), incs.end()), incs.end()); incs.erase(unique(incs.begin(), incs.end()), incs.end());
@ -581,6 +588,7 @@ static void outputDepFile(const string& dfile, const string& objfile,
//doEscape(tmp, "(", "\\("); // TODO ninja can't read ( and ) //doEscape(tmp, "(", "\\("); // TODO ninja can't read ( and )
//doEscape(tmp, ")", "\\)"); //doEscape(tmp, ")", "\\)");
fprintf(out, "%s \\\n", tmp.c_str()); fprintf(out, "%s \\\n", tmp.c_str());
//printf("include: %s \n", tmp.c_str());
} }
fprintf(out, "\n"); fprintf(out, "\n");
@ -596,29 +604,26 @@ bool contains(const std::string& str, const std::string& what) {
return str.find(what) != std::string::npos; return str.find(what) != std::string::npos;
} }
int main() { std::string replace(const std::string& str, const std::string& what, const std::string& replacement) {
size_t pos = str.find(what);
if (pos == std::string::npos)
return str;
std::string replaced = str;
return replaced.replace(pos, what.size(), replacement);
}
// Use the Win32 api instead of argc/argv so we can avoid interpreting the
// rest of command line after the .d and .obj. Custom parsing seemed
// preferable to the ugliness you get into in trying to re-escape quotes for
// subprocesses, so by avoiding argc/argv, the subprocess is called with
// the same command line verbatim.
string srcfile, dfile, objfile, prefix, clpath, rest;
parseCommandLine(GetCommandLine(), srcfile, dfile, objfile,
prefix, clpath, rest);
#if 0 static int process(bool ignoreErrors,
fprintf(stderr, "\n\ncmcldebug:\n"); const string& srcfile,
fprintf(stderr, ".d : %s\n", dfile.c_str()); const string& dfile,
fprintf(stderr, "OBJ : %s\n", objfile.c_str()); const string& objfile,
fprintf(stderr, "CL : %s\n", clpath.c_str()); const string& prefix,
fprintf(stderr, "REST: %s\n", rest.c_str()); const string& cmd) {
fprintf(stderr, "\n\n");
#endif
SubprocessSet subprocs; SubprocessSet subprocs;
Subprocess* subproc = subprocs.Add(clpath + " /showIncludes " + rest); Subprocess* subproc = subprocs.Add(cmd);
if(!subproc) if(!subproc)
return 2; return 2;
@ -647,18 +652,67 @@ int main() {
} }
} else { } else {
if (!isFirstLine || !startsWith(line, srcfile)) { if (!isFirstLine || !startsWith(line, srcfile)) {
if (!ignoreErrors) {
// suppress errors when cl is fed with a rc file
fprintf(stdout, "%s\n", line.c_str()); fprintf(stdout, "%s\n", line.c_str());
}
} else { } else {
isFirstLine = false; isFirstLine = false;
} }
} }
} }
if (!success) if (!success) {
if (ignoreErrors) {
//printf("\n-- RC file %i dependencies in %s\n\n", includes.size(), dfile.c_str());
outputDepFile(dfile, objfile, includes);
}
return exit_code; return exit_code;
}
// don't update .d until/unless we succeed compilation // don't update .d until/unless we succeed compilation
outputDepFile(dfile, objfile, includes); outputDepFile(dfile, objfile, includes);
return 0; return 0;
} }
int main() {
// Use the Win32 api instead of argc/argv so we can avoid interpreting the
// rest of command line after the .d and .obj. Custom parsing seemed
// preferable to the ugliness you get into in trying to re-escape quotes for
// subprocesses, so by avoiding argc/argv, the subprocess is called with
// the same command line verbatim.
string lang, srcfile, dfile, objfile, prefix, cl, binpath, rest;
parseCommandLine(GetCommandLine(), lang, srcfile, dfile, objfile,
prefix, cl, binpath, rest);
#if 0
fprintf(stderr, "\n\ncmcldebug:\n");
fprintf(stderr, ".d : %s\n", dfile.c_str());
fprintf(stderr, "OBJ : %s\n", objfile.c_str());
fprintf(stderr, "CL : %s\n", clpath.c_str());
fprintf(stderr, "REST: %s\n", rest.c_str());
fprintf(stderr, "\n\n");
#endif
if (lang != "RC") {
return process(false, srcfile, dfile, objfile, prefix, binpath + " /showIncludes " + rest);
} else {
// "misuse" cl.exe to get headers from .rc files
// rc: /fo x\CMakeFiles\x.dir\x.rc.res src\x\x.rc
// cl: /out:x\CMakeFiles\x.dir\x.rc.res.dep.obj /Tc src\x\x.rc
cl = "\"" + cl + "\" /showIncludes ";
string clRest = rest;
clRest = replace(clRest, "/fo" + objfile, "/out:" + objfile + ".dep.obj /Tc ");
int ret;
ret = process(true, srcfile, dfile, objfile, prefix, cl + clRest);
ret = process(false, srcfile, "" , objfile, prefix, binpath + " " + rest);
return ret;
}
}