From 38f9e6c3a2dd09f46508e3fba1c69238ddc8d9f4 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Tue, 27 Aug 2024 08:50:10 +0300 Subject: [PATCH 1/4] -y flag for libyosys Python scripts This adds a Python equivalent to the `-c` option, where scripts importing `libyosys` can be imported and used. Most of the work for this was already done to enable Python passes a couple years back, so this is a relatively small changeset. --- kernel/driver.cc | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index 1e4cd0052..bd32872a2 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -241,6 +241,7 @@ int main(int argc, char **argv) std::string topmodule = ""; std::string perffile = ""; bool scriptfile_tcl = false; + bool scriptfile_python = false; bool print_banner = true; bool print_stats = true; bool call_abort = false; @@ -305,6 +306,11 @@ int main(int argc, char **argv) printf("\n"); printf(" -C\n"); printf(" enters TCL interatcive shell mode\n"); +#endif +#ifdef WITH_PYTHON + printf("\n"); + printf(" -y python_scriptfile\n"); + printf(" execute the python script"); #endif printf("\n"); printf(" -p command\n"); @@ -379,7 +385,7 @@ int main(int argc, char **argv) } int opt; - while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:B:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:y:W:w:e:r:D:P:E:x:B:")) != -1) { switch (opt) { @@ -464,11 +470,19 @@ int main(int argc, char **argv) case 's': scriptfile = optarg; scriptfile_tcl = false; + scriptfile_python = false; run_shell = false; break; case 'c': scriptfile = optarg; scriptfile_tcl = true; + scriptfile_python = false; + run_shell = false; + break; + case 'y': + scriptfile = optarg; + scriptfile_tcl = false; + scriptfile_python = true; run_shell = false; break; case 'W': @@ -607,8 +621,9 @@ int main(int argc, char **argv) run_pass(vdef_cmd); } - if (scriptfile.empty() || !scriptfile_tcl) { - // Without a TCL script, arguments following '--' are also treated as frontend files + if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) { + // Without a TCL or Python script, arguments following '--' are also + // treated as frontend files for (int i = optind; i < argc; ++i) frontend_files.push_back(argv[i]); } @@ -636,7 +651,28 @@ int main(int argc, char **argv) if (Tcl_EvalFile(interp, scriptfile.c_str()) != TCL_OK) log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp())); #else - log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n"); + log_error("Can't execute TCL script: this version of yosys is not built with TCL support enabled.\n"); +#endif + } else if (scriptfile_python) { +#ifdef WITH_PYTHON + PyObject *sys = PyImport_ImportModule("sys"); + PyObject *new_argv = PyList_New(argc - optind + 1); + PyList_SetItem(new_argv, 0, PyUnicode_FromString(scriptfile.c_str())); + for (int i = optind; i < argc; ++i) + PyList_SetItem(new_argv, i - optind + 1, PyUnicode_FromString(argv[i])); + + PyObject *old_argv = PyObject_GetAttrString(sys, "argv"); + PyObject_SetAttrString(sys, "argv", new_argv); + Py_DECREF(old_argv); + + FILE *scriptfp = fopen(scriptfile.c_str(), "r"); + if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) { + log_error("Python interpreter encountered an error:\n"); + log_flush(); + PyErr_Print(); + } +#else + log_error("Can't execute Python script: this version of yosys is not built with Python support enabled.\n"); #endif } else run_frontend(scriptfile, "script"); From 738b5eef0bcbf04eff1a263fd8177c9e24ed02da Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Tue, 10 Sep 2024 13:41:04 +0300 Subject: [PATCH 2/4] Add dirname of script file to sys.path This matches the behavior of running a Python interpreter, where the first element of sys.path is the dirname of the script being run. This allows importing of files and modules in the same directory without messing with PYTHONPATH or similar. --- kernel/driver.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index bd32872a2..0e0a8fa7a 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -660,11 +660,13 @@ int main(int argc, char **argv) PyList_SetItem(new_argv, 0, PyUnicode_FromString(scriptfile.c_str())); for (int i = optind; i < argc; ++i) PyList_SetItem(new_argv, i - optind + 1, PyUnicode_FromString(argv[i])); - + PyObject *old_argv = PyObject_GetAttrString(sys, "argv"); PyObject_SetAttrString(sys, "argv", new_argv); Py_DECREF(old_argv); - + + PyRun_SimpleString(("import os;sys.path.insert(0, os.path.dirname(os.path.abspath(\""+scriptfile+"\")))").c_str()); + FILE *scriptfp = fopen(scriptfile.c_str(), "r"); if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) { log_error("Python interpreter encountered an error:\n"); From 8dac27108efdb17313fbeb0a96b1eaae0ecaf2b7 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Wed, 11 Sep 2024 21:39:37 +0300 Subject: [PATCH 3/4] Typos --- kernel/driver.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index 0e0a8fa7a..d30b19c96 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -305,12 +305,12 @@ int main(int argc, char **argv) printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); printf("\n"); printf(" -C\n"); - printf(" enters TCL interatcive shell mode\n"); + printf(" enters TCL interactive shell mode\n"); #endif #ifdef WITH_PYTHON printf("\n"); printf(" -y python_scriptfile\n"); - printf(" execute the python script"); + printf(" execute a python script with libyosys available as a built-in module\n"); #endif printf("\n"); printf(" -p command\n"); From 35c8ad61ac9baaacf7919359b63b930e37d21b09 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 30 Sep 2024 17:38:43 +0300 Subject: [PATCH 4/4] cli/python: error-checking, python interpreter bugfix * Less brittle method of adding script dirname to sys.path * Check if scriptfp successfully opens before using it * Move `log_error` to after `PyErr_Print()` is called --- kernel/driver.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index d30b19c96..53608c260 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -665,13 +665,19 @@ int main(int argc, char **argv) PyObject_SetAttrString(sys, "argv", new_argv); Py_DECREF(old_argv); - PyRun_SimpleString(("import os;sys.path.insert(0, os.path.dirname(os.path.abspath(\""+scriptfile+"\")))").c_str()); + PyObject *py_path = PyUnicode_FromString(scriptfile.c_str()); + PyObject_SetAttrString(sys, "_yosys_script_path", py_path); + Py_DECREF(py_path); + PyRun_SimpleString("import os, sys; sys.path.insert(0, os.path.dirname(os.path.abspath(sys._yosys_script_path)))"); FILE *scriptfp = fopen(scriptfile.c_str(), "r"); + if (scriptfp == nullptr) { + log_error("Failed to open file '%s' for reading.\n", scriptfile.c_str()); + } if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) { - log_error("Python interpreter encountered an error:\n"); log_flush(); PyErr_Print(); + log_error("Python interpreter encountered an exception."); } #else log_error("Can't execute Python script: this version of yosys is not built with Python support enabled.\n");