From bfeeac6e4889d1e9a1083b3c7efc59652981b168 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Mon, 1 Aug 2016 08:20:23 +0200 Subject: o Moving the generation logic to Python, embedding a Python interpreter with the help of pybind11. o Adding install configuration to CMake to make it easier to reuse the project later on. o Splitting out the examples into its own project to make it easier to test the whole installation setup and python/template loading. o Trying to reorganize the code a bit, not very much better yet. --- cli/CMakeLists.txt | 29 +++ cli/generate-header.cpp | 466 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 495 insertions(+) create mode 100644 cli/CMakeLists.txt create mode 100644 cli/generate-header.cpp (limited to 'cli') diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..f3dd326 --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,29 @@ +set(SOURCE_FILES generate-header.cpp) +add_executable(generate-header ${SOURCE_FILES}) + +target_link_libraries(generate-header PUBLIC kicad-utils-core) + +get_target_property(kicad_utils_py_include_directories kicad_utils_py INCLUDE_DIRECTORIES) +target_include_directories(generate-header PRIVATE ${kicad_utils_py_include_directories}) +target_link_libraries(generate-header PUBLIC ${PYTHON_LIBRARIES} stdc++fs) + +add_dependencies(generate-header kicad_utils_py) + +install(TARGETS generate-header + EXPORT kicad_utils_export + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) + +install(DIRECTORY ../template/ + DESTINATION share/kicad-utils/template + FILES_MATCHING PATTERN *.py + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + +install(DIRECTORY ../cmake/ + DESTINATION lib/cmake/KicadUtils) + +install(EXPORT kicad_utils_export + FILE KicadUtilsTargets.cmake + NAMESPACE KicadUtils:: + DESTINATION lib/cmake/KicadUtils) diff --git a/cli/generate-header.cpp b/cli/generate-header.cpp new file mode 100644 index 0000000..1033fc3 --- /dev/null +++ b/cli/generate-header.cpp @@ -0,0 +1,466 @@ +#include "trygvis/kicad.h" + +using namespace trygvis::kicad; +using trygvis::kicad::netlist::component; +using trygvis::kicad::netlist::kicad_net_loader; +using trygvis::kicad::netlist::kicad_parse_exception; +using trygvis::kicad::netlist::net; +using nl = trygvis::kicad::netlist::netlist; + +#include "pybind11/pybind11.h" +#include "pybind11/stl.h" +#include "pybind11/eval.h" + +namespace py = pybind11; + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +static const char PATH_SEPARATOR = ';'; +#else +static const char PATH_SEPARATOR = ':'; +#endif + +using namespace std; +namespace fs = std::experimental::filesystem; + +char *program; + +__attribute__((noreturn)) +void usage(const char *reason = nullptr) { + if (reason) { + fprintf(stderr, "%s\n", reason); + } + fprintf(stderr, "usage: %s -n -r [-o ] [-t ]\n", program); + exit(EX_USAGE); +} + +void parse_args(int argc, char **argv, bool *debug, char **net_filename, char **ref, char **py_template, + vector &template_libs, char **out_filename) { + *debug = false; + *ref = nullptr; + *out_filename = nullptr; + *py_template = nullptr; + *ref = nullptr; + + int c; + while ((c = getopt(argc, argv, "Dn:r:o:t:l:")) != -1) { + switch (c) { + case 'D': + *debug = true; + break; + case 'n': + *net_filename = optarg; + break; + case 'r': + *ref = optarg; + break; + case 't': + *py_template = optarg; + break; + case 'l': + template_libs.push_back(fs::path(optarg)); + break; + case 'o': + if (strcmp("-", optarg) != 0) { + *out_filename = optarg; + } + break; + default: + usage(); + } + } + + if (!*net_filename || !*ref) { + usage(); + } +} + +class part { +public: + part(const string &name) : name(name) { + } + + virtual ~part() { + } + + const string name; + + virtual bool generate(stringstream &out, const string &ref, const vector usages) = 0; +}; + +class arduino_uno : public part { +public: + arduino_uno() : part("ARDUINO_UNO") {} + + ~arduino_uno() { + } + + virtual bool generate(stringstream &out, const string &ref, const vector usages) override { + out << "namespace schematic {" << endl; + for (auto &usage: usages) { + auto node = usage->node_for_ref(ref); + + if (node->pin >= 7 && node->pin <= 12) { + out << "static const int ANALOG_" << usage->name << " = " << (node->pin - 7) << ";" << endl; + } else if (node->pin >= 13 && node->pin <= 26) { + out << "static const int " << usage->name << " = " << (node->pin - 13) << ";" << endl; + } + } + out << "} // namespace schematic" << endl; + + return true; + } +}; + +class intel_quark_d2000 : public part { +public: + intel_quark_d2000() : part("INTEL_QUARK_D2000") {} + + ~intel_quark_d2000() { + } + + virtual bool generate(stringstream &out, const string &ref, const vector usages) override { + static map gpio_map{ + {2, "10"}, + {3, "11"}, + {4, "12"}, + {5, "13"}, + {6, "14"}, + {7, "15"}, + {8, "16"}, + {9, "17"}, + {10, "18"}, + + {11, "9"}, + {13, "20"}, + {14, "21"}, + {15, "22"}, + {16, "23"}, + {18, "19"}, + + {21, "24"}, + + {31, "0"}, + {32, "1"}, + {33, "2"}, + {34, "3"}, + {35, "4"}, + {36, "5"}, + {37, "6"}, + {38, "7"}, + {39, "8"}, + }; + + out << "#include " << endl; + out << "#include " << endl; + out << endl; + out << "enum schematic_direction {" << endl; + out << " schematic_direction_out = 1," << endl; + out << " schematic_direction_in = 2" << endl; + out << "};" << endl;; + + for (auto &usage: usages) { + auto node = usage->node_for_ref(ref); + + auto gpio = gpio_map.find(node->pin); + + if (gpio == gpio_map.end()) { + continue; + } + + out << "static const uint8_t SCHEMATIC_" << usage->name << " = " << gpio->second << ";" << endl; + out << endl; + out << "static inline" << endl; + out << "qm_rc_t schematic_PUSH_BUTTON_direction(enum schematic_direction dir) {" << endl; + out << " qm_gpio_port_config_t cfg;" << endl; + out << "" << endl; + out << " qm_gpio_get_config(QM_GPIO_0, &cfg);" << endl; + out << "" << endl; + out << " if (dir == schematic_direction_out) {" << endl; + out << " cfg.direction |= BIT(SCHEMATIC_PUSH_BUTTON);" << endl; + out << " } else {" << endl; + out << " cfg.direction &= ~BIT(SCHEMATIC_PUSH_BUTTON);" << endl; + out << " }" << endl; + out << "" << endl; + out << " return qm_gpio_set_config(QM_GPIO_0, &cfg);" << endl; + out << "}" << endl; + out << endl; + } + + return true; + } +}; + +//class library { +//public: +// library(const string &name) : name(name) {} +// +// library(library &&l) : name(std::move(l.name)), parts(std::move(l.parts)) { } +// +// virtual ~library() { } +// +// const string name; +// vector> parts; +// +// opt find_part(string name) const { +// for (auto &part: parts) { +// if (part->name == name) { +// return part.get(); +// } +// } +// +// return nullopt; +// } +//}; + +//class kicad_gen { +//public: +// vector libraries; +// +// kicad_gen() { +// library kicad_utils("kicad_utils"); +// kicad_utils.parts.push_back(make_unique()); +// kicad_utils.parts.push_back(make_unique()); +// +// libraries.push_back(std::move(kicad_utils)); +// } +// +// opt find_library(string name) { +// for (auto &l : libraries) { +// if (l.name == name) { +// return &l; +// } +// } +// +// return nullopt; +// } +//}; + +opt find_template(const string &py_template, const vector &paths) { + + fs::path p(py_template); + + if (fs::exists(p)) { + return p; + } + + for (auto &&prefix : paths) { + p = prefix / py_template; + + if (fs::exists(p)) { + return p; + } + } + + return nullopt; +} + +/* +bool generate(const char *ref, nl *const netlist, stringstream &out) { + kicad_gen gen; + opt componentO = netlist->find_component(ref); + + if (!componentO) { + cerr << "Could not find component '" << ref << "'" << endl; + return false; + } + + const component *c = *componentO; + + cerr << "Generating connections for " << c->ref << " (" << c->value << ")" << endl; + + auto libraryO = gen.find_library(c->_lib_source.lib); + + if (!libraryO) { + cerr << "Unsupported library '" << c->_lib_source.lib << "'" << endl; + return false; + } + + const library *library = *libraryO; + + auto partName = c->_lib_source.part; + + auto partO = library->find_part(partName); + + if (!partO) { + cerr << "The library library " << library->name << " does not have a component named " << partName << endl; + return false; + } + + part *part = *partO; + + out << "#ifndef SCHEMATIC_H" << endl; + out << "#define SCHEMATIC_H" << endl; + out << endl; + out << "*/ +/*" << endl; + out << "THIS FILE IS GENERATED. DO NOT EDIT." << endl; + out << endl; + out << "Generated from schematic for reference " << ref << ", part " << partName << " in library " << + library->name << "." << endl; + out << "*//* +" << endl; + out << endl; + + auto usages = netlist->find_usage_of(ref); + + part->generate(out, ref, usages); + + out << "#endif // SCHEMATIC_H" << endl; + + return true; +} +*/ + +bool generate_py(const char *ref, nl *const netlist, stringstream &out, const fs::path &py_template) { + py::dict vars; + pybind11::module kicad_utils_py_module = py::module::import("kicad_utils_py"); + vars = kicad_utils_py_module.attr("__dict__"); + + pybind11::object netlist_py = py::cast(netlist); + + vars["netlist"] = netlist_py; + vars["ref"] = py::cast(ref); + + string init; + init += "generateHeader = GenerateHeader(ref, netlist)\n"; + init += "del ref\n"; + init += "del netlist\n"; + py::eval(init, vars); + + py::eval_file(py_template.string(), vars); + + string str = py::eval("generateHeader.str", vars).cast().cast(); + out << str; + + return true; +} + +int main(int argc, char **argv) { + bool debug; + char *net_filename; + char *ref; + char *py_template; + vector template_libs; + char *out_filename; + + fs::path bin(argv[0]); + fs::path basedir = bin.parent_path().parent_path(); + + template_libs.push_back(fs::current_path()); + template_libs.push_back(basedir / "share" / "kicad-utils" / "template"); + + parse_args(argc, argv, &debug, &net_filename, &ref, &py_template, template_libs, &out_filename); + + kicad_net_loader loader; + + /* + string python_version = Py_GetVersion(); + cout << "Python version: " << python_version << endl; + */ + + /* + map env; + for (char **e = environ; *e != NULL; e++) { + string s = *e; + auto i = s.find('='); + env[s.substr(0, i)] = s.substr(i + 1); + } + + for (auto &&e: env) { + cout << e.first << " = " << e.second << endl; + } + */ + + try { + nl netlist = loader.load(net_filename, cerr); + + stringstream out; + bool ok = false; + + if (py_template) { + cerr << "Template path: " << endl; + for (auto &&p : template_libs) { + cerr << " " << p.string() << endl; + } + + opt tmpl = find_template(py_template, template_libs); + + if (!tmpl) { + cerr << "No such file: " << py_template << endl; + cerr << "Search path: " << endl; + for (auto &&p : template_libs) { + cerr << " " << p.string() << endl; + } + cerr << "Current path is " << fs::current_path().string() << endl; + } else { + wchar_t *program = Py_DecodeLocale(argv[0], NULL); + if (program == NULL) { + fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + exit(1); + } + Py_SetProgramName(program); + + vector module_paths{ + basedir / "python", + basedir / "lib", + }; + + std::wstring_convert > converter; + converter.from_bytes(string("foo")); + + wstring pythonpath(Py_GetPath()); + for (auto &path : module_paths) { + if (!fs::exists(path)) { + continue; + } +// pythonpath += converter.from_bytes(PATH_SEPARATOR + path.string()); + pythonpath = converter.from_bytes(path.string() + PATH_SEPARATOR) + pythonpath; + } + wcerr << "pythonpath=" << pythonpath << endl; + Py_SetPath(pythonpath.c_str()); + + cerr << "Compile-time Python version: " << PY_VERSION << endl; + cerr << "Current python version: " << Py_GetVersion() << endl; + + Py_Initialize(); + try { + ok = generate_py(ref, &netlist, out, tmpl.value()); + } catch (py::error_already_set &e) { + cerr << "Python failure:" << endl << e.what() << endl; + } catch (std::runtime_error &e) { + cerr << "Generation failed: " << e.what() << endl; + } + Py_Finalize(); + PyMem_RawFree(program); + } +// } else { +// ok = generate(ref, &netlist, out); + } + + if (ok) { + auto contents = out.str(); + if (out_filename != nullptr) { + ofstream outstream(out_filename); + outstream << contents; + outstream.close(); + } else { + cout << contents << flush; + } + return EXIT_SUCCESS; + } + return EXIT_FAILURE; + } catch (kicad_parse_exception &e) { + for (auto &m: e.messages) { + fprintf(stderr, "%s\n", m.c_str()); + } + errx(EX_DATAERR, "Could not parse netlist"); + } +} -- cgit v1.2.3