From 54a388beaaec8b9a4e4bb6bfb220d5711a866866 Mon Sep 17 00:00:00 2001 From: Peter Kuemmel Date: Wed, 13 Jun 2012 17:14:16 +0200 Subject: [PATCH] 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. --- Source/cmNinjaTargetGenerator.cxx | 13 +++-- Source/cmcldeps.cxx | 94 ++++++++++++++++++++++++------- 2 files changed, 82 insertions(+), 25 deletions(-) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index ce1aac2fc..f76bc0bcf 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -339,7 +339,7 @@ cmNinjaTargetGenerator bool useClDeps = false; std::string clDepsBinary; 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* showPtr = mf->GetDefinition("CMAKE_CL_SHOWINCLUDE_PREFIX"); @@ -352,8 +352,9 @@ cmNinjaTargetGenerator if (projectName != "CMAKE_TRY_COMPILE") { useClDeps = true; - clDepsBinary = depsPtr; - clShowPrefix = showPtr; + std::string qu = "\""; + clDepsBinary = qu + depsPtr + qu; + clShowPrefix = qu + showPtr + qu; vars.DependencyFile = "$DEP_FILE"; } } @@ -392,8 +393,10 @@ cmNinjaTargetGenerator if(useClDeps) { - cmdLine = "\"" + clDepsBinary + "\" $in \"$DEP_FILE\" $out \"" - + clShowPrefix + "\" " + cmdLine; + std::string cl = mf->GetDefinition("CMAKE_C_COMPILER"); + cl = "\"" + cl + "\" "; + cmdLine = clDepsBinary + " " + lang + " $in \"$DEP_FILE\" $out " + + clShowPrefix + " " + cl + cmdLine; } // Write the rule for compiling file of the given language. diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 549eeac25..542fb9084 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -535,14 +535,17 @@ static string getArg(string& cmdline) { } static void parseCommandLine(LPTSTR wincmdline, + string& lang, string& srcfile, string& dfile, string& objfile, string& prefix, string& clpath, + string& binpath, string& rest) { string cmdline(wincmdline); /* self */ getArg(cmdline); + lang = getArg(cmdline); srcfile = getArg(cmdline); std::string::size_type pos = srcfile.rfind("\\"); if (pos != string::npos) { @@ -554,12 +557,16 @@ static void parseCommandLine(LPTSTR wincmdline, objfile = getArg(cmdline); prefix = getArg(cmdline); clpath = getArg(cmdline); + binpath = getArg(cmdline); rest = trimLeadingSpace(cmdline); } static void outputDepFile(const string& dfile, const string& objfile, vector& incs) { + if (dfile.empty()) + return; + // strip duplicates sort(incs.begin(), 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, ")", "\\)"); fprintf(out, "%s \\\n", tmp.c_str()); + //printf("include: %s \n", tmp.c_str()); } fprintf(out, "\n"); @@ -596,29 +604,26 @@ bool contains(const std::string& str, const std::string& what) { 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 - 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 +static int process(bool ignoreErrors, + const string& srcfile, + const string& dfile, + const string& objfile, + const string& prefix, + const string& cmd) { SubprocessSet subprocs; - Subprocess* subproc = subprocs.Add(clpath + " /showIncludes " + rest); + Subprocess* subproc = subprocs.Add(cmd); + if(!subproc) return 2; @@ -647,18 +652,67 @@ int main() { } } else { if (!isFirstLine || !startsWith(line, srcfile)) { - fprintf(stdout, "%s\n", line.c_str()); + if (!ignoreErrors) { + // suppress errors when cl is fed with a rc file + fprintf(stdout, "%s\n", line.c_str()); + } } else { 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; + } // don't update .d until/unless we succeed compilation outputDepFile(dfile, objfile, includes); 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; + } + +}