parent
be45a9b7d5
commit
229ce1aecf
|
|
@ -636,7 +636,7 @@ public:
|
|||
puts("vlSymsp->_traceDumpOpen();\n");
|
||||
} else {
|
||||
puts("VL_PRINTF_MT(\"-Info: ");
|
||||
puts(protect(nodep->fileline()->filename()));
|
||||
puts(V3OutFormatter::quoteNameControls(protect(nodep->fileline()->filename())));
|
||||
puts(":");
|
||||
puts(cvtToStr(nodep->fileline()->lineno()));
|
||||
puts(": $dumpvar ignored, as Verilated without --trace");
|
||||
|
|
|
|||
|
|
@ -43,15 +43,11 @@ class CMakeEmitter final {
|
|||
template <typename List>
|
||||
static string cmake_list(const List& strs) {
|
||||
string s;
|
||||
if (strs.begin() != strs.end()) {
|
||||
s.append("\"");
|
||||
s.append(VString::quoteAny(*strs.begin(), '"', '\\'));
|
||||
s.append("\"");
|
||||
for (typename List::const_iterator it = ++strs.begin(); it != strs.end(); ++it) {
|
||||
s.append(" \"");
|
||||
s.append(VString::quoteAny(*it, '"', '\\'));
|
||||
s.append("\"");
|
||||
}
|
||||
for (auto it = strs.begin(); it != strs.end(); ++it) {
|
||||
s += '"';
|
||||
s += V3OutFormatter::quoteNameControls(*it);
|
||||
s += '"';
|
||||
if (it != strs.end()) s += ' ';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
|
@ -63,13 +59,13 @@ class CMakeEmitter final {
|
|||
static void cmake_set_raw(std::ofstream& of, const string& name, const string& raw_value,
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
of << "set(" << name << " " << raw_value;
|
||||
if (!cache_type.empty()) of << " CACHE " << cache_type << " \"" << docstring << "\"";
|
||||
if (!cache_type.empty()) of << " CACHE " << cache_type << " \"" << docstring << '"';
|
||||
of << ")\n";
|
||||
}
|
||||
|
||||
static void cmake_set(std::ofstream& of, const string& name, const string& value,
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
const string raw_value = "\"" + value + "\"";
|
||||
const string raw_value = '"' + value + '"';
|
||||
cmake_set_raw(of, name, raw_value, cache_type, docstring);
|
||||
}
|
||||
|
||||
|
|
@ -88,9 +84,10 @@ class CMakeEmitter final {
|
|||
*of << "# which becomes available after executing `find_package(verilator).\n";
|
||||
|
||||
*of << "\n### Constants...\n";
|
||||
cmake_set(*of, "PERL", V3Options::getenvPERL(), "FILEPATH",
|
||||
"Perl executable (from $PERL)");
|
||||
cmake_set(*of, "VERILATOR_ROOT", V3Options::getenvVERILATOR_ROOT(), "PATH",
|
||||
cmake_set(*of, "PERL", V3OutFormatter::quoteNameControls(V3Options::getenvPERL()),
|
||||
"FILEPATH", "Perl executable (from $PERL)");
|
||||
cmake_set(*of, "VERILATOR_ROOT",
|
||||
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
|
||||
"Path to Verilator kit (from $VERILATOR_ROOT)");
|
||||
|
||||
*of << "\n### Compiler flags...\n";
|
||||
|
|
|
|||
|
|
@ -154,9 +154,10 @@ public:
|
|||
}
|
||||
of.puts("\n### Constants...\n");
|
||||
of.puts("# Perl executable (from $PERL)\n");
|
||||
of.puts("PERL = " + V3Options::getenvPERL() + "\n");
|
||||
of.puts("PERL = " + V3OutFormatter::quoteNameControls(V3Options::getenvPERL()) + "\n");
|
||||
of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n");
|
||||
of.puts("VERILATOR_ROOT = " + V3Options::getenvVERILATOR_ROOT() + "\n");
|
||||
of.puts("VERILATOR_ROOT = "
|
||||
+ V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()) + "\n");
|
||||
of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n");
|
||||
of.puts(string{"SYSTEMC_INCLUDE ?= "} + V3Options::getenvSYSTEMC_INCLUDE() + "\n");
|
||||
of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n");
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
// clang-format off
|
||||
#include "V3Error.h"
|
||||
#include "V3Os.h"
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
# include "V3Ast.h"
|
||||
# include "V3Global.h"
|
||||
|
|
@ -262,13 +263,8 @@ void V3Error::init() {
|
|||
|
||||
string V3Error::lineStr(const char* filename, int lineno) VL_PURE {
|
||||
std::ostringstream out;
|
||||
const char* const fnslashp = std::strrchr(filename, '/');
|
||||
if (fnslashp) filename = fnslashp + 1;
|
||||
out << filename << ":" << std::dec << lineno << ":";
|
||||
const char* const spaces = " ";
|
||||
size_t numsp = out.str().length();
|
||||
if (numsp > 20) numsp = 20;
|
||||
out << (spaces + numsp);
|
||||
out << V3Os::filenameNonDir(filename) << ":" << std::dec << lineno << ":";
|
||||
out << std::string(std::max<int>(0, 20 - static_cast<int>(out.str().length())), ' ');
|
||||
return out.str();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -859,7 +859,7 @@ void V3OutFormatter::putcNoTracking(char chr) {
|
|||
string V3OutFormatter::quoteNameControls(const string& namein,
|
||||
V3OutFormatter::Language lang) VL_PURE {
|
||||
// Encode control chars into output-appropriate escapes
|
||||
// Reverse is V3Parse::deQuote
|
||||
// Reverse is VString::unquoteSVString
|
||||
string out;
|
||||
if (lang == LA_XML) {
|
||||
// Encode chars into XML string
|
||||
|
|
|
|||
|
|
@ -203,63 +203,62 @@ string FileLine::xmlDetailedLocation() const {
|
|||
}
|
||||
|
||||
string FileLine::lineDirectiveStrg(int enterExit) const {
|
||||
return std::string{"`line "} + cvtToStr(lastLineno()) + " \"" + filename() + "\" "
|
||||
+ cvtToStr(enterExit) + "\n";
|
||||
return std::string{"`line "} + cvtToStr(lastLineno()) + " \""
|
||||
+ V3OutFormatter::quoteNameControls(filename()) + "\" " + cvtToStr(enterExit) + "\n";
|
||||
}
|
||||
|
||||
void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
||||
// Handle `line directive
|
||||
// Does not parse streamNumber/streamLineno as the next input token
|
||||
// will come from the same stream as the previous line.
|
||||
do {
|
||||
int lineNo;
|
||||
// Skip `line
|
||||
while (*textp && std::isspace(*textp)) ++textp;
|
||||
while (*textp && !std::isspace(*textp)) ++textp;
|
||||
while (*textp && std::isspace(*textp)) ++textp;
|
||||
|
||||
// Skip `line
|
||||
while (*textp && std::isspace(*textp)) ++textp;
|
||||
while (*textp && !std::isspace(*textp)) ++textp;
|
||||
while (*textp && (std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
// Grab linenumber
|
||||
const char* const ln = textp;
|
||||
while (*textp && !std::isspace(*textp)) ++textp;
|
||||
if (0 == strncmp(ln, "`__LINE__", textp - ln)) {
|
||||
// Special case - see docs - don't change other than accounting for `line itself
|
||||
lineNo = lineno() + 1;
|
||||
} else if (std::isdigit(*ln)) {
|
||||
lineNo = std::atoi(ln);
|
||||
} else {
|
||||
break; // Fail
|
||||
}
|
||||
lineno(lineNo);
|
||||
while (*textp && (std::isspace(*textp))) ++textp;
|
||||
|
||||
// Grab linenumber
|
||||
bool fail = false;
|
||||
const char* const ln = textp;
|
||||
while (*textp && !std::isspace(*textp)) ++textp;
|
||||
if (0 == strncmp(ln, "`__LINE__", strlen("`__LINE__"))) {
|
||||
// Special case - see docs - don't change other than accounting for `line itself
|
||||
lineno(lineno() + 1);
|
||||
}
|
||||
if (std::isdigit(*ln)) {
|
||||
lineno(std::atoi(ln));
|
||||
} else {
|
||||
fail = true;
|
||||
}
|
||||
while (*textp && (std::isspace(*textp))) ++textp;
|
||||
if (*textp != '"') fail = true;
|
||||
while (*textp && (std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
// Grab filename
|
||||
if (*textp != '"') break; // Fail
|
||||
const char* const fn = ++textp;
|
||||
while (*textp && *textp != '"') ++textp;
|
||||
if (*textp != '"') break; // Fail
|
||||
string errMsg;
|
||||
const string& parsedFilename = VString::unquoteSVString(string{fn, textp}, errMsg);
|
||||
if (!errMsg.empty()) this->v3error(errMsg.c_str());
|
||||
filename(parsedFilename);
|
||||
++textp;
|
||||
while (*textp && std::isspace(*textp)) ++textp;
|
||||
|
||||
// Grab filename
|
||||
const char* const fn = textp;
|
||||
while (*textp && !(std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
if (textp != fn) {
|
||||
string strfn = fn;
|
||||
strfn = strfn.substr(0, textp - fn);
|
||||
filename(strfn);
|
||||
} else {
|
||||
fail = true;
|
||||
}
|
||||
// Grab level
|
||||
if (!std::isdigit(*textp)) break; // Fail
|
||||
const int level = std::atoi(textp);
|
||||
if (level < 0 || level >= 3) break; // Fail
|
||||
/// TODO: store lineno/filename only when the `line directive is valid
|
||||
/// lineno(lineNo);
|
||||
/// filename(filenameNew);
|
||||
enterExitRef = level;
|
||||
return;
|
||||
} while (false);
|
||||
|
||||
// Grab level
|
||||
while (*textp && (std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
if (std::isdigit(*textp)) {
|
||||
enterExitRef = std::atoi(textp);
|
||||
if (enterExitRef >= 3) fail = true;
|
||||
} else {
|
||||
enterExitRef = 0;
|
||||
fail = true;
|
||||
}
|
||||
|
||||
if (fail && v3Global.opt.pedantic()) {
|
||||
v3error("`line was not properly formed with '`line number \"filename\" level'\n");
|
||||
}
|
||||
|
||||
// printf ("PPLINE %d '%s'\n", s_lineno, s_filename.c_str());
|
||||
// Fail
|
||||
// TODO: show correct place of the code
|
||||
v3error("`line was not properly formed with '`line number \"filename\" level'\n");
|
||||
enterExitRef = 0;
|
||||
}
|
||||
|
||||
void FileLine::forwardToken(const char* textp, size_t size, bool trackLines) {
|
||||
|
|
@ -293,12 +292,7 @@ FileLine* FileLine::copyOrSameFileLine() {
|
|||
|
||||
string FileLine::filebasename() const VL_MT_SAFE { return V3Os::filenameNonDir(filename()); }
|
||||
|
||||
string FileLine::filebasenameNoExt() const {
|
||||
string name = filebasename();
|
||||
string::size_type pos;
|
||||
if ((pos = name.find('.')) != string::npos) name = name.substr(0, pos);
|
||||
return name;
|
||||
}
|
||||
string FileLine::filebasenameNoExt() const { return V3Os::filenameNonDirExt(filename()); }
|
||||
|
||||
string FileLine::firstColumnLetters() const VL_MT_SAFE {
|
||||
const char a = ((firstColumn() / 26) % 26) + 'a';
|
||||
|
|
|
|||
|
|
@ -81,19 +81,21 @@ public:
|
|||
|
||||
// ACCESSOR METHODS
|
||||
void addIncDirUser(const string& incdir) {
|
||||
const auto itFoundPair = m_incDirUserSet.insert(incdir);
|
||||
const string& dir = V3Os::filenameCleanup(incdir);
|
||||
const auto itFoundPair = m_incDirUserSet.insert(dir);
|
||||
if (itFoundPair.second) {
|
||||
// cppcheck-suppress stlFindInsert // cppcheck 1.90 bug
|
||||
m_incDirUsers.push_back(incdir);
|
||||
m_incDirFallbacks.remove(incdir); // User has priority over Fallback
|
||||
m_incDirFallbackSet.erase(incdir); // User has priority over Fallback
|
||||
m_incDirUsers.push_back(dir);
|
||||
m_incDirFallbacks.remove(dir); // User has priority over Fallback
|
||||
m_incDirFallbackSet.erase(dir); // User has priority over Fallback
|
||||
}
|
||||
}
|
||||
void addIncDirFallback(const string& incdir) {
|
||||
if (m_incDirUserSet.find(incdir)
|
||||
const string& dir = V3Os::filenameCleanup(incdir);
|
||||
if (m_incDirUserSet.find(dir)
|
||||
== m_incDirUserSet.end()) { // User has priority over Fallback
|
||||
const auto itFoundPair = m_incDirFallbackSet.insert(incdir);
|
||||
if (itFoundPair.second) m_incDirFallbacks.push_back(incdir);
|
||||
const auto itFoundPair = m_incDirFallbackSet.insert(dir);
|
||||
if (itFoundPair.second) m_incDirFallbacks.push_back(dir);
|
||||
}
|
||||
}
|
||||
void addLangExt(const string& langext, const V3LangCode& lc) {
|
||||
|
|
@ -508,20 +510,16 @@ string V3Options::fileExists(const string& filename) {
|
|||
return ""; // Not found
|
||||
}
|
||||
// Check if it is a directory, ignore if so
|
||||
string filenameOut = V3Os::filenameFromDirBase(dir, basename);
|
||||
string filenameOut = V3Os::filenameJoin(dir, basename);
|
||||
if (!fileStatNormal(filenameOut)) return ""; // Directory
|
||||
return filenameOut;
|
||||
}
|
||||
|
||||
string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) {
|
||||
for (const string& i : m_impp->m_libExtVs) {
|
||||
const string fn = V3Os::filenameFromDirBase(dirname, modname + i);
|
||||
const string fn = V3Os::filenameJoin(dirname, modname + i);
|
||||
string exists = fileExists(fn);
|
||||
if (exists != "") {
|
||||
// Strip ./, it just looks ugly
|
||||
if (exists.substr(0, 2) == "./") exists.erase(0, 2);
|
||||
return exists;
|
||||
}
|
||||
if (exists != "") return exists;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
@ -548,29 +546,30 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la
|
|||
// Find a filename to read the specified module name,
|
||||
// using the incdir and libext's.
|
||||
// Return "" if not found.
|
||||
if (!V3Os::filenameIsRel(modname)) {
|
||||
// modname is an absolute path, so can find getStdPackagePath()
|
||||
string exists = filePathCheckOneDir(modname, "");
|
||||
const string filename = V3Os::filenameCleanup(modname);
|
||||
if (!V3Os::filenameIsRel(filename)) {
|
||||
// filename is an absolute path, so can find getStdPackagePath()
|
||||
string exists = filePathCheckOneDir(filename, "");
|
||||
if (exists != "") return exists;
|
||||
}
|
||||
for (const string& dir : m_impp->m_incDirUsers) {
|
||||
string exists = filePathCheckOneDir(modname, dir);
|
||||
string exists = filePathCheckOneDir(filename, dir);
|
||||
if (exists != "") return exists;
|
||||
}
|
||||
for (const string& dir : m_impp->m_incDirFallbacks) {
|
||||
string exists = filePathCheckOneDir(modname, dir);
|
||||
string exists = filePathCheckOneDir(filename, dir);
|
||||
if (exists != "") return exists;
|
||||
}
|
||||
|
||||
if (m_relativeIncludes) {
|
||||
const string exists = filePathCheckOneDir(modname, lastpath);
|
||||
const string exists = filePathCheckOneDir(filename, lastpath);
|
||||
if (exists != "") return V3Os::filenameRealPath(exists);
|
||||
}
|
||||
|
||||
// Warn and return not found
|
||||
if (errmsg != "") {
|
||||
fl->v3error(errmsg + modname);
|
||||
filePathLookedMsg(fl, modname);
|
||||
fl->v3error(errmsg + filename);
|
||||
filePathLookedMsg(fl, filename);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
@ -592,13 +591,13 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
|
|||
std::cerr << V3Error::warnMoreStandalone() << "... Looked in:" << endl;
|
||||
for (const string& dir : m_impp->m_incDirUsers) {
|
||||
for (const string& ext : m_impp->m_libExtVs) {
|
||||
const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
|
||||
const string fn = V3Os::filenameJoin(dir, modname + ext);
|
||||
std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl;
|
||||
}
|
||||
}
|
||||
for (const string& dir : m_impp->m_incDirFallbacks) {
|
||||
for (const string& ext : m_impp->m_libExtVs) {
|
||||
const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
|
||||
const string fn = V3Os::filenameJoin(dir, modname + ext);
|
||||
std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl;
|
||||
}
|
||||
}
|
||||
|
|
@ -657,7 +656,7 @@ string V3Options::getenvMAKEFLAGS() { //
|
|||
}
|
||||
|
||||
string V3Options::getenvPERL() { //
|
||||
return V3Os::getenvStr("PERL", "perl");
|
||||
return V3Os::filenameCleanup(V3Os::getenvStr("PERL", "perl"));
|
||||
}
|
||||
|
||||
string V3Options::getenvSYSTEMC() {
|
||||
|
|
@ -669,7 +668,7 @@ string V3Options::getenvSYSTEMC() {
|
|||
var = defenv;
|
||||
V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time");
|
||||
}
|
||||
return var;
|
||||
return V3Os::filenameCleanup(var);
|
||||
}
|
||||
|
||||
string V3Options::getenvSYSTEMC_ARCH() {
|
||||
|
|
@ -718,9 +717,9 @@ string V3Options::getenvSYSTEMC_INCLUDE() {
|
|||
}
|
||||
if (var == "") {
|
||||
const string sc = getenvSYSTEMC();
|
||||
if (sc != "") var = sc + "/include";
|
||||
if (sc != "") var = V3Os::filenameJoin(sc, "include");
|
||||
}
|
||||
return var;
|
||||
return V3Os::filenameCleanup(var);
|
||||
}
|
||||
|
||||
string V3Options::getenvSYSTEMC_LIBDIR() {
|
||||
|
|
@ -735,9 +734,9 @@ string V3Options::getenvSYSTEMC_LIBDIR() {
|
|||
if (var == "") {
|
||||
const string sc = getenvSYSTEMC();
|
||||
const string arch = getenvSYSTEMC_ARCH();
|
||||
if (sc != "" && arch != "") var = sc + "/lib-" + arch;
|
||||
if (sc != "" && arch != "") var = V3Os::filenameJoin(sc, "lib-" + arch);
|
||||
}
|
||||
return var;
|
||||
return V3Os::filenameCleanup(var);
|
||||
}
|
||||
|
||||
string V3Options::getenvVERILATOR_ROOT() {
|
||||
|
|
@ -750,11 +749,11 @@ string V3Options::getenvVERILATOR_ROOT() {
|
|||
V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time");
|
||||
}
|
||||
if (var == "") v3fatal("$VERILATOR_ROOT needs to be in environment\n");
|
||||
return var;
|
||||
return V3Os::filenameCleanup(var);
|
||||
}
|
||||
|
||||
string V3Options::getStdPackagePath() {
|
||||
return getenvVERILATOR_ROOT() + "/include/verilated_std.sv";
|
||||
return V3Os::filenameJoin(getenvVERILATOR_ROOT(), "include", "verilated_std.sv");
|
||||
}
|
||||
|
||||
string V3Options::getSupported(const string& var) {
|
||||
|
|
@ -1890,7 +1889,8 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) VL
|
|||
|
||||
string V3Options::parseFileArg(const string& optdir, const string& relfilename) {
|
||||
string filename = V3Os::filenameSubstitute(relfilename);
|
||||
if (optdir != "." && V3Os::filenameIsRel(filename)) filename = optdir + "/" + filename;
|
||||
if (optdir != "." && V3Os::filenameIsRel(filename))
|
||||
filename = V3Os::filenameJoin(optdir, filename);
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
|
|
|||
75
src/V3Os.cpp
75
src/V3Os.cpp
|
|
@ -124,15 +124,46 @@ void V3Os::setenvStr(const string& envvar, const string& value, const string& wh
|
|||
//######################################################################
|
||||
// Generic filename utilities
|
||||
|
||||
static bool isSlash(char ch) VL_PURE { return ch == '/' || ch == '\\'; }
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
static constexpr char V3OS_SLASH = '\\';
|
||||
#else
|
||||
static constexpr char V3OS_SLASH = '/';
|
||||
#endif
|
||||
|
||||
string V3Os::filenameFromDirBase(const string& dir, const string& basename) VL_PURE {
|
||||
// Don't return ./{filename} because if filename was absolute, that makes it relative
|
||||
if (dir.empty() || dir == ".") {
|
||||
return basename;
|
||||
} else {
|
||||
return dir + "/" + basename;
|
||||
static bool isSlash(char ch) VL_PURE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
return ch == '/' || ch == '\\';
|
||||
#else
|
||||
return ch == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
string V3Os::filenameCleanup(const string& filename) VL_PURE {
|
||||
string str;
|
||||
str.reserve(filename.length());
|
||||
bool lastIsSlash = false;
|
||||
for (const char ch : filename) {
|
||||
const bool lastIsSlashOld = lastIsSlash;
|
||||
lastIsSlash = isSlash(ch);
|
||||
if (lastIsSlash && lastIsSlashOld) continue;
|
||||
str += ch;
|
||||
}
|
||||
if (str.size() > 1 && isSlash(str.back())) str.pop_back();
|
||||
while (str.size() > 2 && str[0] == '.' && isSlash(str[1])) str.erase(0, 2);
|
||||
return str;
|
||||
}
|
||||
|
||||
string V3Os::filenameJoin(std::initializer_list<const std::string> paths) VL_PURE {
|
||||
string fullpath;
|
||||
for (const auto& item : paths) {
|
||||
if (item.empty() || item == ".") {
|
||||
continue;
|
||||
} else {
|
||||
if (!fullpath.empty()) fullpath += V3OS_SLASH;
|
||||
fullpath += item;
|
||||
}
|
||||
}
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
string V3Os::filenameDir(const string& filename) VL_PURE {
|
||||
|
|
@ -144,7 +175,7 @@ string V3Os::filenameDir(const string& filename) VL_PURE {
|
|||
if (it.base() == filename.begin()) {
|
||||
return ".";
|
||||
} else {
|
||||
return {filename.begin(), (++it).base()};
|
||||
return string{filename.begin(), (++it).base()};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +195,10 @@ string V3Os::filenameNonExt(const string& filename) VL_PURE {
|
|||
return base;
|
||||
}
|
||||
|
||||
string V3Os::filenameNonDirExt(const string& filename) VL_PURE {
|
||||
return filenameNonExt(filenameNonDir(filename));
|
||||
}
|
||||
|
||||
string V3Os::filenameSubstitute(const string& filename) {
|
||||
string result;
|
||||
// cppcheck-has-bug-suppress unusedLabel
|
||||
|
|
@ -392,3 +427,27 @@ int V3Os::system(const string& command) {
|
|||
return exit_code;
|
||||
}
|
||||
}
|
||||
|
||||
void V3Os::selfTest() {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_SELFTEST(string, filenameCleanup(""), "");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("."), ".");
|
||||
UASSERT_SELFTEST(string, filenameCleanup(".."), "..");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("/"), "/");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("../"), "..");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("//"), "/");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("//."), "/.");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("./"), ".");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("././"), ".");
|
||||
UASSERT_SELFTEST(string, filenameCleanup(".///"), ".");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("a"), "a");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("a/"), "a");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("a/b"), "a/b");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("././//./a/b"), "a/b");
|
||||
UASSERT_SELFTEST(string, filenameCleanup(".//./a///"), "a");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("///a/./b///."), "/a/./b/.");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("aaa/bbb/ccc/"), "aaa/bbb/ccc");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("./aaa/bbb/ccc/"), "aaa/bbb/ccc");
|
||||
UASSERT_SELFTEST(string, filenameCleanup("../aaa/bbb/ccc/"), "../aaa/bbb/ccc");
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
29
src/V3Os.h
29
src/V3Os.h
|
|
@ -35,22 +35,28 @@ public:
|
|||
static void setenvStr(const string& envvar, const string& value, const string& why);
|
||||
|
||||
// METHODS (generic filename utilities)
|
||||
static string filenameFromDirBase(const string& dir, const string& basename) VL_PURE;
|
||||
///< Return non-directory part of filename
|
||||
///< @return concatenated path
|
||||
static string filenameJoin(std::initializer_list<const std::string> paths) VL_PURE;
|
||||
template <typename... Args>
|
||||
static string filenameJoin(Args... args) VL_PURE {
|
||||
return filenameJoin({args...});
|
||||
};
|
||||
///< @return file path without repeated separators and ./ prefix
|
||||
static string filenameCleanup(const string& filename) VL_PURE;
|
||||
///< @return non-directory part of filename
|
||||
static string filenameNonDir(const string& filename) VL_PURE;
|
||||
///< Return non-extensioned (no .) part of filename
|
||||
///< @return non-extensioned (no .) part of filename
|
||||
static string filenameNonExt(const string& filename) VL_PURE;
|
||||
///< Return basename of filename
|
||||
static string filenameNonDirExt(const string& filename) VL_PURE {
|
||||
return filenameNonExt(filenameNonDir(filename));
|
||||
}
|
||||
///< Return directory part of filename
|
||||
///< @return basename of filename
|
||||
static string filenameNonDirExt(const string& filename) VL_PURE;
|
||||
///< @return directory part of filename
|
||||
static string filenameDir(const string& filename) VL_PURE;
|
||||
/// Return filename with env vars removed
|
||||
///< @return filename with env vars removed
|
||||
static string filenameSubstitute(const string& filename);
|
||||
///< Return realpath of filename
|
||||
///< @return realpath of filename
|
||||
static string filenameRealPath(const string& filename) VL_PURE;
|
||||
static bool filenameIsRel(const string& filename) VL_PURE; ///< True if relative
|
||||
///< @return filename is relative
|
||||
static bool filenameIsRel(const string& filename) VL_PURE;
|
||||
|
||||
// METHODS (file utilities)
|
||||
static string getline(std::istream& is, char delim = '\n');
|
||||
|
|
@ -72,6 +78,7 @@ public:
|
|||
// METHODS (sub command)
|
||||
/// Run system command, returns the exit code of the child process.
|
||||
static int system(const string& command);
|
||||
static void selfTest();
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -251,63 +251,9 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name,
|
|||
return nodep;
|
||||
}
|
||||
|
||||
string V3ParseGrammar::deQuote(FileLine* fileline, string text) {
|
||||
// Fix up the quoted strings the user put in, for example "\"" becomes "
|
||||
// Reverse is V3OutFormatter::quoteNameControls(...)
|
||||
bool quoted = false;
|
||||
string newtext;
|
||||
unsigned char octal_val = 0;
|
||||
int octal_digits = 0;
|
||||
for (string::const_iterator cp = text.begin(); cp != text.end(); ++cp) {
|
||||
if (quoted) {
|
||||
if (std::isdigit(*cp)) {
|
||||
octal_val = octal_val * 8 + (*cp - '0');
|
||||
if (++octal_digits == 3) {
|
||||
octal_digits = 0;
|
||||
quoted = false;
|
||||
newtext += octal_val;
|
||||
}
|
||||
} else {
|
||||
if (octal_digits) {
|
||||
// Spec allows 1-3 digits
|
||||
octal_digits = 0;
|
||||
quoted = false;
|
||||
newtext += octal_val;
|
||||
--cp; // Backup to reprocess terminating character as non-escaped
|
||||
continue;
|
||||
}
|
||||
quoted = false;
|
||||
if (*cp == 'n') {
|
||||
newtext += '\n';
|
||||
} else if (*cp == 'a') {
|
||||
newtext += '\a'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'f') {
|
||||
newtext += '\f'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'r') {
|
||||
newtext += '\r';
|
||||
} else if (*cp == 't') {
|
||||
newtext += '\t';
|
||||
} else if (*cp == 'v') {
|
||||
newtext += '\v'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'x' && std::isxdigit(cp[1])
|
||||
&& std::isxdigit(cp[2])) { // SystemVerilog 3.1
|
||||
#define vl_decodexdigit(c) ((std::isdigit(c) ? ((c) - '0') : (std::tolower((c)) - 'a' + 10)))
|
||||
newtext
|
||||
+= static_cast<char>(16 * vl_decodexdigit(cp[1]) + vl_decodexdigit(cp[2]));
|
||||
cp += 2;
|
||||
} else if (std::isalnum(*cp)) {
|
||||
fileline->v3error("Unknown escape sequence: \\" << *cp);
|
||||
break;
|
||||
} else {
|
||||
newtext += *cp;
|
||||
}
|
||||
}
|
||||
} else if (*cp == '\\') {
|
||||
quoted = true;
|
||||
octal_digits = 0;
|
||||
} else {
|
||||
newtext += *cp;
|
||||
}
|
||||
}
|
||||
return newtext;
|
||||
string V3ParseGrammar::unquoteString(FileLine* fileline, string text) {
|
||||
string errMsg;
|
||||
string res = VString::unquoteSVString(text, errMsg);
|
||||
if (!errMsg.empty()) fileline->v3error(errMsg.c_str());
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ string V3PreProcImp::removeDefines(const string& text) {
|
|||
string rtnsym = text;
|
||||
for (int loopprevent = 0; loopprevent < 100; loopprevent++) {
|
||||
string xsym = rtnsym;
|
||||
if (xsym.substr(0, 1) == "`") xsym.replace(0, 1, "");
|
||||
if (xsym[0] == '`') xsym.erase(0, 1);
|
||||
if (defExists(xsym)) {
|
||||
val = defValue(xsym);
|
||||
if (val != rtnsym) {
|
||||
|
|
|
|||
|
|
@ -663,7 +663,8 @@ std::pair<AstVarScope*, AstNodeStmt*> makeEvalLoop(AstNetlist* netlistp, const s
|
|||
newcallp->dtypeSetVoid();
|
||||
blockp->addNodesp(newcallp->makeStmt());
|
||||
add("#endif\n");
|
||||
add("VL_FATAL_MT(\"" + file + "\", " + line + ", \"\", ");
|
||||
add("VL_FATAL_MT(\"" + V3OutFormatter::quoteNameControls(file) + "\", " + line
|
||||
+ ", \"\", ");
|
||||
add("\"" + name + " region did not converge.\");\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "V3String.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
#include "V3Global.h"
|
||||
|
|
@ -130,6 +131,70 @@ string VString::escapeStringForPath(const string& str) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static int vl_decodexdigit(char c) {
|
||||
return std::isdigit(c) ? c - '0' : std::tolower(c) - 'a' + 10;
|
||||
}
|
||||
|
||||
string VString::unquoteSVString(const string& text, string& errOut) {
|
||||
bool quoted = false;
|
||||
string newtext;
|
||||
newtext.reserve(text.size());
|
||||
unsigned char octal_val = 0;
|
||||
int octal_digits = 0;
|
||||
for (string::const_iterator cp = text.begin(); cp != text.end(); ++cp) {
|
||||
if (quoted) {
|
||||
if (std::isdigit(*cp)) {
|
||||
octal_val = octal_val * 8 + (*cp - '0');
|
||||
if (++octal_digits == 3) {
|
||||
octal_digits = 0;
|
||||
quoted = false;
|
||||
newtext += octal_val;
|
||||
}
|
||||
} else {
|
||||
if (octal_digits) {
|
||||
// Spec allows 1-3 digits
|
||||
octal_digits = 0;
|
||||
quoted = false;
|
||||
newtext += octal_val;
|
||||
--cp; // Backup to reprocess terminating character as non-escaped
|
||||
continue;
|
||||
}
|
||||
quoted = false;
|
||||
if (*cp == 'n') {
|
||||
newtext += '\n';
|
||||
} else if (*cp == 'a') {
|
||||
newtext += '\a'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'f') {
|
||||
newtext += '\f'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'r') {
|
||||
newtext += '\r';
|
||||
} else if (*cp == 't') {
|
||||
newtext += '\t';
|
||||
} else if (*cp == 'v') {
|
||||
newtext += '\v'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'x' && std::isxdigit(cp[1])
|
||||
&& std::isxdigit(cp[2])) { // SystemVerilog 3.1
|
||||
newtext
|
||||
+= static_cast<char>(16 * vl_decodexdigit(cp[1]) + vl_decodexdigit(cp[2]));
|
||||
cp += 2;
|
||||
} else if (std::isalnum(*cp)) {
|
||||
errOut = "Unknown escape sequence: \\";
|
||||
errOut += *cp;
|
||||
break;
|
||||
} else {
|
||||
newtext += *cp;
|
||||
}
|
||||
}
|
||||
} else if (*cp == '\\') {
|
||||
quoted = true;
|
||||
octal_digits = 0;
|
||||
} else {
|
||||
newtext += *cp;
|
||||
}
|
||||
}
|
||||
return newtext;
|
||||
}
|
||||
|
||||
string VString::spaceUnprintable(const string& str) VL_PURE {
|
||||
string result;
|
||||
for (const char c : str) {
|
||||
|
|
|
|||
|
|
@ -102,6 +102,9 @@ public:
|
|||
// 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);
|
||||
// Convert SV quoted string input from source code to normal form.
|
||||
// Reverse is V3OutFormatter::quoteNameControls(...)
|
||||
static string unquoteSVString(const string& text, string& errOut);
|
||||
// Escape path in Windows
|
||||
// e.g. input `C:\Program Files\My Program\My Program.exe` becomes
|
||||
// `C:\\Program\ Files\\My\ Program\\My\ Program.exe`
|
||||
|
|
|
|||
|
|
@ -642,6 +642,7 @@ static void verilate(const string& argString) {
|
|||
{
|
||||
const V3MtDisabledLockGuard mtDisabler{v3MtDisabledLock()};
|
||||
|
||||
V3Os::selfTest();
|
||||
VHashSha256::selfTest();
|
||||
VSpellCheck::selfTest();
|
||||
V3Graph::selfTest();
|
||||
|
|
@ -782,7 +783,7 @@ int main(int argc, char** argv) {
|
|||
V3PreShell::boot();
|
||||
|
||||
// Command option parsing
|
||||
v3Global.opt.buildDepBin(VString::escapeStringForPath(argv[0]));
|
||||
v3Global.opt.buildDepBin(V3Os::filenameCleanup(argv[0]));
|
||||
v3Global.opt.parseOpts(new FileLine{FileLine::commandLineFilename()}, argc - 1, argv + 1);
|
||||
|
||||
// Validate settings (aka Boost.Program_options)
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public:
|
|||
AstNode* attrsp) VL_MT_DISABLED;
|
||||
AstNode* createSupplyExpr(FileLine* fileline, const string& name, int value) VL_MT_DISABLED;
|
||||
AstText* createTextQuoted(FileLine* fileline, const string& text) {
|
||||
string newtext = deQuote(fileline, text);
|
||||
string newtext = GRAMMARP->unquoteString(fileline, text);
|
||||
return new AstText{fileline, newtext};
|
||||
}
|
||||
AstNode* createCellOrIfaceRef(FileLine* fileline, const string& name, AstPin* pinlistp,
|
||||
|
|
@ -253,7 +253,7 @@ public:
|
|||
return createArray(dtypep, rangearraysp, isPacked);
|
||||
}
|
||||
}
|
||||
string deQuote(FileLine* fileline, string text) VL_MT_DISABLED;
|
||||
string unquoteString(FileLine* fileline, string text) VL_MT_DISABLED;
|
||||
void checkDpiVer(FileLine* fileline, const string& str) {
|
||||
if (str != "DPI-C" && !v3Global.opt.bboxSys()) {
|
||||
fileline->v3error("Unsupported DPI type '" << str << "': Use 'DPI-C'");
|
||||
|
|
@ -5675,13 +5675,13 @@ parseRefBase<nodep>:
|
|||
|
||||
// yaSTRING shouldn't be used directly, instead via an abstraction below
|
||||
str<strp>: // yaSTRING but with \{escapes} need decoded
|
||||
yaSTRING { $$ = PARSEP->newString(GRAMMARP->deQuote($<fl>1, *$1)); }
|
||||
yaSTRING { $$ = PARSEP->newString(GRAMMARP->unquoteString($<fl>1, *$1)); }
|
||||
;
|
||||
|
||||
strAsInt<nodeExprp>:
|
||||
yaSTRING
|
||||
{ // Numeric context, so IEEE 1800-2017 11.10.3 "" is a "\000"
|
||||
$$ = new AstConst{$<fl>1, AstConst::VerilogStringLiteral{}, GRAMMARP->deQuote($<fl>1, *$1)}; }
|
||||
$$ = new AstConst{$<fl>1, AstConst::VerilogStringLiteral{}, GRAMMARP->unquoteString($<fl>1, *$1)}; }
|
||||
;
|
||||
|
||||
strAsIntIgnore<nodeExprp>: // strAsInt, but never matches for when expr shouldn't parse strings
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
-Info: some file:100:1: aaaaaaaa
|
||||
: ... note: In instance 't'
|
||||
100 | $info("aaaaaaaa");
|
||||
| ^~~~~
|
||||
-Info: some file:101:1: bbbbbbbb
|
||||
: ... note: In instance 't'
|
||||
101 | $info("bbbbbbbb");
|
||||
| ^~~~~
|
||||
-Info: somefile.v:200:1: cccccccc
|
||||
: ... note: In instance 't'
|
||||
200 | $info("cccccccc");
|
||||
| ^~~~~
|
||||
-Info: /a/somefile.v:300:1: dddddddd
|
||||
: ... note: In instance 't'
|
||||
300 | $info("dddddddd");
|
||||
| ^~~~~
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(linter => 1);
|
||||
|
||||
lint(
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Anthony Donlon.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
`line 100 "some file" 0
|
||||
$info("aaaaaaaa");
|
||||
$info("bbbbbbbb");
|
||||
`line 200 "somefile.v" 0
|
||||
$info("cccccccc");
|
||||
`line 300 "/a/somefile.v" 0
|
||||
$info("dddddddd");
|
||||
endmodule
|
||||
|
|
@ -1,13 +1,22 @@
|
|||
%Error: t/t_pp_line_bad.v:100:1: `line was not properly formed with '`line number "filename" level'
|
||||
100 | `line 100
|
||||
| ^
|
||||
%Error: somefile:100:1: `line was not properly formed with '`line number "filename" level'
|
||||
100 | `line 100
|
||||
%Error: t/t_pp_line_bad.v:200:1: `line was not properly formed with '`line number "filename" level'
|
||||
200 | `line 100
|
||||
| ^
|
||||
%Error: t/t_pp_line_bad.v:300:1: `line was not properly formed with '`line number "filename" level'
|
||||
300 | `line 100
|
||||
| ^
|
||||
%Error: some file:400:1: `line was not properly formed with '`line number "filename" level'
|
||||
400 | `line 100
|
||||
| ^
|
||||
%Error: somefile:500:1: `line was not properly formed with '`line number "filename" level'
|
||||
500 | `line 100
|
||||
| ^
|
||||
%Error: some file:600:1: `line was not properly formed with '`line number "filename" level'
|
||||
600 | `line 100
|
||||
| ^
|
||||
%Error: somefile:100:1: `line was not properly formed with '`line number "filename" level'
|
||||
%Error: t/t_pp_line_bad.v:7:1: Define or directive not defined: '`line'
|
||||
7 | `line
|
||||
| ^~~~~
|
||||
%Error: somefile:100:1: `line was not properly formed with '`line number "filename" level'
|
||||
t/t_pp_line_bad.v:101:1: ... note: In file included from 't_pp_line_bad.v'
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(vlt => 1);
|
||||
scenarios(linter => 1);
|
||||
|
||||
lint(
|
||||
verilator_flags2 => ["-Wpedantic"],
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
`line
|
||||
`line 100
|
||||
`line 100 somefile 1
|
||||
`line 100 "somefile"
|
||||
`line 100 "somefile" 3
|
||||
`line 200 somefile
|
||||
`line 300 "somefile 1
|
||||
`line 400 "some file"
|
||||
`line 500 "somefile" 3
|
||||
`line 600 "some file" 3
|
||||
|
|
|
|||
Loading…
Reference in New Issue