mirror of https://github.com/llvm/circt.git
[Debug] Make paths in HGLDD files relative (#6451)
Make paths emitted into HGLDD companion files relative to the file itself, or emit absolute paths. For example, the `foo/bar/hello.dd` companion file to `foo/bar/hello.sv` will now refer to the SV file as `hello.sv`, and to a `src/input.scala` file as `../../src/input.scala`. If paths cannot be made relative, for example because the output directory contains `../` components that cannot be handled, the paths are made absolute. This fixes an issue where beforehand a tool consuming the HGLDD file would have to know the working directory in which `firtool` was run to create the HGLDD file. Now the files are self-contained, and paths are anchored at the HGLDD file itself.
This commit is contained in:
parent
37b25335b9
commit
fd9392d31e
|
@ -115,6 +115,51 @@ struct DenseMapInfo<JValue> {
|
|||
};
|
||||
} // namespace llvm
|
||||
|
||||
/// Make the given `path` relative to the `relativeTo` path and store the result
|
||||
/// in `relativePath`. Returns whether the conversion was successful. Fails if
|
||||
/// the `relativeTo` path has a longer prefix of `../` than `path`, or if it
|
||||
/// contains any non-prefix `../` components. Does not clear `relativePath`
|
||||
/// before appending to it.
|
||||
static bool makePathRelative(StringRef path, StringRef relativeTo,
|
||||
SmallVectorImpl<char> &relativePath) {
|
||||
using namespace llvm::sys;
|
||||
auto sourceIt = path::begin(path);
|
||||
auto outputIt = path::begin(relativeTo);
|
||||
auto sourceEnd = path::end(path);
|
||||
auto outputEnd = path::end(relativeTo);
|
||||
|
||||
// Strip common prefix:
|
||||
// - (), () -> (), ()
|
||||
// - (a/b/c/d), (a/b/e/f) -> (c/d), (e/f)
|
||||
// - (a/b), (a/b/c/d) -> (), (c/d)
|
||||
// - (../a/b), (../a/c) -> (b), (c)
|
||||
while (outputIt != outputEnd && sourceIt != sourceEnd &&
|
||||
*outputIt == *sourceIt) {
|
||||
++outputIt;
|
||||
++sourceIt;
|
||||
}
|
||||
|
||||
// For every component in the output path insert a `../` into the source
|
||||
// path. Abort if the output path contains a `../`, because we don't
|
||||
// know where that climbs out to. Consider the changes to the following
|
||||
// output-source pairs as an example:
|
||||
//
|
||||
// - (a/b), (c/d) -> (), (../../c/d)
|
||||
// - (), (a/b) -> (), (a/b)
|
||||
// - (../a), (c/d) -> (../a), (c/d)
|
||||
// - (a/../b), (c/d) -> (../b), (../c/d)
|
||||
for (; outputIt != outputEnd && *outputIt != ".."; ++outputIt)
|
||||
path::append(relativePath, "..");
|
||||
for (; sourceIt != sourceEnd; ++sourceIt)
|
||||
path::append(relativePath, *sourceIt);
|
||||
|
||||
// If there are no more remaining components in the output path, we were
|
||||
// successfully able to translate them into `..` in the source path.
|
||||
// Otherwise the `relativeTo` path contained `../` components that we could
|
||||
// not handle.
|
||||
return outputIt == outputEnd;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// HGLDD File Emission
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -192,7 +237,7 @@ struct FileEmitter {
|
|||
SmallVector<DIModule *> modules;
|
||||
SmallString<64> outputFileName;
|
||||
StringAttr hdlFile;
|
||||
SmallMapVector<StringAttr, unsigned, 8> sourceFiles;
|
||||
SmallMapVector<StringAttr, std::pair<StringAttr, unsigned>, 8> sourceFiles;
|
||||
Namespace objectNamespace;
|
||||
SmallMapVector<JValue, StringRef, 8> structDefs;
|
||||
SmallString<128> structNameHint;
|
||||
|
@ -205,32 +250,7 @@ struct FileEmitter {
|
|||
void emitVariable(JOStream &json, DIVariable *variable);
|
||||
EmittedExpr emitExpression(Value value);
|
||||
|
||||
/// Get a numeric index for the given `sourceFile`. Populates `sourceFiles`
|
||||
/// with a unique ID assignment for each source file.
|
||||
unsigned getSourceFile(StringAttr sourceFile, bool emitted) {
|
||||
// Apply the source file prefix if this is a source file (emitted = false).
|
||||
if (!emitted && !options->sourceFilePrefix.empty() &&
|
||||
!llvm::sys::path::is_absolute(sourceFile.getValue())) {
|
||||
SmallString<64> buffer;
|
||||
buffer = options->sourceFilePrefix;
|
||||
llvm::sys::path::append(buffer, sourceFile.getValue());
|
||||
sourceFile = StringAttr::get(sourceFile.getContext(), buffer);
|
||||
}
|
||||
|
||||
// Apply the output file prefix if this is an outpu file (emitted = true).
|
||||
if (emitted && !options->outputFilePrefix.empty() &&
|
||||
!llvm::sys::path::is_absolute(sourceFile.getValue())) {
|
||||
SmallString<64> buffer;
|
||||
buffer = options->outputFilePrefix;
|
||||
llvm::sys::path::append(buffer, sourceFile.getValue());
|
||||
sourceFile = StringAttr::get(sourceFile.getContext(), buffer);
|
||||
}
|
||||
|
||||
auto &slot = sourceFiles[sourceFile];
|
||||
if (slot == 0)
|
||||
slot = sourceFiles.size();
|
||||
return slot;
|
||||
}
|
||||
unsigned getSourceFile(StringAttr sourceFile, bool emitted);
|
||||
|
||||
FileLineColLoc findBestLocation(Location loc, bool emitted) {
|
||||
return ::findBestLocation(loc, emitted, options->onlyExistingFileLocs);
|
||||
|
@ -314,6 +334,82 @@ struct FileEmitter {
|
|||
|
||||
} // namespace
|
||||
|
||||
/// Get a numeric index for the given `sourceFile`. Populates `sourceFiles`
|
||||
/// with a unique ID assignment for each source file.
|
||||
unsigned FileEmitter::getSourceFile(StringAttr sourceFile, bool emitted) {
|
||||
using namespace llvm::sys;
|
||||
|
||||
// Check if we have already allocated an ID for this source file. If we
|
||||
// have, return it. Otherwise, assign a new ID and normalize the path
|
||||
// according to HGLDD requirements.
|
||||
auto &slot = sourceFiles[sourceFile];
|
||||
if (slot.first)
|
||||
return slot.second;
|
||||
slot.second = sourceFiles.size();
|
||||
|
||||
// If the source file is an absolute path, simply use that unchanged.
|
||||
if (path::is_absolute(sourceFile.getValue())) {
|
||||
slot.first = sourceFile;
|
||||
return slot.second;
|
||||
}
|
||||
|
||||
// If specified, apply the output file prefix if this is an output file
|
||||
// (`emitted` is true), or the source file prefix if this is a source file
|
||||
// (`emitted` is false).
|
||||
StringRef filePrefix =
|
||||
emitted ? options->outputFilePrefix : options->sourceFilePrefix;
|
||||
if (!filePrefix.empty()) {
|
||||
SmallString<64> buffer = filePrefix;
|
||||
path::append(buffer, sourceFile.getValue());
|
||||
slot.first = StringAttr::get(sourceFile.getContext(), buffer);
|
||||
return slot.second;
|
||||
}
|
||||
|
||||
// Otherwise make the path relative to the HGLDD output file.
|
||||
|
||||
// Remove any `./` and `../` inside the path. This has also been applied
|
||||
// to the `outputFileName`. As a result, both paths start with zero or
|
||||
// more `../`, followed by the rest of the path without any `./` or `../`.
|
||||
SmallString<64> sourcePath = sourceFile.getValue();
|
||||
path::remove_dots(sourcePath, true);
|
||||
|
||||
// If the output file is also relative, try to determine the relative path
|
||||
// between them directly.
|
||||
StringRef relativeToDir = path::parent_path(outputFileName);
|
||||
if (!path::is_absolute(outputFileName)) {
|
||||
SmallString<64> buffer;
|
||||
if (makePathRelative(sourcePath, relativeToDir, buffer)) {
|
||||
slot.first = StringAttr::get(sourceFile.getContext(), buffer);
|
||||
return slot.second;
|
||||
}
|
||||
}
|
||||
|
||||
// If the above failed, try to make the output and source paths absolute and
|
||||
// retry computing a relative path. Only do this if conversion to absolute
|
||||
// paths is successful for both paths, and if the resulting paths have at
|
||||
// least the first path component in common. This prevents computing a
|
||||
// relative path between `/home/foo/bar` and `/tmp/baz/noob` as
|
||||
// `../../../tmp/baz/noob`.
|
||||
SmallString<64> outputPath = relativeToDir;
|
||||
fs::make_absolute(sourcePath);
|
||||
fs::make_absolute(outputPath);
|
||||
if (path::is_absolute(sourcePath) && path::is_absolute(outputPath)) {
|
||||
auto firstSourceComponent = *path::begin(path::relative_path(sourcePath));
|
||||
auto firstOutputComponent = *path::begin(path::relative_path(outputPath));
|
||||
if (firstSourceComponent == firstOutputComponent) {
|
||||
SmallString<64> buffer;
|
||||
if (makePathRelative(sourcePath, outputPath, buffer)) {
|
||||
slot.first = StringAttr::get(sourceFile.getContext(), buffer);
|
||||
return slot.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise simply use the absolute source file path.
|
||||
slot.first = StringAttr::get(sourceFile.getContext(), sourcePath);
|
||||
return slot.second;
|
||||
}
|
||||
|
||||
void FileEmitter::emit(llvm::raw_ostream &os) {
|
||||
JOStream json(os, 2);
|
||||
emit(json);
|
||||
|
@ -346,8 +442,8 @@ void FileEmitter::emit(JOStream &json) {
|
|||
json.attributeObject("HGLDD", [&] {
|
||||
json.attribute("version", "1.0");
|
||||
json.attributeArray("file_info", [&] {
|
||||
for (auto [file, index] : sourceFiles)
|
||||
json.value(file.getValue());
|
||||
for (auto [key, fileAndId] : sourceFiles)
|
||||
json.value(fileAndId.first.getValue());
|
||||
});
|
||||
if (hdlFileIndex)
|
||||
json.attribute("hdl_file_index", *hdlFileIndex);
|
||||
|
@ -832,6 +928,7 @@ Emitter::Emitter(Operation *module, const EmitHGLDDOptions &options)
|
|||
else
|
||||
llvm::sys::path::append(emitter.outputFileName, fileName);
|
||||
llvm::sys::path::replace_extension(emitter.outputFileName, "dd");
|
||||
llvm::sys::path::remove_dots(emitter.outputFileName, true);
|
||||
files.push_back(std::move(emitter));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue