diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 8ff9995ad..c9756852e 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -54,6 +54,7 @@ Jamie Iles Jan Van Winkel Jean Berniolles Jeremy Bennett +Jiacheng Qian Jiuyang Liu John Coiner John Demme diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index a2206af5e..902cd0526 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -71,15 +71,6 @@ class CMakeEmitter final { cmake_set_raw(of, name, raw_value, cache_type, docstring); } - // Swap all backslashes for forward slashes, because of Windows - static string deslash(const string& s) { - std::string res = s; - for (char& c : res) { - if (c == '\\') c = '/'; - } - return res; - } - static void emitOverallCMake() { const std::unique_ptr of{ V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")}; @@ -95,9 +86,9 @@ class CMakeEmitter final { *of << "# which becomes available after executing `find_package(verilator).\n"; *of << "\n### Constants...\n"; - cmake_set(*of, "PERL", deslash(V3Options::getenvPERL()), "FILEPATH", + cmake_set(*of, "PERL", V3Options::getenvPERL(), "FILEPATH", "Perl executable (from $PERL)"); - cmake_set(*of, "VERILATOR_ROOT", deslash(V3Options::getenvVERILATOR_ROOT()), "PATH", + cmake_set(*of, "VERILATOR_ROOT", V3Options::getenvVERILATOR_ROOT(), "PATH", "Path to Verilator kit (from $VERILATOR_ROOT)"); *of << "\n### Compiler flags...\n"; @@ -186,22 +177,22 @@ class CMakeEmitter final { } *of << "# Global classes, need linked once per executable\n"; - cmake_set_raw(*of, name + "_GLOBAL", deslash(cmake_list(global))); + cmake_set_raw(*of, name + "_GLOBAL", cmake_list(global)); *of << "# Generated module classes, non-fast-path, compile with low/medium optimization\n"; - cmake_set_raw(*of, name + "_CLASSES_SLOW", deslash(cmake_list(classes_slow))); + cmake_set_raw(*of, name + "_CLASSES_SLOW", cmake_list(classes_slow)); *of << "# Generated module classes, fast-path, compile with highest optimization\n"; - cmake_set_raw(*of, name + "_CLASSES_FAST", deslash(cmake_list(classes_fast))); + cmake_set_raw(*of, name + "_CLASSES_FAST", cmake_list(classes_fast)); *of << "# Generated support classes, non-fast-path, compile with " "low/medium optimization\n"; - cmake_set_raw(*of, name + "_SUPPORT_SLOW", deslash(cmake_list(support_slow))); + cmake_set_raw(*of, name + "_SUPPORT_SLOW", cmake_list(support_slow)); *of << "# Generated support classes, fast-path, compile with highest optimization\n"; - cmake_set_raw(*of, name + "_SUPPORT_FAST", deslash(cmake_list(support_fast))); + cmake_set_raw(*of, name + "_SUPPORT_FAST", cmake_list(support_fast)); *of << "# All dependencies\n"; - cmake_set_raw(*of, name + "_DEPS", deslash(cmake_list(V3File::getAllDeps()))); + cmake_set_raw(*of, name + "_DEPS", cmake_list(V3File::getAllDeps())); *of << "# User .cpp files (from .cpp's on Verilator command line)\n"; - cmake_set_raw(*of, name + "_USER_CLASSES", deslash(cmake_list(v3Global.opt.cppFiles()))); + cmake_set_raw(*of, name + "_USER_CLASSES", cmake_list(v3Global.opt.cppFiles())); if (const V3HierBlockPlan* const planp = v3Global.hierPlanp()) { *of << "# Verilate hierarchical blocks\n"; // Sorted hierarchical blocks in order of leaf-first. @@ -221,10 +212,10 @@ class CMakeEmitter final { } *of << "verilate(" << prefix << " PREFIX " << prefix << " TOP_MODULE " << hblockp->modp()->name() << " DIRECTORY " - << deslash(v3Global.opt.makeDir() + "/" + prefix) << " SOURCES "; + << v3Global.opt.makeDir() + "/" + prefix << " SOURCES "; for (const auto& childr : children) { *of << " " - << deslash(v3Global.opt.makeDir() + "/" + childr->hierWrapper(true)); + << v3Global.opt.makeDir() + "/" + childr->hierWrapper(true); } *of << " "; const string vFile = hblockp->vFileIfNecessary(); @@ -232,7 +223,7 @@ class CMakeEmitter final { const V3StringList& vFiles = v3Global.opt.vFiles(); for (const string& i : vFiles) *of << V3Os::filenameRealPath(i) << " "; *of << " VERILATOR_ARGS "; - *of << "-f " << deslash(hblockp->commandArgsFileName(true)) + *of << "-f " << hblockp->commandArgsFileName(true) << " -CFLAGS -fPIC" // hierarchical block will be static, but may be linked // with .so << ")\n"; @@ -240,14 +231,14 @@ class CMakeEmitter final { *of << "\n# Verilate the top module that refers to lib-create wrappers of above\n"; *of << "verilate(${TOP_TARGET_NAME} PREFIX " << v3Global.opt.prefix() << " TOP_MODULE " << v3Global.rootp()->topModulep()->name() << " DIRECTORY " - << deslash(v3Global.opt.makeDir()) << " SOURCES "; + << v3Global.opt.makeDir() << " SOURCES "; for (const auto& itr : *planp) { *of << " " - << deslash(v3Global.opt.makeDir() + "/" + itr.second->hierWrapper(true)); + << v3Global.opt.makeDir() + "/" + itr.second->hierWrapper(true); } - *of << " " << deslash(cmake_list(v3Global.opt.vFiles())); + *of << " " << cmake_list(v3Global.opt.vFiles()); *of << " VERILATOR_ARGS "; - *of << "-f " << deslash(planp->topCommandArgsFileName(true)); + *of << "-f " << planp->topCommandArgsFileName(true); *of << ")\n"; } } diff --git a/src/V3Os.cpp b/src/V3Os.cpp index 8088e120f..a0f936e4c 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -76,6 +76,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Environment string V3Os::getenvStr(const string& envvar, const string& defaultValue) { + string ret = ""; #if defined(_MSC_VER) // Note: MinGW does not offer _dupenv_s const char* envvalue = nullptr; @@ -83,17 +84,18 @@ string V3Os::getenvStr(const string& envvar, const string& defaultValue) { if (envvalue != nullptr) { const std::string result{envvalue}; free(envvalue); - return result; + ret = result; } else { - return defaultValue; + ret = defaultValue; } #else if (const char* const envvalue = getenv(envvar.c_str())) { - return envvalue; + ret = envvalue; } else { - return defaultValue; + ret = defaultValue; } #endif + return VString::escapeStringForPath(ret); } void V3Os::setenvStr(const string& envvar, const string& value, const string& why) { diff --git a/src/V3String.cpp b/src/V3String.cpp index 4b1f2f7f6..a688958e4 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -116,6 +116,19 @@ string VString::quoteStringLiteralForShell(const string& str) { return result; } +string VString::escapeStringForPath(const string &str) { + if (str.find(R"(\\)") != string::npos) return str; // if it has been escaped already, don't do it again + if (str.find('/') != string::npos) return str; // can be replaced by `__MINGW32__` or `_WIN32` + string result; + const char space = ' '; // escape space like this `Program Files` + const char escape = '\\'; + for (const char c: str) { + if (c == space || c == escape) result.push_back(escape); + result.push_back(c); + } + return result; +} + string VString::spaceUnprintable(const string& str) { string out; for (const char c : str) { diff --git a/src/V3String.h b/src/V3String.h index e004347f5..5cbd32402 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -100,6 +100,9 @@ public: static string quotePercent(const string& str) { return quoteAny(str, '%', '%'); } // Surround a raw string by double quote and escape if necessary // e.g. input abc's becomes "\"abc\'s\"" + static string escapeStringForPath(const string& str); + // Escape path in Windows + // e.g. input `C:\Program Files\My Program\My Program.exe` becomes `C:\\Program\ Files\\My\ Program\\My\ Program.exe` static string quoteStringLiteralForShell(const string& str); // Replace any unprintable with space // This includes removing tabs, so column tracking is correct diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 536c523da..febe42703 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -737,7 +737,7 @@ int main(int argc, char** argv, char** /*env*/) { V3PreShell::boot(); // Command option parsing - v3Global.opt.buildDepBin(argv[0]); + v3Global.opt.buildDepBin(VString::escapeStringForPath(argv[0])); const string argString = V3Options::argString(argc - 1, argv + 1); v3Global.opt.parseOpts(new FileLine{FileLine::commandLineFilename()}, argc - 1, argv + 1);