diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2016-08-01 08:20:23 +0200 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2016-08-01 08:20:23 +0200 |
commit | bfeeac6e4889d1e9a1083b3c7efc59652981b168 (patch) | |
tree | a937d844a59da7c509685dcd1ddd9933772e526f | |
parent | c307e9f234e544386fa3ae53083c7510668e1716 (diff) | |
download | kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.tar.gz kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.tar.bz2 kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.tar.xz kicad-utils-bfeeac6e4889d1e9a1083b3c7efc59652981b168.zip |
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.
25 files changed, 596 insertions, 98 deletions
diff --git a/.gitmodules b/.gitmodules index a84f300..83b6a4d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,7 @@ [submodule "pybind11"] - path = pybind11 + path = python/pybind11 url = https://github.com/pybind/pybind11.git branch = master +[submodule "python/pybind11"] + path = python/pybind11 + url = https://github.com/pybind/pybind11 diff --git a/CMakeLists.txt b/CMakeLists.txt index 926f7c0..e70bce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,13 @@ cmake_minimum_required(VERSION 3.5) project(kicad_utils) -find_package(Antlr4) -antlr4_add_target(TARGET KicadNet LEXER KicadNetLexer.g4 PARSER KicadNetParser.g4 STATIC) +add_compile_options(--std=c++14) +add_compile_options(-Wall) +add_compile_options(-Wextra) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") +add_subdirectory(core) +add_subdirectory(python) +add_subdirectory(cli) +#add_subdirectory(examples) -set(SOURCE_FILES generate-header.cpp kicad.cpp include/trygvis/kicad.h include-priv/trygvis/antlr.h include-priv/trygvis/string_utils.h) -add_executable(generate-header ${SOURCE_FILES}) -target_include_directories(generate-header PUBLIC include PRIVATE include-priv) -target_link_libraries(generate-header KicadNet Antlr4::antlr4_shared) -target_compile_options(generate-header PUBLIC --std=c++14) -target_compile_options(generate-header PUBLIC -Wall -Wextra) - -include(kicad.cmake) -add_custom_target(all_examples) -add_subdirectory(examples) +message(STATUS "The examples are not built as they depend on a full installation of kicad-utils. Do a make install first and then build the examples") 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/generate-header.cpp b/cli/generate-header.cpp index d134d47..1033fc3 100644 --- a/generate-header.cpp +++ b/cli/generate-header.cpp @@ -1,21 +1,34 @@ -#include <iostream> -#include <fstream> -#include <sstream> -#include <err.h> -#include <sysexits.h> -#include <getopt.h> -#include <cstring> -#include <memory> -#include <map> #include "trygvis/kicad.h" -using namespace std; 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 <fstream> +#include <sstream> +#include <err.h> +#include <sysexits.h> +#include <map> +#include <cstring> +#include <experimental/filesystem> + +#ifdef _WIN32 +static const char PATH_SEPARATOR = ';'; +#else +static const char PATH_SEPARATOR = ':'; +#endif + +using namespace std; +namespace fs = std::experimental::filesystem; char *program; @@ -24,18 +37,20 @@ void usage(const char *reason = nullptr) { if (reason) { fprintf(stderr, "%s\n", reason); } - fprintf(stderr, "usage: %s -n <net file> -r <ref> [-o <out file>]\n", program); + fprintf(stderr, "usage: %s -n <net file> -r <ref> [-o <out file>] [-t <python template>]\n", program); exit(EX_USAGE); } -void parse_args(int argc, char **argv, bool *debug, char **net_filename, char **ref, char **out_filename) { +void parse_args(int argc, char **argv, bool *debug, char **net_filename, char **ref, char **py_template, + vector<fs::path> &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:")) != -1) { + while ((c = getopt(argc, argv, "Dn:r:o:t:l:")) != -1) { switch (c) { case 'D': *debug = true; @@ -46,6 +61,12 @@ void parse_args(int argc, char **argv, bool *debug, char **net_filename, char ** 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; @@ -56,7 +77,7 @@ void parse_args(int argc, char **argv, bool *debug, char **net_filename, char ** } } - if (!*net_filename && !*ref) { + if (!*net_filename || !*ref) { usage(); } } @@ -76,7 +97,7 @@ public: class arduino_uno : public part { public: - arduino_uno() : part("ARDUINO_UNO") { } + arduino_uno() : part("ARDUINO_UNO") {} ~arduino_uno() { } @@ -100,7 +121,7 @@ public: class intel_quark_d2000 : public part { public: - intel_quark_d2000() : part("INTEL_QUARK_D2000") { } + intel_quark_d2000() : part("INTEL_QUARK_D2000") {} ~intel_quark_d2000() { } @@ -177,52 +198,74 @@ public: } }; -class library { -public: - library(const string &name) : name(name) { } - - library(library &&l) : name(std::move(l.name)), parts(std::move(l.parts)) { } - - const string name; - vector<unique_ptr<part>> parts; - - opt<part *> find_part(string name) const { - for (auto &part: parts) { - if (part->name == name) { - return part.get(); - } - } - - return nullopt; +//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<unique_ptr<part>> parts; +// +// opt<part *> find_part(string name) const { +// for (auto &part: parts) { +// if (part->name == name) { +// return part.get(); +// } +// } +// +// return nullopt; +// } +//}; + +//class kicad_gen { +//public: +// vector<library> libraries; +// +// kicad_gen() { +// library kicad_utils("kicad_utils"); +// kicad_utils.parts.push_back(make_unique<arduino_uno>()); +// kicad_utils.parts.push_back(make_unique<intel_quark_d2000>()); +// +// libraries.push_back(std::move(kicad_utils)); +// } +// +// opt<library *> find_library(string name) { +// for (auto &l : libraries) { +// if (l.name == name) { +// return &l; +// } +// } +// +// return nullopt; +// } +//}; + +opt<fs::path> find_template(const string &py_template, const vector<fs::path> &paths) { + + fs::path p(py_template); + + if (fs::exists(p)) { + return p; } -}; - -class kicad_gen { -public: - vector<library> libraries; - - kicad_gen() { - library kicad_utils("kicad_utils"); - kicad_utils.parts.push_back(make_unique<arduino_uno>()); - kicad_utils.parts.push_back(make_unique<intel_quark_d2000>()); - libraries.push_back(std::move(kicad_utils)); - } + for (auto &&prefix : paths) { + p = prefix / py_template; - opt<library *> find_library(string name) { - for (auto &l : libraries) { - if (l.name == name) { - return &l; - } + if (fs::exists(p)) { + return p; } - - return nullopt; } -}; -bool generate(const char *ref, const trygvis::kicad::netlist::netlist &netlist, stringstream &out) { + return nullopt; +} + +/* +bool generate(const char *ref, nl *const netlist, stringstream &out) { kicad_gen gen; - opt<const component *> componentO = netlist.find_component(ref); + opt<const component *> componentO = netlist->find_component(ref); if (!componentO) { cerr << "Could not find component '" << ref << "'" << endl; @@ -256,15 +299,17 @@ bool generate(const char *ref, const trygvis::kicad::netlist::netlist &netlist, out << "#ifndef SCHEMATIC_H" << endl; out << "#define SCHEMATIC_H" << endl; out << 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; + library->name << "." << endl; + out << "*//* +" << endl; out << endl; - auto usages = netlist.find_usage_of(ref); + auto usages = netlist->find_usage_of(ref); part->generate(out, ref, usages); @@ -272,23 +317,133 @@ bool generate(const char *ref, const trygvis::kicad::netlist::netlist &netlist, return true; } +*/ -int main(int argc, char **argv) { - program = argv[0]; +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<py::eval_mode::eval_statements>(init, vars); + + py::eval_file(py_template.string(), vars); + + string str = py::eval<py::eval_mode::eval_expr>("generateHeader.str", vars).cast<py::object>().cast<string>(); + out << str; + + return true; +} + +int main(int argc, char **argv) { bool debug; char *net_filename; char *ref; + char *py_template; + vector<fs::path> template_libs; char *out_filename; - parse_args(argc, argv, &debug, &net_filename, &ref, &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<string, string> 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 { - auto netlist = loader.load(net_filename); + nl netlist = loader.load(net_filename, cerr); stringstream out; - auto ok = generate(ref, netlist, out); + bool ok = false; + + if (py_template) { + cerr << "Template path: " << endl; + for (auto &&p : template_libs) { + cerr << " " << p.string() << endl; + } + + opt<fs::path> 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<fs::path> module_paths{ + basedir / "python", + basedir / "lib", + }; + + std::wstring_convert<codecvt_utf8<wchar_t> > 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(); diff --git a/cmake/KicadUtilsConfig.cmake b/cmake/KicadUtilsConfig.cmake new file mode 100644 index 0000000..2302fe2 --- /dev/null +++ b/cmake/KicadUtilsConfig.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/kicad_generate_header.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/KicadUtilsTargets.cmake") diff --git a/kicad.cmake b/cmake/kicad_generate_header.cmake index f18e6fe..930c5ed 100644 --- a/kicad.cmake +++ b/cmake/kicad_generate_header.cmake @@ -1,6 +1,6 @@ function(kicad_generate_header) set(options IN_SOURCE) - set(oneValueArgs OUTPUT NET REF OUTPUT_DIR) + set(oneValueArgs OUTPUT NET REF OUTPUT_DIR TEMPLATE TEMPLATE_LIB_LIST) cmake_parse_arguments(kicad_gen "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -25,19 +25,33 @@ function(kicad_generate_header) endif () set(output_dir "${kicad_gen_OUTPUT_DIR}") + set(output_rel_dir "${output_dir}") else () if (kicad_gen_IN_SOURCE) set(output_dir "${CMAKE_CURRENT_SOURCE_DIR}") + set(output_rel_dir "${CMAKE_SOURCE_DIR}") else () set(output_dir "${CMAKE_CURRENT_BINARY_DIR}") + set(output_rel_dir "${CMAKE_BINARY_DIR}") endif () endif () - set(output_file "${output_dir}/${output}") + if (kicad_gen_TEMPLATE) + set(t "-t") + set(template "${kicad_gen_TEMPLATE}") + endif () + + set(lib "-l;${CMAKE_CURRENT_SOURCE_DIR}") + foreach(l IN LISTS kicad_gen_TEMPLATE_LIB_LIST) + list(APPEND lib -l) + list(APPEND lib "${l}") + endforeach () + + set(output_file "${output_dir}/${output}") + file(RELATIVE_PATH output_file_rel "${output_rel_dir}" ${output_file}) add_custom_command(OUTPUT "${output_file}" - COMMAND generate-header -n ${NET} -r ${REF} -o ${output_file} + COMMAND KicadUtils::generate-header -n ${NET} -r ${REF} -o ${output_file} ${t} ${template} ${lib} MAIN_DEPENDENCY ${NET} - DEPENDS kicad_utils - COMMENT "Generating ${output}") + COMMENT "Generating ${output_file_rel}") endfunction() diff --git a/configure b/configure new file mode 100755 index 0000000..4aaae1c --- /dev/null +++ b/configure @@ -0,0 +1,51 @@ +#!/bin/bash + +PROJECT=kicad-utils + +set -e + +CMAKE=`which cmake 2>/dev/null` +PREFIX=$HOME/opt/$PROJECT +# TODO: find basedir from the current binary +BASEDIR=`pwd` + +if [ ! -x "$CMAKE" ] +then + echo "cmake is not installed" + exit 1 +fi + +cd $BASEDIR + +NEW=`test -d build && echo 1 || true` + +mkdir -p build + +CMAKE_OPTS=() +CMAKE_OPTS+=(-DCMAKE_INSTALL_PREFIX=$PREFIX) +#CMAKE_OPTS+=(-DCMAKE_SKIP_RPATH=NO) +#CMAKE_OPTS+=(-DCMAKE_SKIP_INSTALL_RPATH=NO) +# These are required until Antlr C++ is installed by the system +CMAKE_OPTS+=(-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE) +CMAKE_OPTS+=(-DAntlr4_DIR=$HOME/opt/antlr4-cpp/lib/cmake/Antlr4) + +cd build +echo "" +echo "Generating build.." +echo "" +cmake "${CMAKE_OPTS[@]}" .. + + +echo "" +echo "" +echo "" + +if [[ $NEW == 1 ]] +then + echo "Reusing existing build/ directory. You should probably run cd build && make clean" +else + echo "The build files are generated in build/. To build run: cd build && make install" + echo "The binaries will be installed under $PREFIX" +fi + +echo "" diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt new file mode 100644 index 0000000..cbf8e78 --- /dev/null +++ b/core/CMakeLists.txt @@ -0,0 +1,22 @@ +find_package(Antlr4) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +antlr4_add_target( + TARGET KicadNet + LEXER KicadNetLexer.g4 + PARSER KicadNetParser.g4 + STATIC) + +add_library(kicad-utils-core + kicad.cpp) +target_sources(kicad-utils-core PRIVATE $<TARGET_OBJECTS:KicadNet>) + +get_target_property(KicadNet_includes KicadNet INCLUDE_DIRECTORIES) + +target_include_directories(kicad-utils-core + PUBLIC include + PRIVATE include-priv ${KicadNet_includes}) + +target_link_libraries(kicad-utils-core + Antlr4::antlr4_shared) diff --git a/KicadNetLexer.g4 b/core/KicadNetLexer.g4 index e10f7e7..e10f7e7 100644 --- a/KicadNetLexer.g4 +++ b/core/KicadNetLexer.g4 diff --git a/KicadNetParser.g4 b/core/KicadNetParser.g4 index 3971111..3971111 100644 --- a/KicadNetParser.g4 +++ b/core/KicadNetParser.g4 diff --git a/include-priv/trygvis/antlr.h b/core/include-priv/trygvis/antlr.h index f5656ea..f5656ea 100644 --- a/include-priv/trygvis/antlr.h +++ b/core/include-priv/trygvis/antlr.h diff --git a/include-priv/trygvis/string_utils.h b/core/include-priv/trygvis/string_utils.h index 9902a3f..9902a3f 100644 --- a/include-priv/trygvis/string_utils.h +++ b/core/include-priv/trygvis/string_utils.h diff --git a/include/trygvis/kicad.h b/core/include/trygvis/kicad.h index e555e9c..0c5005d 100644 --- a/include/trygvis/kicad.h +++ b/core/include/trygvis/kicad.h @@ -1,9 +1,10 @@ #pragma once +#include <experimental/optional> +#include <ostream> +#include <stdexcept> #include <string> #include <vector> -#include <stdexcept> -#include <experimental/optional> namespace trygvis { namespace kicad { @@ -21,7 +22,7 @@ struct lib_source { const std::string lib; const std::string part; - lib_source(const std::string &lib, const std::string &part) : lib(lib), part(part) { } + lib_source(const std::string &lib, const std::string &part) : lib(lib), part(part) {} }; struct component { @@ -30,7 +31,7 @@ struct component { const lib_source _lib_source; component(const std::string &ref, const std::string &value, const lib_source &_lib_source) : - ref(ref), value(value), _lib_source(_lib_source) { } + ref(ref), value(value), _lib_source(_lib_source) {} }; struct pin { @@ -48,7 +49,7 @@ public: const std::string ref; const int pin; - node(const std::string &ref, int pin) : ref(ref), pin(pin) { } + node(const std::string &ref, int pin) : ref(ref), pin(pin) {} }; class net { @@ -57,9 +58,9 @@ public: const std::string name; const std::vector<node> nodes; - net(int code, const std::string &name, const std::vector<node> &nodes) : code(code), name(name), nodes(nodes) { } + net(int code, const std::string &name, const std::vector<node> &nodes) : code(code), name(name), nodes(nodes) {} - const node * node_for_ref(const std::string &ref) const; + const node *node_for_ref(const std::string &ref) const; }; struct netlist { @@ -75,7 +76,9 @@ struct netlist { class kicad_parse_exception : public std::runtime_error { public: explicit kicad_parse_exception(const std::vector<std::string> &messages) : - runtime_error("Parse error"), messages(messages) { } + runtime_error("Parse error"), messages(messages) {} + + ~kicad_parse_exception() {} const std::vector<std::string> messages; }; @@ -86,7 +89,7 @@ public: virtual ~kicad_net_loader(); - netlist load(std::string path); + netlist load(std::string path, std::ostream &err); void setDebug(bool debug); diff --git a/kicad.cpp b/core/kicad.cpp index 0a6931d..1a6ca2f 100644 --- a/kicad.cpp +++ b/core/kicad.cpp @@ -136,7 +136,7 @@ public: } }; -netlist kicad_net_loader::load(string path) { +netlist kicad_net_loader::load(string path, ostream &err) { ANTLRFileStream input(path); KicadNetLexer lexer(&input); @@ -146,7 +146,7 @@ netlist kicad_net_loader::load(string path) { if (debug_) { for (auto token : tokens.getTokens()) { - cerr << token->toString() << endl; + err << token->toString() << endl; } } @@ -169,7 +169,7 @@ netlist kicad_net_loader::load(string path) { auto file = parser.file(); if (debug_ && parser.getNumberOfSyntaxErrors() == 0) { - cerr << file->toStringTree(&parser) << endl; + err << file->toStringTree(&parser) << endl; } return netlist { diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 04baca4..58048ff 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,2 +1,11 @@ +cmake_minimum_required(VERSION 3.5) +project(kicad_utils_examples) + +find_package(KicadUtils QUIET) + +if (NOT KicadUtils_FOUND) + message(FATAL_ERROR "The KicadUtils CMake package was not found. Did you pass the correct value for KicadUtils_DIR?\nIt should probably be something like KicadUtils_DIR=$HOME/opt/kicad-utils/lib/cmake/KicadUtils") +endif () + add_subdirectory(arduino-led) add_subdirectory(intel-quark-d2000) diff --git a/examples/arduino-led/CMakeLists.txt b/examples/arduino-led/CMakeLists.txt index 06ef920..a615fb7 100644 --- a/examples/arduino-led/CMakeLists.txt +++ b/examples/arduino-led/CMakeLists.txt @@ -1,3 +1,5 @@ +find_package(KicadUtils) + kicad_generate_header( OUTPUT schematic.h NET schematic/arduino-led.net diff --git a/examples/intel-quark-d2000/CMakeLists.txt b/examples/intel-quark-d2000/CMakeLists.txt index 1822996..3920d4b 100644 --- a/examples/intel-quark-d2000/CMakeLists.txt +++ b/examples/intel-quark-d2000/CMakeLists.txt @@ -1,8 +1,11 @@ +find_package(KicadUtils) + kicad_generate_header( OUTPUT schematic.h NET schematic/intel-quark-d2000.net REF U1 - IN_SOURCE) + TEMPLATE intel-quark-d2000.py + TEMPLATE_LIB_LIST .) if (FALSE) # If you have Intel Quark support for CMake something like this would work: @@ -10,5 +13,5 @@ if (FALSE) #add_executable(arduino-led arduino-led.ino schematic.h) #target_include_directories(arduino-led PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/kicad-include) else () - add_custom_target(intel-quark-d2000 ALL DEPENDS schematic.h) + add_custom_target(intel-quark-d2000 ALL DEPENDS schematic.h schematic-py.h) endif () diff --git a/examples/intel-quark-d2000/intel-quark-d2000.py b/examples/intel-quark-d2000/intel-quark-d2000.py new file mode 100644 index 0000000..b69a919 --- /dev/null +++ b/examples/intel-quark-d2000/intel-quark-d2000.py @@ -0,0 +1,2 @@ +# noinspection PyUnresolvedReferences +generateHeader.write("foo") diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000..2b612b9 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,13 @@ +add_subdirectory(pybind11) + +pybind11_add_module(kicad_utils_py kicad_utils_py.cpp + include/trygvis/kicad/python.h + GenerateHeaderPy.cpp include/trygvis/kicad/GenerateHeaderPy.h) +pybind11_enable_warnings(kicad_utils_py) + +target_include_directories(kicad_utils_py PRIVATE include) +target_link_libraries(kicad_utils_py PRIVATE kicad-utils-core) + +install(TARGETS kicad_utils_py + EXPORT kicad_utils_export + LIBRARY DESTINATION lib) diff --git a/python/GenerateHeaderPy.cpp b/python/GenerateHeaderPy.cpp new file mode 100644 index 0000000..1a039a3 --- /dev/null +++ b/python/GenerateHeaderPy.cpp @@ -0,0 +1,40 @@ +#include "trygvis/kicad/GenerateHeaderPy.h" + +namespace trygvis { +namespace kicad { +namespace python { + +using namespace std; + +GenerateHeaderPy::GenerateHeaderPy(const string &ref, nl *netlist) : ref_(ref), netlist_(netlist) { +} + +GenerateHeaderPy::~GenerateHeaderPy() {} + +string GenerateHeaderPy::ref() { + return ref_; +} + +nl *GenerateHeaderPy::netlist() { + if (!netlist_) { + throw domain_error("No current netlist"); + } + + return netlist_; +} + +string GenerateHeaderPy::str() { + return buf_.str(); +} + +void GenerateHeaderPy::print(const string &str) { + buf_ << str; +} + +void GenerateHeaderPy::println(const string &str) { + buf_ << str << endl; +} + +} // namespace python +} // namespace kicad +} // namespace trygvis diff --git a/python/include/trygvis/kicad/GenerateHeaderPy.h b/python/include/trygvis/kicad/GenerateHeaderPy.h new file mode 100644 index 0000000..893b7ed --- /dev/null +++ b/python/include/trygvis/kicad/GenerateHeaderPy.h @@ -0,0 +1,45 @@ +#ifndef KICAD_UTILS_GENERATEHEADEPY_H +#define KICAD_UTILS_GENERATEHEADEPY_H + +#include "trygvis/kicad.h" +#include "pybind11/pybind11.h" +#include <memory> +#include <sstream> + +namespace trygvis { +namespace kicad { +namespace python { + +namespace py = pybind11; + +using nl = trygvis::kicad::netlist::netlist; + +class GenerateHeaderPy { +public: + GenerateHeaderPy(const std::string &ref, nl *netlist); + + virtual ~GenerateHeaderPy(); + + std::string ref(); + + nl *netlist(); + + std::string str(); + + void print(const std::string &str); + + void println(const std::string &str); + +private: + std::string ref_; + + nl *netlist_; + + std::stringstream buf_; +}; + +} // namespace python +} // namespace kicad +} // namespace trygvis + +#endif diff --git a/python/include/trygvis/kicad/python.h b/python/include/trygvis/kicad/python.h new file mode 100644 index 0000000..f2ae816 --- /dev/null +++ b/python/include/trygvis/kicad/python.h @@ -0,0 +1,3 @@ +#pragma once + +#include "trygvis/kicad/GenerateHeaderPy.h" diff --git a/python/kicad_utils_py.cpp b/python/kicad_utils_py.cpp new file mode 100644 index 0000000..6cd3948 --- /dev/null +++ b/python/kicad_utils_py.cpp @@ -0,0 +1,38 @@ +#include "trygvis/kicad/GenerateHeaderPy.h" + +#include "pybind11/stl.h" + +using namespace std; +using namespace trygvis::kicad::netlist; +using namespace trygvis::kicad::python; + +void init_module(py::module &m) { + py::class_<node>(m, "node"). + def_readonly("ref", &node::ref). + def_readonly("pin", &node::pin); + + py::class_<net>(m, "net"). + def_readonly("code", &net::code). + def_readonly("name", &net::name). + def_readonly("nodes", &net::nodes). + def("node_for_ref", &net::node_for_ref, py::return_value_policy::reference); + + py::class_<nl>(m, "netlist"). + def("find_usages_of", &nl::find_usage_of, py::return_value_policy::reference); + + py::class_<GenerateHeaderPy>(m, "GenerateHeader"). + def(py::init<const string &, nl *>()). + def_property_readonly("netlist", &GenerateHeaderPy::netlist). + def_property_readonly("ref", &GenerateHeaderPy::ref). + def_property_readonly("str", &GenerateHeaderPy::str). + def("print", &GenerateHeaderPy::print). + def("println", &GenerateHeaderPy::println); +} + +PYBIND11_PLUGIN(kicad_utils_py) { + py::module kicad_utils("kicad_utils_py"); + + init_module(kicad_utils); + + return kicad_utils.ptr(); +} diff --git a/python/pybind11 b/python/pybind11 new file mode 160000 +Subproject f38f359f96815421f1780c1a676715efd041f1a diff --git a/template/intel-quark-d2000.py b/template/intel-quark-d2000.py new file mode 100644 index 0000000..85572c5 --- /dev/null +++ b/template/intel-quark-d2000.py @@ -0,0 +1,69 @@ +global generateHeader +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' +} + +generateHeader.println(""" +#include <stddef.h> +#include <qm_gpio.h> + +enum schematic_direction { + schematic_direction_out = 1, + schematic_direction_in = 2 +}; +""") + +usages = generateHeader.netlist.find_usages_of(generateHeader.ref) + +for usage in usages: + node = usage.node_for_ref(generateHeader.ref) + + gpio = gpio_map.get(node.pin) + + if gpio is None: + continue + + generateHeader.println( + 'static const uint8_t SCHEMATIC_' + usage.name + ' = ' + gpio + ';\n' + '\n' + 'static inline\n' + 'qm_rc_t schematic_' + usage.name + '_direction(enum schematic_direction dir) {\n' + ' qm_gpio_port_config_t cfg;\n' + '\n' + ' qm_gpio_get_config(QM_GPIO_0, &cfg);\n' + '\n' + ' if (dir == schematic_direction_out) {\n' + ' cfg.direction |= BIT(SCHEMATIC_' + usage.name + ');\n' + ' } else {\n' + ' cfg.direction &= ~BIT(SCHEMATIC_' + usage.name + ');\n' + ' }\n' + '\n' + ' return qm_gpio_set_config(QM_GPIO_0, &cfg);\n' + '}') |