diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfd434558..1db8c2b67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,9 +29,23 @@ jobs: - name: Build wheel uses: ./.github/actions/build-wheel + - name: Compile and run path helper fixture + run: | + g++ -std=c++20 -Wall -Werror src/interface/python/test/py_path_utils_test.cc -o /tmp/py_path_utils_test + /tmp/py_path_utils_test + + - name: Smoke test installed wheel + run: | + python -m venv /tmp/wheel-smoke + /tmp/wheel-smoke/bin/pip install dist/wheel/repaired/*.whl pytest + # Run from outside the checkout so `import ecc_tools_bin` resolves + # to the installed wheel, not the source-tree package. + cd /tmp && /tmp/wheel-smoke/bin/python -m pytest "$GITHUB_WORKSPACE/tests/test_pathlike_contract.py" -q + - name: Upload repaired wheel uses: actions/upload-artifact@v4 with: name: ecc-tools-wheel path: dist/wheel/repaired/*.whl if-no-files-found: error + diff --git a/pyproject.toml b/pyproject.toml index fb7bbaa9f..339e08bba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" [project] name = "ecc-tools-bin" -version = "0.1.0-alpha.7" +version = "0.1.0-alpha.8" requires-python = ">=3.11" dependencies = [ "numpy", diff --git a/src/interface/python/py_config/py_config.cpp b/src/interface/python/py_config/py_config.cpp index b54b0fffc..70d9d624e 100644 --- a/src/interface/python/py_config/py_config.cpp +++ b/src/interface/python/py_config/py_config.cpp @@ -16,52 +16,74 @@ // *************************************************************************************** #include "py_config.h" +#include "../py_path_utils.h" #include #include #include namespace python_interface { -bool flow_init(const std::string& flow_config) +bool flow_init(const std::filesystem::path& flow_config) { - bool init_ok = iplf::plfInst->initFlow(flow_config); + const std::string flow_config_ = flow_config.string(); + bool init_ok = iplf::plfInst->initFlow(flow_config_); return init_ok; } -bool db_init(const std::string& config_path, const std::string& tech_lef_path, const std::vector& lef_paths, - const std::string& def_path, const std::string& verilog_path, const std::string& output_path, const std::string& feature_path, - const std::vector& lib_paths, const std::string& sdc_path) +bool db_init(const std::optional& config_path, const std::optional& tech_lef_path, + const std::vector& lef_paths, const std::optional& def_path, + const std::optional& verilog_path, const std::optional& output_path, + const std::optional& feature_path, const std::vector& lib_paths, + const std::optional& sdc_path) { + const std::string config_path_ = path_or_empty(config_path); + const std::string tech_lef_path_ = path_or_empty(tech_lef_path); + std::vector lef_paths_; + lef_paths_.reserve(lef_paths.size()); + for (const auto& lef_path : lef_paths) { + lef_paths_.push_back(lef_path.string()); + } + const std::string def_path_ = path_or_empty(def_path); + const std::string verilog_path_ = path_or_empty(verilog_path); + const std::string output_path_ = path_or_empty(output_path); + const std::string feature_path_ = path_or_empty(feature_path); + std::vector lib_paths_; + lib_paths_.reserve(lib_paths.size()); + for (const auto& lib_path : lib_paths) { + lib_paths_.push_back(lib_path.string()); + } + const std::string sdc_path_ = path_or_empty(sdc_path); + idm::DataConfig& dm_config = dmInst->get_config(); - if (not config_path.empty()) { - bool init_ok = dm_config.initConfig(config_path); + if (not config_path_.empty()) { + bool init_ok = dm_config.initConfig(config_path_); if (not init_ok) { return false; } } - if (not tech_lef_path.empty()) { - dm_config.set_tech_lef_path(tech_lef_path); + if (not tech_lef_path_.empty()) { + dm_config.set_tech_lef_path(tech_lef_path_); } - if (not lef_paths.empty()) { - dm_config.set_lef_paths(lef_paths); + if (not lef_paths_.empty()) { + dm_config.set_lef_paths(lef_paths_); } - if (not def_path.empty()) { - dm_config.set_def_path(def_path); + if (not def_path_.empty()) { + dm_config.set_def_path(def_path_); } - if (not verilog_path.empty()) { - dm_config.set_verilog_path(verilog_path); + if (not verilog_path_.empty()) { + dm_config.set_verilog_path(verilog_path_); } - if (not output_path.empty()) { - dm_config.set_output_path(output_path); + if (not output_path_.empty()) { + dm_config.set_output_path(output_path_); } - if (not lib_paths.empty()) { - dm_config.set_lib_paths(lib_paths); + if (not lib_paths_.empty()) { + dm_config.set_lib_paths(lib_paths_); } - if (not sdc_path.empty()) { - dm_config.set_sdc_path(sdc_path); + if (not sdc_path_.empty()) { + dm_config.set_sdc_path(sdc_path_); } - if (not feature_path.empty()) { - dm_config.set_feature_path(feature_path); + if (not feature_path_.empty()) { + dm_config.set_feature_path(feature_path_); } return true; } diff --git a/src/interface/python/py_config/py_config.h b/src/interface/python/py_config/py_config.h index fd1657b5d..cfc82784d 100644 --- a/src/interface/python/py_config/py_config.h +++ b/src/interface/python/py_config/py_config.h @@ -16,13 +16,17 @@ // *************************************************************************************** #pragma once +#include +#include #include #include namespace python_interface { -bool flow_init(const std::string& flow_config); +bool flow_init(const std::filesystem::path& flow_config); -bool db_init(const std::string& config_path, const std::string& tech_lef_path, const std::vector& lef_paths, - const std::string& def_path, const std::string& verilog_path, const std::string& output_path, const std::string& feature_path, - const std::vector& lib_paths, const std::string& sdc_path); +bool db_init(const std::optional& config_path, const std::optional& tech_lef_path, + const std::vector& lef_paths, const std::optional& def_path, + const std::optional& verilog_path, const std::optional& output_path, + const std::optional& feature_path, const std::vector& lib_paths, + const std::optional& sdc_path); } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_config/py_register_config.h b/src/interface/python/py_config/py_register_config.h index c041da6f9..d5402f5d5 100644 --- a/src/interface/python/py_config/py_register_config.h +++ b/src/interface/python/py_config/py_register_config.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_config.h" namespace python_interface { namespace py = pybind11; @@ -25,15 +26,15 @@ void register_config(pybind11::module& m){ m.def("flow_init", flow_init, py::arg("flow_config")); m.def("db_init", db_init, - py::arg("config_path") = "", - py::arg("tech_lef_path") = "", - py::arg("lef_paths") = std::vector {}, - py::arg("def_path") = "", - py::arg("verilog_path") = "", - py::arg("output_path") = "", - py::arg("feature_path") = "", - py::arg("lib_paths") = std::vector{}, - py::arg("sdc_path") = "" + py::arg("config_path") = py::none(), + py::arg("tech_lef_path") = py::none(), + py::arg("lef_paths") = std::vector{}, + py::arg("def_path") = py::none(), + py::arg("verilog_path") = py::none(), + py::arg("output_path") = py::none(), + py::arg("feature_path") = py::none(), + py::arg("lib_paths") = std::vector{}, + py::arg("sdc_path") = py::none() ); } } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_eval/py_eval.cpp b/src/interface/python/py_eval/py_eval.cpp index 74bfb98f0..23fd81fe6 100644 --- a/src/interface/python/py_eval/py_eval.cpp +++ b/src/interface/python/py_eval/py_eval.cpp @@ -178,24 +178,29 @@ void eval_macro_channel(float die_size_ratio) { } -void eval_cell_hierarchy(const std::string& plot_path, int level, int forward) +void eval_cell_hierarchy(const std::filesystem::path& plot_path, int level, int forward) { + [[maybe_unused]] const std::string plot_path_ = plot_path.string(); } -void eval_macro_hierarchy(const std::string& plot_path, int level, int forward) +void eval_macro_hierarchy(const std::filesystem::path& plot_path, int level, int forward) { + [[maybe_unused]] const std::string plot_path_ = plot_path.string(); } -void eval_macro_connection(const std::string& plot_path, int level, int forward) +void eval_macro_connection(const std::filesystem::path& plot_path, int level, int forward) { + [[maybe_unused]] const std::string plot_path_ = plot_path.string(); } -void eval_macro_pin_connection(const std::string& plot_path, int level, int forward) +void eval_macro_pin_connection(const std::filesystem::path& plot_path, int level, int forward) { + [[maybe_unused]] const std::string plot_path_ = plot_path.string(); } -void eval_macro_io_pin_connection(const std::string& plot_path, int level, int forward) +void eval_macro_io_pin_connection(const std::filesystem::path& plot_path, int level, int forward) { + [[maybe_unused]] const std::string plot_path_ = plot_path.string(); } diff --git a/src/interface/python/py_eval/py_eval.h b/src/interface/python/py_eval/py_eval.h index c4933e428..cc6cc63ab 100644 --- a/src/interface/python/py_eval/py_eval.h +++ b/src/interface/python/py_eval/py_eval.h @@ -16,6 +16,7 @@ // *************************************************************************************** #pragma once +#include #include #include @@ -50,11 +51,11 @@ ieval::TimingSummary timing_power_egr(); void eval_macro_margin(); void eval_macro_channel(float die_size_ratio = 0.5); void eval_continuous_white_space(); -void eval_cell_hierarchy(const std::string& plot_path, int level = 1, int forward = 1); -void eval_macro_hierarchy(const std::string& plot_path, int level = 1, int forward = 1); -void eval_macro_connection(const std::string& plot_path, int level = 1, int forward = 1); -void eval_macro_pin_connection(const std::string& plot_path, int level = 1, int forward = 1); -void eval_macro_io_pin_connection(const std::string& plot_path, int level = 1, int forward = 1); +void eval_cell_hierarchy(const std::filesystem::path& plot_path, int level = 1, int forward = 1); +void eval_macro_hierarchy(const std::filesystem::path& plot_path, int level = 1, int forward = 1); +void eval_macro_connection(const std::filesystem::path& plot_path, int level = 1, int forward = 1); +void eval_macro_pin_connection(const std::filesystem::path& plot_path, int level = 1, int forward = 1); +void eval_macro_io_pin_connection(const std::filesystem::path& plot_path, int level = 1, int forward = 1); std::vector eval_overflow(); diff --git a/src/interface/python/py_eval/py_register_eval.h b/src/interface/python/py_eval/py_register_eval.h index 3173d41ff..f15630d0d 100644 --- a/src/interface/python/py_eval/py_register_eval.h +++ b/src/interface/python/py_eval/py_register_eval.h @@ -17,7 +17,12 @@ #pragma once #include #include +#include +#include +#include + +#include "../py_path_utils.h" #include "py_eval.h" namespace python_interface { @@ -46,37 +51,43 @@ void register_eval(py::module& m) // density evaluation functions - m.def("cell_density", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::string& save_path = "") -> py::tuple { - auto [max_density, avg_density] = cell_density(bin_cnt_x, bin_cnt_y, save_path); + m.def("cell_density", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::optional& save_path = std::nullopt) -> py::tuple { + const std::string save_path_ = path_or_empty(save_path); + auto [max_density, avg_density] = cell_density(bin_cnt_x, bin_cnt_y, save_path_); return py::make_tuple(max_density, avg_density); - }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = ""); + }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = py::none()); - m.def("pin_density", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::string& save_path = "") -> py::tuple { - auto [max_density, avg_density] = pin_density(bin_cnt_x, bin_cnt_y, save_path); + m.def("pin_density", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::optional& save_path = std::nullopt) -> py::tuple { + const std::string save_path_ = path_or_empty(save_path); + auto [max_density, avg_density] = pin_density(bin_cnt_x, bin_cnt_y, save_path_); return py::make_tuple(max_density, avg_density); - }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = ""); + }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = py::none()); - m.def("net_density", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::string& save_path = "") -> py::tuple { - auto [max_density, avg_density] = net_density(bin_cnt_x, bin_cnt_y, save_path); + m.def("net_density", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::optional& save_path = std::nullopt) -> py::tuple { + const std::string save_path_ = path_or_empty(save_path); + auto [max_density, avg_density] = net_density(bin_cnt_x, bin_cnt_y, save_path_); return py::make_tuple(max_density, avg_density); - }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = ""); + }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = py::none()); // congestion evalation - m.def("rudy_congestion", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::string& save_path = "") -> py::tuple { - auto [max_congestion, total_congestion] = rudy_congestion(bin_cnt_x, bin_cnt_y, save_path); + m.def("rudy_congestion", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::optional& save_path = std::nullopt) -> py::tuple { + const std::string save_path_ = path_or_empty(save_path); + auto [max_congestion, total_congestion] = rudy_congestion(bin_cnt_x, bin_cnt_y, save_path_); return py::make_tuple(max_congestion, total_congestion); - }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = ""); + }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = py::none()); - m.def("lut_rudy_congestion", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::string& save_path = "") -> py::tuple { - auto [max_congestion, total_congestion] = lut_rudy_congestion(bin_cnt_x, bin_cnt_y, save_path); + m.def("lut_rudy_congestion", [](int bin_cnt_x = 256, int bin_cnt_y = 256, const std::optional& save_path = std::nullopt) -> py::tuple { + const std::string save_path_ = path_or_empty(save_path); + auto [max_congestion, total_congestion] = lut_rudy_congestion(bin_cnt_x, bin_cnt_y, save_path_); return py::make_tuple(max_congestion, total_congestion); - }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = ""); + }, py::arg("bin_cnt_x") = 256, py::arg("bin_cnt_y") = 256, py::arg("save_path") = py::none()); - m.def("egr_congestion", [](const std::string& save_path = "") -> py::tuple { - auto [max_congestion, total_congestion] = egr_congestion(save_path); + m.def("egr_congestion", [](const std::optional& save_path = std::nullopt) -> py::tuple { + const std::string save_path_ = path_or_empty(save_path); + auto [max_congestion, total_congestion] = egr_congestion(save_path_); return py::make_tuple(max_congestion, total_congestion); - }, py::arg("save_path") = ""); + }, py::arg("save_path") = py::none()); // timing and power evaluation diff --git a/src/interface/python/py_feature/py_feature.cpp b/src/interface/python/py_feature/py_feature.cpp index ac9391cfa..a78b81d9b 100644 --- a/src/interface/python/py_feature/py_feature.cpp +++ b/src/interface/python/py_feature/py_feature.cpp @@ -20,44 +20,53 @@ namespace python_interface { -bool feature_summary(const std::string& path) +bool feature_summary(const std::filesystem::path& path) { - return featureInst->save_summary(path); + const std::string path_ = path.string(); + return featureInst->save_summary(path_); } -bool feature_tool(const std::string& path, const std::string& step) +bool feature_tool(const std::filesystem::path& path, const std::string& step) { - return featureInst->save_tools(path, step); + const std::string path_ = path.string(); + return featureInst->save_tools(path_, step); } -bool feature_eval_map(const std::string& path, const int& bin_cnt_x, const int& bin_cnt_y) +bool feature_eval_map(const std::filesystem::path& path, const int& bin_cnt_x, const int& bin_cnt_y) { - return featureInst->save_eval_map(path, bin_cnt_x, bin_cnt_y); + const std::string path_ = path.string(); + return featureInst->save_eval_map(path_, bin_cnt_x, bin_cnt_y); } -bool feature_net_eval(const std::string& path) +bool feature_net_eval(const std::filesystem::path& path) { - return featureInst->save_net_eval(path); + const std::string path_ = path.string(); + return featureInst->save_net_eval(path_); } -bool feature_route(const std::string& path) +bool feature_route(const std::filesystem::path& path) { - return featureInst->save_route_data(path); + const std::string path_ = path.string(); + return featureInst->save_route_data(path_); } -bool feature_route_read(const std::string& path) +bool feature_route_read(const std::filesystem::path& path) { - return featureInst->read_route_data(path); + const std::string path_ = path.string(); + return featureInst->read_route_data(path_); } -bool feature_macro_drc(const std::string& path, const std::string& drc_path) +bool feature_macro_drc(const std::filesystem::path& path, const std::filesystem::path& drc_path) { - return featureInst->feature_macro_drc(path, drc_path); + const std::string path_ = path.string(); + const std::string drc_path_ = drc_path.string(); + return featureInst->feature_macro_drc(path_, drc_path_); } -bool feature_eval_summary(const std::string& path, int32_t grid_size) +bool feature_eval_summary(const std::filesystem::path& path, int32_t grid_size) { - return featureInst->save_eval_summary(path, grid_size); + const std::string path_ = path.string(); + return featureInst->save_eval_summary(path_, grid_size); } bool feature_eval_union(const std::string& jsonl_path, const std::string& csv_path, int32_t grid_size) @@ -65,24 +74,28 @@ bool feature_eval_union(const std::string& jsonl_path, const std::string& csv_pa return featureInst->save_eval_union(jsonl_path, csv_path, grid_size); } -bool feature_pl_eval(const std::string& json_path, int32_t grid_size) +bool feature_pl_eval(const std::filesystem::path& json_path, int32_t grid_size) { - return featureInst->save_pl_eval(json_path, grid_size); + const std::string json_path_ = json_path.string(); + return featureInst->save_pl_eval(json_path_, grid_size); } -bool feature_cts_eval(const std::string& json_path, int32_t grid_size) +bool feature_cts_eval(const std::filesystem::path& json_path, int32_t grid_size) { - return featureInst->save_cts_eval(json_path, grid_size); + const std::string json_path_ = json_path.string(); + return featureInst->save_cts_eval(json_path_, grid_size); } -bool feature_timing_eval_summary(const std::string& path) +bool feature_timing_eval_summary(const std::filesystem::path& path) { - return featureInst->save_timing_eval_summary(path); + const std::string path_ = path.string(); + return featureInst->save_timing_eval_summary(path_); } -bool feature_cong_map(const std::string& step, const std::string& dir) +bool feature_cong_map(const std::string& step, const std::filesystem::path& dir) { - return featureInst->save_cong_map(step, dir); + const std::string dir_ = dir.string(); + return featureInst->save_cong_map(step, dir_); } } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_feature/py_feature.h b/src/interface/python/py_feature/py_feature.h index e7afffbc9..021ec4bf8 100644 --- a/src/interface/python/py_feature/py_feature.h +++ b/src/interface/python/py_feature/py_feature.h @@ -16,22 +16,23 @@ // *************************************************************************************** #pragma once +#include #include namespace python_interface { -bool feature_summary(const std::string& path); -bool feature_tool(const std::string& path, const std::string& step); -bool feature_pl_eval(const std::string& json_path, int32_t grid_size = 1); -bool feature_cts_eval(const std::string& json_path, int32_t grid_size = 1); +bool feature_summary(const std::filesystem::path& path); +bool feature_tool(const std::filesystem::path& path, const std::string& step); +bool feature_pl_eval(const std::filesystem::path& json_path, int32_t grid_size = 1); +bool feature_cts_eval(const std::filesystem::path& json_path, int32_t grid_size = 1); -bool feature_eval_map(const std::string& path, const int& bin_cnt_x, const int& bin_cnt_y); -bool feature_route(const std::string& path); -bool feature_route_read(const std::string& path); -bool feature_macro_drc(const std::string& path, const std::string& drc_path); -bool feature_eval_summary(const std::string& path, int32_t grid_size); -bool feature_timing_eval_summary(const std::string& path); -bool feature_net_eval(const std::string& path); -bool feature_cong_map(const std::string& step, const std::string& dir); +bool feature_eval_map(const std::filesystem::path& path, const int& bin_cnt_x, const int& bin_cnt_y); +bool feature_route(const std::filesystem::path& path); +bool feature_route_read(const std::filesystem::path& path); +bool feature_macro_drc(const std::filesystem::path& path, const std::filesystem::path& drc_path); +bool feature_eval_summary(const std::filesystem::path& path, int32_t grid_size); +bool feature_timing_eval_summary(const std::filesystem::path& path); +bool feature_net_eval(const std::filesystem::path& path); +bool feature_cong_map(const std::string& step, const std::filesystem::path& dir); } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_feature/py_register_feature.h b/src/interface/python/py_feature/py_register_feature.h index 1a2c06df0..8ff6d9c2c 100644 --- a/src/interface/python/py_feature/py_register_feature.h +++ b/src/interface/python/py_feature/py_register_feature.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_feature.h" diff --git a/src/interface/python/py_icts/py_icts.cpp b/src/interface/python/py_icts/py_icts.cpp index e6c879b13..e5d989259 100644 --- a/src/interface/python/py_icts/py_icts.cpp +++ b/src/interface/python/py_icts/py_icts.cpp @@ -19,15 +19,18 @@ #include namespace python_interface { -bool CtsAutoRun(const std::string& cts_config, const std::string& cts_work_dir) +bool CtsAutoRun(const std::filesystem::path& cts_config, const std::filesystem::path& cts_work_dir) { - bool cts_run_ok = iplf::tmInst->autoRunCTS(cts_config, cts_work_dir); + const std::string cts_config_ = cts_config.string(); + const std::string cts_work_dir_ = cts_work_dir.string(); + bool cts_run_ok = iplf::tmInst->autoRunCTS(cts_config_, cts_work_dir_); return cts_run_ok; } -bool CtsReport(const std::string& path) +bool CtsReport(const std::filesystem::path& path) { - return iplf::tmInst->reportCTS(path); + const std::string path_ = path.string(); + return iplf::tmInst->reportCTS(path_); } } // namespace python_interface diff --git a/src/interface/python/py_icts/py_icts.h b/src/interface/python/py_icts/py_icts.h index 5a9f406ad..9818dc991 100644 --- a/src/interface/python/py_icts/py_icts.h +++ b/src/interface/python/py_icts/py_icts.h @@ -15,9 +15,10 @@ // See the Mulan PSL v2 for more details. // *************************************************************************************** #pragma once +#include #include namespace python_interface { -bool CtsAutoRun(const std::string& cts_config, const std::string& cts_work_dir); -bool CtsReport(const std::string& path); +bool CtsAutoRun(const std::filesystem::path& cts_config, const std::filesystem::path& cts_work_dir); +bool CtsReport(const std::filesystem::path& path); } // namespace python_interface diff --git a/src/interface/python/py_icts/py_register_icts.h b/src/interface/python/py_icts/py_register_icts.h index c5d7a5827..c3ececd07 100644 --- a/src/interface/python/py_icts/py_register_icts.h +++ b/src/interface/python/py_icts/py_register_icts.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_icts.h" diff --git a/src/interface/python/py_idb/py_db.cpp b/src/interface/python/py_idb/py_db.cpp index 1823ceed1..20ac14802 100644 --- a/src/interface/python/py_idb/py_db.cpp +++ b/src/interface/python/py_idb/py_db.cpp @@ -16,107 +16,132 @@ // *************************************************************************************** #include "py_db.h" +#include "../py_path_utils.h" #include "db_fm/file_soc.h" #include #include "view_json_io.h" namespace python_interface { -bool initIdb(const std::string& config_path) +bool initIdb(const std::filesystem::path& config_path) { - return dmInst->init(config_path); + const std::string config_path_ = config_path.string(); + return dmInst->init(config_path_); } -bool initTechLef(const std::string& techlef_path) +bool initTechLef(const std::filesystem::path& techlef_path) { - dmInst->get_config().set_tech_lef_path(techlef_path); - return dmInst->readLef(vector{techlef_path}, true); + const std::string techlef_path_ = techlef_path.string(); + dmInst->get_config().set_tech_lef_path(techlef_path_); + return dmInst->readLef(vector{techlef_path_}, true); } -bool initLef(const std::vector& lef_paths) +bool initLef(const std::vector& lef_paths) { - dmInst->get_config().set_lef_paths(lef_paths); - return dmInst->readLef(lef_paths); + std::vector lef_paths_; + lef_paths_.reserve(lef_paths.size()); + for (const auto& lef_path : lef_paths) { + lef_paths_.push_back(lef_path.string()); + } + dmInst->get_config().set_lef_paths(lef_paths_); + return dmInst->readLef(lef_paths_); } -bool initDef(const std::string& def_path) +bool initDef(const std::filesystem::path& def_path) { - dmInst->get_config().set_def_path(def_path); - return dmInst->readDef(def_path); + const std::string def_path_ = def_path.string(); + dmInst->get_config().set_def_path(def_path_); + return dmInst->readDef(def_path_); } -bool initVerilog(const std::string& verilog_path, const std::string& top_module) +bool initVerilog(const std::filesystem::path& verilog_path, const std::string& top_module) { - dmInst->get_config().set_verilog_path(verilog_path); - return dmInst->readVerilog(verilog_path, top_module); + const std::string verilog_path_ = verilog_path.string(); + dmInst->get_config().set_verilog_path(verilog_path_); + return dmInst->readVerilog(verilog_path_, top_module); } -bool initLib(const std::vector& lib_paths) +bool initLib(const std::vector& lib_paths) { - dmInst->get_config().set_lib_paths(lib_paths); - return dmInst->readLib(lib_paths); + std::vector lib_paths_; + lib_paths_.reserve(lib_paths.size()); + for (const auto& lib_path : lib_paths) { + lib_paths_.push_back(lib_path.string()); + } + dmInst->get_config().set_lib_paths(lib_paths_); + return dmInst->readLib(lib_paths_); } -bool initSdc(const std::string& sdc_path) +bool initSdc(const std::optional& sdc_path) { - dmInst->get_config().set_sdc_path(sdc_path); + const std::string sdc_path_ = path_or_empty(sdc_path); + dmInst->get_config().set_sdc_path(sdc_path_); return true; } -bool initSpef(const std::string& spef_path) +bool initSpef(const std::filesystem::path& spef_path) { - dmInst->get_config().set_spef_path(spef_path); - return dmInst->readSpef(spef_path); + const std::string spef_path_ = spef_path.string(); + dmInst->get_config().set_spef_path(spef_path_); + return dmInst->readSpef(spef_path_); } -bool saveDef(const std::string& def_name) +bool saveDef(const std::filesystem::path& def_name) { - return dmInst->saveDef(def_name); + const std::string def_name_ = def_name.string(); + return dmInst->saveDef(def_name_); } -bool saveMacroTCL(const std::string& def_name) +bool saveMacroTCL(const std::filesystem::path& tcl_name) { - return dmInst->saveMacroTCL(def_name); + const std::string tcl_name_ = tcl_name.string(); + return dmInst->saveMacroTCL(tcl_name_); } -bool saveNetList(const std::string& netlist_path, std::set exclude_cell_names /* = {} */, +bool saveNetList(const std::filesystem::path& netlist_path, std::set exclude_cell_names /* = {} */, bool is_add_space_for_escape_name /* = false*/) { - dmInst->saveVerilog(netlist_path, std::move(exclude_cell_names), is_add_space_for_escape_name); + const std::string netlist_path_ = netlist_path.string(); + dmInst->saveVerilog(netlist_path_, std::move(exclude_cell_names), is_add_space_for_escape_name); return true; } -bool saveGDSII(const std::string& gds_name, bool is_hardened /* = false */) +bool saveGDSII(const std::filesystem::path& gds_name, bool is_hardened /* = false */) { - return dmInst->saveGDSII(gds_name, is_hardened); + const std::string gds_name_ = gds_name.string(); + return dmInst->saveGDSII(gds_name_, is_hardened); } -bool saveJson(const std::string& path) +bool saveJson(const std::filesystem::path& path) { + const std::string path_ = path.string(); std::string options = ""; - return dmInst->saveJSON(path, options); + return dmInst->saveJSON(path_, options); } -bool saveViewJson(const std::string& output_dir, const std::string& json_format, bool compress) +bool saveViewJson(const std::filesystem::path& output_dir, const std::string& json_format, bool compress) { + const std::string output_dir_ = output_dir.string(); idb::ViewJsonWriteOptions options; if (!idb::parseViewJsonFormat(json_format, options.format)) { std::cout << "Save view json failed: unsupported json_format `" << json_format << "`, expected `pretty` or `compact`." << std::endl; return false; } options.compress = compress; - return dmInst->saveViewJson(output_dir, options); + return dmInst->saveViewJson(output_dir_, options); } -bool applyViewJsonEdits(const std::string& edits_path, bool compress) +bool applyViewJsonEdits(const std::filesystem::path& edits_path, bool compress) { - return dmInst->applyViewJsonEdits(edits_path, compress); + const std::string edits_path_ = edits_path.string(); + return dmInst->applyViewJsonEdits(edits_path_, compress); } -bool saveData(const std::string& path) +bool saveData(const std::filesystem::path& path) { - return dmInst->saveData(path); + const std::string path_ = path.string(); + return dmInst->saveData(path_); } bool resetData() @@ -125,22 +150,23 @@ bool resetData() return true; } -bool loadData(const std::string& path) +bool loadData(const std::filesystem::path& path) { - return dmInst->loadData(path); + const std::string path_ = path.string(); + return dmInst->loadData(path_); } -bool writeSocJson(const std::string& path, const std::vector& harden_cores /* = {} */) +bool writeSocJson(const std::filesystem::path& path, const std::vector& harden_cores /* = {} */) { - idb::JsonSoc soc_file(path, harden_cores); + const std::string path_ = path.string(); + idb::JsonSoc soc_file(path_, harden_cores); return soc_file.saveFileData(); } -bool writeAbstractLef(const std::string& output_lef_path) +bool writeAbstractLef(const std::filesystem::path& output_lef_path) { - namespace fs = std::filesystem; - - return dmInst->saveLef(output_lef_path); + const std::string output_lef_path_ = output_lef_path.string(); + return dmInst->saveLef(output_lef_path_); } } // namespace python_interface diff --git a/src/interface/python/py_idb/py_db.h b/src/interface/python/py_idb/py_db.h index 72d3fcd49..66342711f 100644 --- a/src/interface/python/py_idb/py_db.h +++ b/src/interface/python/py_idb/py_db.h @@ -16,31 +16,33 @@ // *************************************************************************************** #pragma once +#include +#include #include #include #include namespace python_interface { -bool initIdb(const std::string& config_path); -bool initTechLef(const std::string& techlef_path); -bool initLef(const std::vector& lef_paths); -bool initDef(const std::string& def_path); -bool initVerilog(const std::string& verilog_path, const std::string& top_module); -bool initLib(const std::vector& lib_paths); -bool initSdc(const std::string& sdc_path); -bool initSpef(const std::string& spef_path); -bool saveDef(const std::string& def_name); -bool saveMacroTCL(const std::string& tcl_name); -bool saveNetList(const std::string& netlist_path, std::set exclude_cell_names = {}, bool is_add_space_for_escape_name = false); -bool saveGDSII(const std::string& gds_name, bool is_harden = false); -bool saveJson(const std::string& path); -bool saveViewJson(const std::string& output_dir, const std::string& json_format = "pretty", bool compress = false); -bool applyViewJsonEdits(const std::string& edits_path, bool compress = false); -bool saveData(const std::string& path); +bool initIdb(const std::filesystem::path& config_path); +bool initTechLef(const std::filesystem::path& techlef_path); +bool initLef(const std::vector& lef_paths); +bool initDef(const std::filesystem::path& def_path); +bool initVerilog(const std::filesystem::path& verilog_path, const std::string& top_module); +bool initLib(const std::vector& lib_paths); +bool initSdc(const std::optional& sdc_path); +bool initSpef(const std::filesystem::path& spef_path); +bool saveDef(const std::filesystem::path& def_name); +bool saveMacroTCL(const std::filesystem::path& tcl_name); +bool saveNetList(const std::filesystem::path& netlist_path, std::set exclude_cell_names = {}, bool is_add_space_for_escape_name = false); +bool saveGDSII(const std::filesystem::path& gds_name, bool is_harden = false); +bool saveJson(const std::filesystem::path& path); +bool saveViewJson(const std::filesystem::path& output_dir, const std::string& json_format = "pretty", bool compress = false); +bool applyViewJsonEdits(const std::filesystem::path& edits_path, bool compress = false); +bool saveData(const std::filesystem::path& path); bool resetData(); -bool loadData(const std::string& path); -bool writeSocJson(const std::string& path, const std::vector& harden_cores = {}); -bool writeAbstractLef(const std::string& output_lef_path); +bool loadData(const std::filesystem::path& path); +bool writeSocJson(const std::filesystem::path& path, const std::vector& harden_cores = {}); +bool writeAbstractLef(const std::filesystem::path& output_lef_path); } // namespace python_interface diff --git a/src/interface/python/py_idb/py_db_op.h b/src/interface/python/py_idb/py_db_op.h index 6502ef788..3c24ce17d 100644 --- a/src/interface/python/py_idb/py_db_op.h +++ b/src/interface/python/py_idb/py_db_op.h @@ -17,12 +17,15 @@ #pragma once #include +#include +#include #include #include #include #include #include +#include "../py_path_utils.h" #include "IdbEnum.h" #include "IdbInstance.h" @@ -44,14 +47,15 @@ bool clearBlockage(const std::string& type) return true; } -bool idbGet(const std::string& inst_name, const std::string& net_name, const std::string& file_name) +bool idbGet(const std::string& inst_name, const std::string& net_name, const std::optional& file_name) { + const std::string file_name_ = path_or_empty(file_name); bool ok = false; if (not inst_name.empty()) { - ok |= rptInst->reportInstance(file_name, inst_name); + ok |= rptInst->reportInstance(file_name_, inst_name); } if (not net_name.empty()) { - ok |= rptInst->reportNet(file_name, net_name); + ok |= rptInst->reportNet(file_name_, net_name); } return ok; } diff --git a/src/interface/python/py_idb/py_register_idb.h b/src/interface/python/py_idb/py_register_idb.h index 98cd7f939..a8bf2a527 100644 --- a/src/interface/python/py_idb/py_register_idb.h +++ b/src/interface/python/py_idb/py_register_idb.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include @@ -36,7 +37,7 @@ void register_idb(py::module& m) m.def("def_init", initDef, py::arg("def_path")); m.def("verilog_init", initVerilog, py::arg("verilog_path"), py::arg("top_module")); m.def("lib_init", initLib, py::arg("lib_paths")); - m.def("sdc_init", initSdc, py::arg("sdc_path")); + m.def("sdc_init", initSdc, py::arg("sdc_path") = py::none()); m.def("spef_init", initSpef, py::arg("spef_path")); m.def("def_save", saveDef, py::arg("def_name")); // TODO: @@ -59,7 +60,7 @@ void register_idb_op(pybind11::module& m) m.def("set_net", setNet, py::arg("net_name"), py::arg("net_type")); m.def("remove_except_pg_net", removeExceptPgNet); m.def("clear_blockage", clearBlockage, py::arg("type")); - m.def("idb_get", idbGet, py::arg("inst_name") = "", py::arg("net_name") = "", py::arg("file_name") = ""); + m.def("idb_get", idbGet, py::arg("inst_name") = "", py::arg("net_name") = "", py::arg("file_name") = py::none()); m.def("delete_inst", idbDeleteInstance, py::arg("inst_name")); m.def("delete_net", idbDeleteNet, py::arg("net_name")); m.def("create_inst", idbCreateInstance, py::arg("inst_name"), py::arg("cell_master"), py::arg("coord_x") = 0, py::arg("coord_y") = 0, diff --git a/src/interface/python/py_idrc/py_idrc.cpp b/src/interface/python/py_idrc/py_idrc.cpp index 4a60d608b..2f7f46396 100644 --- a/src/interface/python/py_idrc/py_idrc.cpp +++ b/src/interface/python/py_idrc/py_idrc.cpp @@ -18,15 +18,17 @@ #include +#include "../py_path_utils.h" #include "DRCInterface.hpp" namespace python_interface { -bool init_drc(const std::string& temp_directory_path, const int& thread_number) +bool init_drc(const std::optional& temp_directory_path, const int& thread_number) { + const std::string temp_directory_path_ = path_or_empty(temp_directory_path); std::map config_map; - if (temp_directory_path != "") { - config_map.insert(std::make_pair("-temp_directory_path", temp_directory_path)); + if (temp_directory_path_ != "") { + config_map.insert(std::make_pair("-temp_directory_path", temp_directory_path_)); } config_map.insert(std::make_pair("-thread_number", thread_number)); @@ -35,14 +37,17 @@ bool init_drc(const std::string& temp_directory_path, const int& thread_number) return true; } -bool run_drc(const std::string& config, const std::string& report) +bool run_drc(const std::optional& config, const std::optional& report) { - return iplf::tmInst->autoRunDRC(config, report, true); + const std::string config_ = path_or_empty(config); + const std::string report_ = path_or_empty(report); + return iplf::tmInst->autoRunDRC(config_, report_, true); } -bool save_drc(const std::string& path) +bool save_drc(const std::optional& path) { - return iplf::tmInst->saveDrcDetailToFile(path); + const std::string path_ = path_or_empty(path); + return iplf::tmInst->saveDrcDetailToFile(path_); } } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_idrc/py_idrc.h b/src/interface/python/py_idrc/py_idrc.h index ea52fb875..687617fbb 100644 --- a/src/interface/python/py_idrc/py_idrc.h +++ b/src/interface/python/py_idrc/py_idrc.h @@ -16,11 +16,13 @@ // *************************************************************************************** #pragma once +#include +#include #include namespace python_interface { -bool init_drc(const std::string& temp_directory_path, const int& thread_number); -bool run_drc(const std::string& config, const std::string& report); -bool save_drc(const std::string& path); +bool init_drc(const std::optional& temp_directory_path, const int& thread_number); +bool run_drc(const std::optional& config, const std::optional& report); +bool save_drc(const std::optional& path); } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_idrc/py_register_idrc.h b/src/interface/python/py_idrc/py_register_idrc.h index 2e061497e..0dabd6d78 100644 --- a/src/interface/python/py_idrc/py_register_idrc.h +++ b/src/interface/python/py_idrc/py_register_idrc.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_idrc.h" @@ -25,9 +26,9 @@ namespace py = pybind11; void register_idrc(py::module& m) { - m.def("init_drc", init_drc, py::arg("temp_directory_path") = "", py::arg("thread_number") = 128); - m.def("run_drc", run_drc, py::arg("config") = "", py::arg("report") = ""); - m.def("save_drc", save_drc, py::arg("path") = ""); + m.def("init_drc", init_drc, py::arg("temp_directory_path") = py::none(), py::arg("thread_number") = 128); + m.def("run_drc", run_drc, py::arg("config") = py::none(), py::arg("report") = py::none()); + m.def("save_drc", save_drc, py::arg("path") = py::none()); } } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_ircx/py_ircx.cpp b/src/interface/python/py_ircx/py_ircx.cpp index ce10d4987..84c2b20ce 100644 --- a/src/interface/python/py_ircx/py_ircx.cpp +++ b/src/interface/python/py_ircx/py_ircx.cpp @@ -50,8 +50,9 @@ bool validate_pdk(const std::optional& pdk) } // namespace -bool init_rcx(const std::string& config, const std::optional& pdk) +bool init_rcx(const std::filesystem::path& config, const std::optional& pdk) { + const std::string config_ = config.string(); active_backend = RcxBackend::kUninitialized; if (!validate_pdk(pdk)) { @@ -59,7 +60,7 @@ bool init_rcx(const std::string& config, const std::optional& pdk) } if (is_ics55_pdk(pdk)) { - if (ircx_ics55_init(config.c_str()) != 0) { + if (ircx_ics55_init(config_.c_str()) != 0) { active_backend = RcxBackend::kIcs55; return true; } @@ -67,7 +68,7 @@ bool init_rcx(const std::string& config, const std::optional& pdk) return false; } - if (RCX_API_INST.init(config)) { + if (RCX_API_INST.init(config_)) { active_backend = RcxBackend::kNative; return true; } diff --git a/src/interface/python/py_ircx/py_ircx.h b/src/interface/python/py_ircx/py_ircx.h index bd4802371..a5313710d 100644 --- a/src/interface/python/py_ircx/py_ircx.h +++ b/src/interface/python/py_ircx/py_ircx.h @@ -16,6 +16,7 @@ // *************************************************************************************** #pragma once +#include #include #include @@ -23,7 +24,7 @@ namespace python_interface { -bool init_rcx(const std::string& config, const std::optional& pdk = std::nullopt); +bool init_rcx(const std::filesystem::path& config, const std::optional& pdk = std::nullopt); bool run_rcx(); bool report_rcx(); diff --git a/src/interface/python/py_ircx/py_register_ircx.h b/src/interface/python/py_ircx/py_register_ircx.h index 8cec8b253..93cab77aa 100644 --- a/src/interface/python/py_ircx/py_register_ircx.h +++ b/src/interface/python/py_ircx/py_register_ircx.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_ircx.h" diff --git a/src/interface/python/py_irt/py_irt.cpp b/src/interface/python/py_irt/py_irt.cpp index c9bda9042..39da69db6 100644 --- a/src/interface/python/py_irt/py_irt.cpp +++ b/src/interface/python/py_irt/py_irt.cpp @@ -20,6 +20,7 @@ #include +#include "../py_path_utils.h" #include "RTInterface.hpp" #include "flow_config.h" namespace python_interface { @@ -32,12 +33,13 @@ bool destroyRT() return true; } -bool runERT(std::string& config, std::map& config_dict) +bool runERT(const std::optional& config, std::map& config_dict) { + const std::string config_ = path_or_empty(config); std::map config_map; bool pass = false; - pass = !pass ? initConfigMapByJSON(config, config_map) : pass; + pass = !pass ? initConfigMapByJSON(config_, config_map) : pass; if (!pass) { return false; } @@ -51,14 +53,15 @@ bool runRT() return true; } -bool initRT(std::string& config, std::map& config_dict) +bool initRT(const std::optional& config, std::map& config_dict) { + const std::string config_ = path_or_empty(config); iplf::flowConfigInst->set_status_stage("iRT - Routing"); std::map config_map; bool pass = false; - pass = !pass ? initConfigMapByJSON(config, config_map) : pass; + pass = !pass ? initConfigMapByJSON(config_, config_map) : pass; if (!pass) { return false; } diff --git a/src/interface/python/py_irt/py_irt.h b/src/interface/python/py_irt/py_irt.h index fb74fff1f..44622e309 100644 --- a/src/interface/python/py_irt/py_irt.h +++ b/src/interface/python/py_irt/py_irt.h @@ -18,12 +18,15 @@ #include +#include +#include + namespace python_interface { bool destroyRT(); -bool initRT(std::string& config, std::map& config_dict); +bool initRT(const std::optional& config, std::map& config_dict); bool runDR(); -bool runERT(std::string& config, std::map& config_dict); +bool runERT(const std::optional& config, std::map& config_dict); bool runRT(); } // namespace python_interface diff --git a/src/interface/python/py_irt/py_register_irt.h b/src/interface/python/py_irt/py_register_irt.h index 04b3becf3..4c8be8a68 100644 --- a/src/interface/python/py_irt/py_register_irt.h +++ b/src/interface/python/py_irt/py_register_irt.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "ScriptEngine.hh" #include "py_irt.h" @@ -25,8 +26,8 @@ namespace py = pybind11; void register_irt(py::module& m) { m.def("destroy_rt", destroyRT); - m.def("init_rt", initRT, py::arg("config") = "", py::arg("config_dict") = std::map{}); - m.def("run_ert", runERT, py::arg("config") = "", py::arg("config_dict") = std::map{}); + m.def("init_rt", initRT, py::arg("config") = py::none(), py::arg("config_dict") = std::map{}); + m.def("run_ert", runERT, py::arg("config") = py::none(), py::arg("config_dict") = std::map{}); m.def("run_rt", runRT); } } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_ista/py_ista.cpp b/src/interface/python/py_ista/py_ista.cpp index 333c13709..29ce52789 100644 --- a/src/interface/python/py_ista/py_ista.cpp +++ b/src/interface/python/py_ista/py_ista.cpp @@ -16,6 +16,7 @@ // *************************************************************************************** #include "py_ista.h" +#include "../py_path_utils.h" #include "STAInterface.hpp" namespace python_interface { @@ -23,12 +24,13 @@ namespace python_interface { bool initStaConfigMapByJSON(const std::string& config, std::map& config_map); void initStaConfigMapByDict(std::map& config_dict, std::map& config_map); -bool initSTA(std::string& config, std::map& config_dict) +bool initSTA(const std::optional& config, std::map& config_dict) { + const std::string config_ = path_or_empty(config); std::map config_map; bool pass = false; - pass = config.empty() ? true : initStaConfigMapByJSON(config, config_map); + pass = config_.empty() ? true : initStaConfigMapByJSON(config_, config_map); if (!pass) { return false; } diff --git a/src/interface/python/py_ista/py_ista.h b/src/interface/python/py_ista/py_ista.h index 8a7268075..d1a607b80 100644 --- a/src/interface/python/py_ista/py_ista.h +++ b/src/interface/python/py_ista/py_ista.h @@ -18,9 +18,12 @@ #include +#include +#include + namespace python_interface { -bool initSTA(std::string& config, std::map& config_dict); +bool initSTA(const std::optional& config, std::map& config_dict); bool runSTA(); bool extractLib(); bool destroySTA(); diff --git a/src/interface/python/py_ista/py_register_ista.h b/src/interface/python/py_ista/py_register_ista.h index f7e9a7a27..0f5dd9748 100644 --- a/src/interface/python/py_ista/py_register_ista.h +++ b/src/interface/python/py_ista/py_register_ista.h @@ -18,6 +18,7 @@ #include #include +#include #include "py_ista.h" @@ -26,7 +27,7 @@ namespace py = pybind11; void register_ista(py::module& m) { - m.def("init_sta", initSTA, py::arg("config") = "", py::arg("config_dict") = std::map{}); + m.def("init_sta", initSTA, py::arg("config") = py::none(), py::arg("config_dict") = std::map{}); m.def("run_sta", runSTA); m.def("extract_lib", extractLib); m.def("destroy_sta", destroySTA); diff --git a/src/interface/python/py_izh/py_izh.cpp b/src/interface/python/py_izh/py_izh.cpp index d4da49aec..391af6142 100644 --- a/src/interface/python/py_izh/py_izh.cpp +++ b/src/interface/python/py_izh/py_izh.cpp @@ -20,18 +20,20 @@ #include #include +#include "../py_path_utils.h" #include "ZHInterface.hpp" namespace python_interface { bool initZHConfigMapByJSON(const std::string& config, std::map& config_map); -bool fix_fanout(const std::string& config) +bool fix_fanout(const std::optional& config) { + const std::string config_ = path_or_empty(config); std::map config_map; bool pass = false; - pass = !pass ? initZHConfigMapByJSON(config, config_map) : pass; + pass = !pass ? initZHConfigMapByJSON(config_, config_map) : pass; if (!pass) { return false; } @@ -40,12 +42,13 @@ bool fix_fanout(const std::string& config) return true; } -bool insert_filler(const std::string& config) +bool insert_filler(const std::optional& config) { + const std::string config_ = path_or_empty(config); std::map config_map; bool pass = false; - pass = !pass ? initZHConfigMapByJSON(config, config_map) : pass; + pass = !pass ? initZHConfigMapByJSON(config_, config_map) : pass; if (!pass) { return false; } diff --git a/src/interface/python/py_izh/py_izh.h b/src/interface/python/py_izh/py_izh.h index 553ef8cc6..3288cb2ca 100644 --- a/src/interface/python/py_izh/py_izh.h +++ b/src/interface/python/py_izh/py_izh.h @@ -16,11 +16,13 @@ // *************************************************************************************** #pragma once +#include +#include #include namespace python_interface { -bool fix_fanout(const std::string& config); -bool insert_filler(const std::string& config); +bool fix_fanout(const std::optional& config); +bool insert_filler(const std::optional& config); } // namespace python_interface diff --git a/src/interface/python/py_izh/py_register_izh.h b/src/interface/python/py_izh/py_register_izh.h index d1961a89d..45605f141 100644 --- a/src/interface/python/py_izh/py_register_izh.h +++ b/src/interface/python/py_izh/py_register_izh.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_izh.h" @@ -25,8 +26,8 @@ namespace py = pybind11; void register_izh(py::module& m) { - m.def("fix_fanout", fix_fanout, py::arg("config") = ""); - m.def("insert_filler", insert_filler, py::arg("config") = ""); + m.def("fix_fanout", fix_fanout, py::arg("config") = py::none()); + m.def("insert_filler", insert_filler, py::arg("config") = py::none()); } } // namespace python_interface diff --git a/src/interface/python/py_path_utils.h b/src/interface/python/py_path_utils.h new file mode 100644 index 000000000..8149202c6 --- /dev/null +++ b/src/interface/python/py_path_utils.h @@ -0,0 +1,38 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#pragma once + +#include +#include +#include + +namespace python_interface { + +// Canonicalize an optional path parameter: std::nullopt and an empty path +// both map to "", a non-empty path maps to its .string(). This preserves the +// empty-string unset sentinel the interface internals check with .empty(), +// so an omitted/None argument and an explicitly passed "" stay +// indistinguishable after canonicalization. +inline std::string path_or_empty(const std::optional& path) +{ + if (not path.has_value() || path->empty()) { + return ""; + } + return path->string(); +} + +} // namespace python_interface diff --git a/src/interface/python/py_report/py_register_report.h b/src/interface/python/py_report/py_register_report.h index 8c85f5cc4..411ab23af 100644 --- a/src/interface/python/py_report/py_register_report.h +++ b/src/interface/python/py_report/py_register_report.h @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include "py_report.h" @@ -24,11 +25,11 @@ namespace python_interface { namespace py = pybind11; void register_report(py::module& m) { - m.def("report_wirelength", reportWireLength, py::arg("path") = ""); - m.def("report_db", reportDbSummary, py::arg("path") = ""); - m.def("report_congestion", reportCong, py::arg("path") = ""); - m.def("report_dangling_net", reportDanglingNet, py::arg("path") = ""); - m.def("report_route", reportRoute, py::arg("path") = "", py::arg("net") = "", py::arg("summary") = true); + m.def("report_wirelength", reportWireLength, py::arg("path") = py::none()); + m.def("report_db", reportDbSummary, py::arg("path") = py::none()); + m.def("report_congestion", reportCong, py::arg("path") = py::none()); + m.def("report_dangling_net", reportDanglingNet, py::arg("path") = py::none()); + m.def("report_route", reportRoute, py::arg("path") = py::none(), py::arg("net") = "", py::arg("summary") = true); m.def("report_place_distribution", reportPlaceDistribution, py::arg("prefixes") = std::vector{}); m.def("report_prefixed_instance", reportPrefixedInst, py::arg("prefix"), py::arg("level") = 1, py::arg("num_threshold") = 1); m.def("report_drc", reportDRC, py::arg("path")); diff --git a/src/interface/python/py_report/py_report.cpp b/src/interface/python/py_report/py_report.cpp index 6e3a64916..dbf8efa57 100644 --- a/src/interface/python/py_report/py_report.cpp +++ b/src/interface/python/py_report/py_report.cpp @@ -18,28 +18,35 @@ #include +#include "../py_path_utils.h" + namespace python_interface { -bool reportDbSummary(const std::string& path) +bool reportDbSummary(const std::optional& path) { - return rptInst->reportDBSummary(path); + const std::string path_ = path_or_empty(path); + return rptInst->reportDBSummary(path_); } -bool reportWireLength(const std::string& path) +bool reportWireLength(const std::optional& path) { - return rptInst->reportWL(path); + const std::string path_ = path_or_empty(path); + return rptInst->reportWL(path_); } -bool reportCong(const std::string& path) +bool reportCong(const std::optional& path) { - return rptInst->reportCongestion(path); + const std::string path_ = path_or_empty(path); + return rptInst->reportCongestion(path_); } -bool reportDanglingNet(const std::string& path) +bool reportDanglingNet(const std::optional& path) { - return rptInst->reportDanglingNet(path); + const std::string path_ = path_or_empty(path); + return rptInst->reportDanglingNet(path_); } -bool reportRoute(const std::string& path, const std::string& netname, bool summary) +bool reportRoute(const std::optional& path, const std::string& netname, bool summary) { - return rptInst->reportRoute(path, netname, summary); + const std::string path_ = path_or_empty(path); + return rptInst->reportRoute(path_, netname, summary); } bool reportPlaceDistribution(const std::vector& prefixes) @@ -51,7 +58,8 @@ bool reportPrefixedInst(const std::string& prefix, int level, int num_threshold) return rptInst->reportInstLevel(prefix, level, num_threshold); } -bool reportDRC(const std::string& filename){ - return rptInst->reportDRC(filename); +bool reportDRC(const std::filesystem::path& filename){ + const std::string filename_ = filename.string(); + return rptInst->reportDRC(filename_); } } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/py_report/py_report.h b/src/interface/python/py_report/py_report.h index 89dca69ef..a684b81eb 100644 --- a/src/interface/python/py_report/py_report.h +++ b/src/interface/python/py_report/py_report.h @@ -16,18 +16,20 @@ // *************************************************************************************** #pragma once +#include +#include #include #include #include "report_manager.h" namespace python_interface { -bool reportDbSummary(const std::string& path); -bool reportWireLength(const std::string& path); -bool reportCong(const std::string& path); -bool reportDanglingNet(const std::string& path); -bool reportRoute(const std::string& path, const std::string& netname, bool summary); +bool reportDbSummary(const std::optional& path); +bool reportWireLength(const std::optional& path); +bool reportCong(const std::optional& path); +bool reportDanglingNet(const std::optional& path); +bool reportRoute(const std::optional& path, const std::string& netname, bool summary); bool reportPlaceDistribution(const std::vector& prefixes); bool reportPrefixedInst(const std::string& prefix, int level, int num_threshold); -bool reportDRC(const std::string& filename); +bool reportDRC(const std::filesystem::path& filename); } // namespace python_interface \ No newline at end of file diff --git a/src/interface/python/test/py_path_utils_test.cc b/src/interface/python/test/py_path_utils_test.cc new file mode 100644 index 000000000..df934bdfc --- /dev/null +++ b/src/interface/python/test/py_path_utils_test.cc @@ -0,0 +1,46 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +// Self-contained compile-plus-assert fixture for py_path_utils.h. Build: +// g++ -std=c++20 -Wall -Werror src/interface/python/test/py_path_utils_test.cc -o /tmp/py_path_utils_test +#include +#include +#include +#include + +#include "../py_path_utils.h" + +// Compile-time guard against calling .empty() directly on a converted +// optional path parameter: std::optional has no such +// member, so misuse fails to compile. The concept must stay dependent on T; +// a bare requires-expression on a concrete type is a hard error, not a +// false constraint. +template +concept has_empty_member = requires(T t) { t.empty(); }; +static_assert(not has_empty_member>); +static_assert(has_empty_member); // control + +int main() +{ + using python_interface::path_or_empty; + + assert(path_or_empty(std::nullopt) == ""); + assert(path_or_empty(std::optional{std::filesystem::path{}}) == ""); + assert(path_or_empty(std::optional{std::filesystem::path{""}}) == ""); + assert(path_or_empty(std::optional{std::filesystem::path{"foo/bar"}}) == "foo/bar"); + + return 0; +} diff --git a/tests/test_pathlike_contract.py b/tests/test_pathlike_contract.py new file mode 100644 index 000000000..910ce0aa2 --- /dev/null +++ b/tests/test_pathlike_contract.py @@ -0,0 +1,355 @@ +"""Behavior contract suite for the os.PathLike bindings in ecc_py. + +Runs against an INSTALLED ecc-tools-bin wheel, not the source tree: the +source tree ships an ``ecc_tools_bin`` package without the compiled +extension, so the import guard below fails loudly if the suite is +accidentally run with the repo root on ``sys.path``. + +Calls that mutate global C++ state (the ``*_init`` family, ``save_data``, +``idb_get``) are executed in a fresh subprocess each so one test cannot +poison the next through module-level state. +""" + +import subprocess +import sys +import textwrap + +import pytest + +from ecc_tools_bin import ecc_py + + +def test_installed_wheel_is_imported(): + # The source tree's ecc_tools_bin/ has no compiled extension; the + # installed wheel resolves ecc_py to a .so inside site-packages. + path = getattr(ecc_py, "__file__", "") or "" + print(f"ecc_py imported from: {path}") + assert path.endswith(".so"), f"ecc_py is not the compiled extension: {path!r}" + assert "site-packages" in path, f"ecc_py not imported from an installed wheel: {path!r}" + + +def _run(body, cwd): + """Run ``body`` in a fresh interpreter; assertions inside it gate the exit code.""" + script = "from ecc_tools_bin import ecc_py\nfrom pathlib import Path\n\n" + textwrap.dedent(body) + proc = subprocess.run( + [sys.executable, "-c", script], + cwd=cwd, + capture_output=True, + text=True, + ) + assert proc.returncode == 0, ( + f"subprocess failed with exit code {proc.returncode}\n" + f"--- stdout ---\n{proc.stdout}\n--- stderr ---\n{proc.stderr}" + ) + + +# --------------------------------------------------------------------------- +# 1. input-file path: def_init +# --------------------------------------------------------------------------- + + +def test_def_init_accepts_str_path_and_custom_pathlike(tmp_path): + _run( + """ + class CustomPath: + def __init__(self, path): + self._path = path + def __fspath__(self): + return self._path + + target = '/nonexistent/input.def' + expected = ecc_py.def_init(target) + assert expected is False + assert ecc_py.def_init(Path(target)) == expected + assert ecc_py.def_init(CustomPath(target)) == expected + """, + cwd=tmp_path, + ) + + +def test_def_init_rejects_non_pathlike(tmp_path): + _run( + """ + for bad in (None, 5): + try: + ecc_py.def_init(bad) + except TypeError: + pass + else: + raise AssertionError(f'def_init({bad!r}) did not raise TypeError') + """, + cwd=tmp_path, + ) + + +# --------------------------------------------------------------------------- +# 2. output-file path: save_data +# --------------------------------------------------------------------------- + + +def test_save_data_path_matches_str(tmp_path): + _run( + """ + expected = ecc_py.save_data('/nonexistent/out.db') + assert expected is False + assert ecc_py.save_data(Path('/nonexistent/out.db')) == expected + """, + cwd=tmp_path, + ) + + +def test_save_data_rejects_none(tmp_path): + _run( + """ + try: + ecc_py.save_data(None) + except TypeError: + pass + else: + raise AssertionError('save_data(None) did not raise TypeError') + """, + cwd=tmp_path, + ) + + +# --------------------------------------------------------------------------- +# 3. optional config path: init_rt +# --------------------------------------------------------------------------- + + +def test_init_rt_none_and_empty_match_omitted(tmp_path): + _run( + """ + omitted = ecc_py.init_rt(config_dict={}) + assert omitted is False + assert ecc_py.init_rt(config=None, config_dict={}) == omitted + assert ecc_py.init_rt(config='', config_dict={}) == omitted + """, + cwd=tmp_path, + ) + + +def test_init_rt_path_matches_str(tmp_path): + _run( + """ + expected = ecc_py.init_rt(config='/nonexistent/rt.toml', config_dict={}) + assert expected is False + assert ecc_py.init_rt(config=Path('/nonexistent/rt.toml'), config_dict={}) == expected + """, + cwd=tmp_path, + ) + + +def test_init_rt_rejects_non_pathlike_config(tmp_path): + _run( + """ + try: + ecc_py.init_rt(config=5, config_dict={}) + except TypeError: + pass + else: + raise AssertionError('init_rt(config=5) did not raise TypeError') + """, + cwd=tmp_path, + ) + + +def test_init_rt_config_dict_still_requires_str_values(tmp_path): + _run( + """ + try: + ecc_py.init_rt(config='', config_dict={'-temp_directory_path': Path('/tmp/x')}) + except TypeError: + pass + else: + raise AssertionError('init_rt accepted a Path value in config_dict') + """, + cwd=tmp_path, + ) + + +# --------------------------------------------------------------------------- +# 4. optional report/save path: idb_get +# --------------------------------------------------------------------------- + + +def test_idb_get_none_and_empty_match_omitted(tmp_path): + _run( + """ + omitted = ecc_py.idb_get() + assert omitted is False + assert ecc_py.idb_get(file_name=None) == omitted + assert ecc_py.idb_get(file_name='') == omitted + """, + cwd=tmp_path, + ) + + +def test_idb_get_path_matches_str(tmp_path): + _run( + """ + expected = ecc_py.idb_get(file_name='/nonexistent/out.db') + assert expected is False + assert ecc_py.idb_get(file_name=Path('/nonexistent/out.db')) == expected + """, + cwd=tmp_path, + ) + + +def test_idb_get_rejects_non_pathlike_file_name(tmp_path): + _run( + """ + try: + ecc_py.idb_get(file_name=5) + except TypeError: + pass + else: + raise AssertionError('idb_get(file_name=5) did not raise TypeError') + """, + cwd=tmp_path, + ) + + +# --------------------------------------------------------------------------- +# 5. list path: lef_init +# --------------------------------------------------------------------------- + + +def test_lef_init_accepts_str_path_mixed_and_custom(tmp_path): + _run( + """ + class CustomPath: + def __init__(self, path): + self._path = path + def __fspath__(self): + return self._path + + a = '/nonexistent/a.lef' + b = '/nonexistent/b.lef' + expected = ecc_py.lef_init([a, b]) + assert expected is True + assert ecc_py.lef_init([Path(a), Path(b)]) == expected + assert ecc_py.lef_init([a, Path(b), CustomPath(a)]) == expected + """, + cwd=tmp_path, + ) + + +def test_lef_init_rejects_non_pathlike_elements(tmp_path): + _run( + """ + for bad in ([None], [3]): + try: + ecc_py.lef_init(bad) + except TypeError: + pass + else: + raise AssertionError(f'lef_init({bad!r}) did not raise TypeError') + """, + cwd=tmp_path, + ) + + +# --------------------------------------------------------------------------- +# 6. sdc equivalence: sdc_init +# --------------------------------------------------------------------------- + + +def test_sdc_init_none_and_empty_match_omitted(tmp_path): + _run( + """ + omitted = ecc_py.sdc_init() + assert omitted is True + assert ecc_py.sdc_init(None) == omitted + assert ecc_py.sdc_init('') == omitted + """, + cwd=tmp_path, + ) + + +def test_sdc_init_path_matches_str(tmp_path): + _run( + """ + expected = ecc_py.sdc_init('/nonexistent.sdc') + assert ecc_py.sdc_init(Path('/nonexistent.sdc')) == expected + """, + cwd=tmp_path, + ) + + +def test_sdc_init_rejects_non_pathlike(tmp_path): + _run( + """ + try: + ecc_py.sdc_init(5) + except TypeError: + pass + else: + raise AssertionError('sdc_init(5) did not raise TypeError') + """, + cwd=tmp_path, + ) + + +# --------------------------------------------------------------------------- +# 7. doc smoke: rendered signatures advertise os.PathLike +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name", + [ + "sdc_init", + "def_init", + "lef_init", + "init_rt", + "idb_get", + "cell_density", + "report_drc", + "init_rcx", + "db_init", + ], +) +def test_doc_mentions_pathlike(name): + doc = getattr(ecc_py, name).__doc__ or "" + assert "os.PathLike" in doc, f"{name} doc does not mention os.PathLike: {doc!r}" + + +@pytest.mark.parametrize( + "name,param", + [ + ("sdc_init", "sdc_path"), + ("init_rt", "config"), + ("idb_get", "file_name"), + ("cell_density", "save_path"), + ("db_init", "config_path"), + ("db_init", "def_path"), + ("db_init", "sdc_path"), + ], +) +def test_doc_optional_pathlike_defaults_to_none(name, param): + doc = getattr(ecc_py, name).__doc__ or "" + assert f"{param}: Optional[os.PathLike] = None" in doc, ( + f"{name} doc does not render '{param}: Optional[os.PathLike] = None': {doc!r}" + ) + + +def test_doc_lef_init_renders_list_of_pathlike(): + doc = ecc_py.lef_init.__doc__ or "" + assert "lef_paths: List[os.PathLike]" in doc, f"lef_init doc: {doc!r}" + + +def test_doc_verilog_init_top_module_still_str(): + doc = ecc_py.verilog_init.__doc__ or "" + assert "top_module: str" in doc, f"verilog_init doc: {doc!r}" + + +def test_doc_init_rcx_pdk_still_optional_str(): + doc = ecc_py.init_rcx.__doc__ or "" + assert "pdk: Optional[str] = None" in doc, f"init_rcx doc: {doc!r}" + + +def test_doc_init_rt_config_dict_still_dict(): + doc = ecc_py.init_rt.__doc__ or "" + assert "config_dict: Dict" in doc, f"init_rt doc: {doc!r}" + assert "config_dict: os.PathLike" not in doc, f"init_rt doc: {doc!r}"