From b7af214b0bc52080199c31d81abce37e6fd5a4ca Mon Sep 17 00:00:00 2001
From: Roland Coeurjoly <rolandcoeurjoly@gmail.com>
Date: Fri, 30 Aug 2024 19:12:30 +0200
Subject: [PATCH] Add relative path resolution support for script I/O
 operations

---
 kernel/yosys.cc                               | 26 +++++++++++++++++--
 tests/various/script_relative_path.ys         |  1 +
 .../script_relative_path_files/.gitignore     |  1 +
 .../script_relative_path_files/design.v       |  3 +++
 .../relative_path_read_write_operations.ys    | 16 ++++++++++++
 5 files changed, 45 insertions(+), 2 deletions(-)
 create mode 100644 tests/various/script_relative_path.ys
 create mode 100644 tests/various/script_relative_path_files/.gitignore
 create mode 100644 tests/various/script_relative_path_files/design.v
 create mode 100644 tests/various/script_relative_path_files/relative_path_read_write_operations.ys

diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index efdc54b796c..db8cda6b487 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -74,7 +74,9 @@
 #include <errno.h>
 
 #include "libs/json11/json11.hpp"
-
+#if !defined(__wasm)
+#include <filesystem>
+#endif
 YOSYS_NAMESPACE_BEGIN
 
 int autoidx = 1;
@@ -712,6 +714,20 @@ void rewrite_filename(std::string &filename)
 	if (filename.compare(0, 2, "~/") == 0)
 		filename = filename.replace(0, 1, getenv("HOME"));
 #endif
+#if !defined(__wasm)
+	if (yosys_get_design()->scratchpad_get_bool("script.relative_path_resolution_enabled")) {
+		std::filesystem::path file_path(filename);
+		if (file_path.is_relative()) {
+			const std::string script_dir = yosys_get_design()->scratchpad_get_string("script.dir", "");
+			if (!script_dir.empty()) {
+				std::filesystem::path script_dir_path(script_dir);
+				// Use std::filesystem::path operator/= for portable path concatenation
+				file_path = script_dir_path / file_path;
+				filename = file_path.string();
+			}
+		}
+	}
+#endif
 }
 
 #ifdef YOSYS_ENABLE_TCL
@@ -1193,7 +1209,13 @@ bool run_frontend(std::string filename, std::string command, RTLIL::Design *desi
 
 		FILE *backup_script_file = Frontend::current_script_file;
 		Frontend::current_script_file = f;
-
+#if !defined(__wasm)
+		const std::filesystem::path input_file_path(filename);
+		const std::filesystem::path script_dir = input_file_path.parent_path();
+		if (!script_dir.string().empty()) {
+			yosys_get_design()->scratchpad_set_string("script.dir", script_dir.string());
+		}
+#endif
 		try {
 			std::string command;
 			while (fgetline(f, command)) {
diff --git a/tests/various/script_relative_path.ys b/tests/various/script_relative_path.ys
new file mode 100644
index 00000000000..78f2175abc2
--- /dev/null
+++ b/tests/various/script_relative_path.ys
@@ -0,0 +1 @@
+script script_relative_path_files/relative_path_read_write_operations.ys
\ No newline at end of file
diff --git a/tests/various/script_relative_path_files/.gitignore b/tests/various/script_relative_path_files/.gitignore
new file mode 100644
index 00000000000..1969f8d71a8
--- /dev/null
+++ b/tests/various/script_relative_path_files/.gitignore
@@ -0,0 +1 @@
+/*.il
\ No newline at end of file
diff --git a/tests/various/script_relative_path_files/design.v b/tests/various/script_relative_path_files/design.v
new file mode 100644
index 00000000000..4629b501175
--- /dev/null
+++ b/tests/various/script_relative_path_files/design.v
@@ -0,0 +1,3 @@
+module top(input a, b, output o);
+assign o = a & b;
+endmodule
diff --git a/tests/various/script_relative_path_files/relative_path_read_write_operations.ys b/tests/various/script_relative_path_files/relative_path_read_write_operations.ys
new file mode 100644
index 00000000000..cfcbef322a9
--- /dev/null
+++ b/tests/various/script_relative_path_files/relative_path_read_write_operations.ys
@@ -0,0 +1,16 @@
+scratchpad -set script.relative_path_resolution_enabled 1
+read_verilog design.v
+design -reset
+
+read_verilog ./design.v
+design -reset
+
+read_verilog ../script_relative_path_files/design.v
+write_rtlil design.il
+design -reset
+read_rtlil design.il
+design -reset
+
+scratchpad -set script.relative_path_resolution_enabled 0
+logger -expect error "Can't open input file `design.v' for reading: No such file or directory" 1
+read_verilog design.v
\ No newline at end of file