From 0e763f320b0e5babb75e20eab60bbaddb32c35e1 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 9 Jul 2026 15:54:17 +1200 Subject: [PATCH 1/2] Fix ContentListing::option source location Use a null location instead of referencing `log_help.cc`. --- kernel/log_help.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 01c9a93f6..6ba51bfa5 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -44,7 +44,7 @@ void ContentListing::usage(const string &text, void ContentListing::option(const string &text, const string &description, const source_location location) { - auto option = open_option(text); + auto option = open_option(text, source_location()); if (description.length()) option->add_content("text", description, location); } From 53d1e7615b9184fc124a4bee3b5a50ef0b77199b Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 9 Jul 2026 16:05:15 +1200 Subject: [PATCH 2/2] Fixup help contents source_file Stop empty source files `""` from being exported to json. Move source path fixing to a separate function. `ContentListing::to_json` now takes the source root, recursively calling the source path fixup function on all source paths in the content listing. --- kernel/log_help.cc | 39 ++++++++++++++++++++++++++++++++++----- kernel/log_help.h | 8 +++++++- kernel/register.cc | 20 +++++--------------- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/kernel/log_help.cc b/kernel/log_help.cc index 6ba51bfa5..36f01abcc 100644 --- a/kernel/log_help.cc +++ b/kernel/log_help.cc @@ -21,16 +21,45 @@ USING_YOSYS_NAMESPACE -Json ContentListing::to_json() const { +std::filesystem::path Yosys::fixup_source_path (const char* source_file, + const std::filesystem::path &source_root, bool* skip_source_group) +{ + std::filesystem::path source_path(source_file); + if (source_path.is_absolute()) { + // using proximate instead of relative means that we + // still get the source path if they aren't relative + auto proximate_path = std::filesystem::proximate(source_path, source_root); + if (proximate_path == proximate_path.lexically_normal()) { + // we're only interested if it's a subpath of our root dir + // if the normal form differs, then the proximate path includes ".." + return proximate_path; + } else if (skip_source_group != nullptr) { + // don't try to group external paths + *skip_source_group = true; + } + } + return source_path; +} + +Json ContentListing::to_json(const std::filesystem::path &source_root) const { Json::object object; + object["type"] = type; - if (body.length()) object["body"] = body; - if (strcmp(source_file, "unknown") != 0) object["source_file"] = source_file; - if (source_line != 0) object["source_line"] = source_line; + if (body.length()) + object["body"] = body; + + if (has_source()) + object["source_file"] = fixup_source_path(source_file, source_root).string(); + if (source_line != 0) + object["source_line"] = source_line; + object["options"] = Json(options); + Json::array content_array; - for (auto child : _content) content_array.push_back(child.to_json()); + for (auto child : _content) + content_array.push_back(child.to_json(source_root)); object["content"] = content_array; + return object; } diff --git a/kernel/log_help.h b/kernel/log_help.h index 3ce0ac7aa..6301ef7f4 100644 --- a/kernel/log_help.h +++ b/kernel/log_help.h @@ -22,9 +22,14 @@ #include "kernel/yosys_common.h" #include "kernel/json.h" +#include +#include YOSYS_NAMESPACE_BEGIN +std::filesystem::path fixup_source_path (const char* source_file, + const std::filesystem::path &source_root, bool* skip_source_group = nullptr); + class ContentListing { vector _content; public: @@ -49,6 +54,7 @@ public: _content.push_back({type, body, location}); } + bool has_source() const { return strlen(source_file) && strcmp(source_file, "unknown") != 0; } bool has_content() const { return _content.size() != 0; } vector::const_iterator begin() const { return _content.cbegin(); } @@ -90,7 +96,7 @@ public: const source_location location = source_location::current() ); - Json to_json() const; + Json to_json(const std::filesystem::path &source_root) const; }; class PrettyHelp diff --git a/kernel/register.cc b/kernel/register.cc index a88d2acc8..db42de008 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -921,20 +921,10 @@ struct HelpPass : public Pass { string source_file = pass->location.file_name(); bool has_source = source_file.compare("unknown") != 0; std::filesystem::path source_path; - auto no_source_group = false; + auto skip_source_group = false; + if (has_source) { - source_path = std::filesystem::path(pass->location.file_name()); - if (source_path.is_absolute()) { - // using proximate instead of relative means that we - // still get the source path if they aren't relative - auto proximate_path = std::filesystem::proximate(source_path, source_root); - if (proximate_path == std::filesystem::weakly_canonical(proximate_path)) - // we're only interested if it's a subpath of our root dir - source_path = proximate_path; - else - // don't try to group external paths - no_source_group = true; - } + source_path = fixup_source_path(pass->location.file_name(), source_root, &skip_source_group); source_file = source_path.string(); } @@ -947,7 +937,7 @@ struct HelpPass : public Pass { else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0)) cmd_help.group = "frontends"; else if (has_source) { - if (source_path.has_parent_path() && !no_source_group) + if (source_path.has_parent_path() && !skip_source_group) cmd_help.group = source_path.parent_path().string(); } // implicit !has_source @@ -973,7 +963,7 @@ struct HelpPass : public Pass { json.entry("title", title); json.name("content"); json.begin_array(); for (auto &content : cmd_help) - json.value(content.to_json()); + json.value(content.to_json(source_root)); json.end_array(); json.entry("group", cmd_help.group); json.entry("source_file", source_file);