diff --git a/CMakeLists.txt b/CMakeLists.txt index c958309..91b1a39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ add_executable(wrapit src/TypeRcd.cpp src/TypeMapper.cpp src/utils.cpp + src/cxxwrap_version.cpp src/uuid_utils.cpp src/libclang-ext.cpp src/FunctionWrapper.cpp diff --git a/doc/config.md b/doc/config.md index b0e77f5..b6b4588 100644 --- a/doc/config.md +++ b/doc/config.md @@ -22,6 +22,11 @@ include_dirs = [ "." ] # See clang -std= option # Possible values: "c++11", "c++14", "c++17", and "c++20" cxx-std = "c++17" + +# Version of CxxWrap the code must be generated for. +# Pay attention to quote the string. E.g, "0.14" +# If empty, assume latest supported one. +cxxwrap_version = "" ``` ### Extra options to control the generated code organisation diff --git a/examples/ex001-HelloWorld/CMakeLists.txt b/examples/ex001-HelloWorld/CMakeLists.txt index 7603d08..4d5dab2 100644 --- a/examples/ex001-HelloWorld/CMakeLists.txt +++ b/examples/ex001-HelloWorld/CMakeLists.txt @@ -5,33 +5,8 @@ project(ex001-HelloWorld) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}/deps") -# Path for CxxWrap cmake files: - -find_program(PRINT_CXXWRAP_PATH print-cxxwrap-path.jl - PATHS "${CMAKE_CURRENT_LIST_DIR}/../../buildtools") - -if(PRINT_CXXWRAP_PATH STREQUAL PRINT_CXXWRAP_PATH-NOTFOUND) - message(FATAL_ERROR "Failed to find the print-cxxwarp-path.jl build tool.") -endif() - -execute_process( - COMMAND "${PRINT_CXXWRAP_PATH}" - OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE CXXWRAP_PREFIX - RESULT_VARIABLE result) - -if(NOT result EQUAL 0) - message(FATAL_ERROR "Execution of ${PRINT_CXXWRAP_PATH} failed") -endif() - -list(APPEND CMAKE_PREFIX_PATH ${CXXWRAP_PREFIX}) - -find_package(JlCxx) -get_target_property(JlCxx_location JlCxx::cxxwrap_julia LOCATION) -get_filename_component(JlCxx_location ${JlCxx_location} DIRECTORY) -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib;${JlCxx_location}") - -message(STATUS "Found JlCxx at ${JlCxx_location}") +# julia is used to retrieve the CxxWrap library paths +find_program(JULIA julia REQUIRED) set(WRAPIT_VERBOSITY 1 CACHE STRING "Define verbosity level of the wrapit command. An integer, 0 for a quiet mode, 1 for a normal mode, higher numbers for a verbosity that increases with the number.") @@ -46,14 +21,81 @@ else() endif() set(WRAPIT_WIT_FILE "${CMAKE_SOURCE_DIR}/Hello.wit") -set(WRAPPER_MODULE "Hello") +message(STATUS "Wrapit configuration file: ${WRAPIT_WIT_FILE}") + +execute_process( + COMMAND ${WRAPIT} --get module_name ${WRAPIT_WIT_FILE} + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE WRAPPER_MODULE + RESULT_VARIABLE result) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to retrieve the Julia module name parameter using the configuration file ${WRAPIT_WIT_FILE}.") +endif() + + set(WRAPPER_LIB jl${WRAPPER_MODULE}) set(WRAPPER_JULIA_PACKAGE_DIR ${WRAPPER_MODULE}) set(WRAPPER_JULIA_PACKAGE_FILE ${WRAPPER_MODULE}.jl) +execute_process( + COMMAND ${JULIA} "--project=${CMAKE_BINARY_DIR}" -e "import TOML; print(get(TOML.parse(open(\"${WRAPIT_WIT_FILE}\")), \"cxxwrap_version\", \"\"));" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CXXWRAP_REQUESTED_VERSION + RESULT_VARIABLE result + ) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to parse ${WRAPIT_WIT_FILE}") +endif() + +if("${CXXWRAP_REQUESTED_VERSION}" STREQUAL "") +execute_process( + COMMAND ${JULIA} --project=${CMAKE_BINARY_DIR} -e "import Pkg; Pkg.add(\"CxxWrap\"); import CxxWrap; print(pkgversion(CxxWrap));" + OUTPUT_VARIABLE CXXWRAP_INSTALLED_VERSION + RESULT_VARIABLE result +) + #if no CxxWrap version requirement was specified in the .wit file, + #we align it to the version that was installed + set(WRAPIT_OPT --add-cfg "cxxwrap_version=\"${CXXWRAP_INSTALLED_VERSION}\"") + message(STATUS ${WRAPIT}) +else() +execute_process( + COMMAND ${JULIA} --project=${CMAKE_BINARY_DIR} -e "import Pkg; import CxxWrap; Pkg.add(name=\"CxxWrap\", version=\"${CXXWRAP_REQUESTED_VERSION}\"); Pkg.resolve(); print(pkgversion(CxxWrap));" + OUTPUT_VARIABLE CXXWRAP_INSTALLED_VERSION + RESULT_VARIABLE result) +endif() + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to install CxxWrap") +elseif("${CXXWRAP_REQUESTED_VERSION}" STREQUAL "") + message(STATUS "CxxWrap version requested to be compatible with any version, using v${CXXWRAP_INSTALLED_VERSION}") +else() + message(STATUS "CxxWrap version requested to be compatible with ${CXXWRAP_REQUESTED_VERSION}, using version: ${CXXWRAP_INSTALLED_VERSION}") +endif() + +execute_process( + COMMAND "${JULIA}" --project=${CMAKE_BINARY_DIR} -e "import CxxWrap; print(CxxWrap.prefix_path())" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CXXWRAP_PREFIX + RESULT_VARIABLE result) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to retrieve CxxWrap library path") +else() + message(STATUS "CxxWrap library path prefix: ${CXXWRAP_PREFIX}") +endif() + +find_package(JlCxx PATHS ${CXXWRAP_PREFIX}) + +get_target_property(JlCxx_location JlCxx::cxxwrap_julia LOCATION) +get_filename_component(JlCxx_location ${JlCxx_location} DIRECTORY) +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib;${JlCxx_location}") + + # Generate the wrapper code. This is done at configure time. execute_process( - COMMAND "${WRAPIT}" -v "${WRAPIT_VERBOSITY}" --force --update --cmake --output-prefix "${CMAKE_BINARY_DIR}" "${WRAPIT_WIT_FILE}" + COMMAND "${WRAPIT}" ${WRAPIT_OPT} -v "${WRAPIT_VERBOSITY}" --force --update --cmake --output-prefix "${CMAKE_BINARY_DIR}" "${WRAPIT_WIT_FILE}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" COMMAND_ECHO STDERR RESULT_VARIABLE result) diff --git a/examples/ex001-HelloWorld/Makefile b/examples/ex001-HelloWorld/Makefile index f9df159..2b35ac6 100644 --- a/examples/ex001-HelloWorld/Makefile +++ b/examples/ex001-HelloWorld/Makefile @@ -9,7 +9,16 @@ CXXFLAGS += $(patsubst -std=gnu%,,$(shell $(JL_SHARE)/julia-config.jl --cflags)) LDFLAGS += $(shell $(JL_SHARE)/julia-config.jl --ldflags) LDLIBS += $(shell $(JL_SHARE)/julia-config.jl --ldlibs) WRAPIT = $(shell which wrapit) -CXXWRAP_PREFIX=$(shell ../../buildtools/print-cxxwrap-path.jl) +WIT_FILE = Hello.wit +CXXWRAP_VERSION=$(shell julia -e "import TOML; print(get(TOML.parse(open(\"$(WIT_FILE)\")), \"cxxwrap_version\", \"\"));") + +ifeq ($(CXXWRAP_VERSION),) +CXXWRAP_PREFIX=$(shell mkdir -p $(BUILD_DIR); julia --project=$(BUILD_DIR) -e "import Pkg; Pkg.add(\"CxxWrap\"); Pkg.resolve(); import CxxWrap; print(CxxWrap.prefix_path())") +CXXWRAP_VERSION:=$(shell julia --project=${BUILD_DIR} -e "import CxxWrap; print(pkgversion(CxxWrap));") +WRAPIT_OPT=--add-cfg cxxwrap_version=\"$(CXXWRAP_VERSION)\" +else +CXXWRAP_PREFIX=$(shell mkdir -p $(BUILD_DIR); julia --project=$(BUILD_DIR) -e "import Pkg; Pkg.add(name=\"CxxWrap\", version=\"$(CXXWRAP_VERSION)\"); Pkg.resolve(); import CxxWrap; print(CxxWrap.prefix_path())") +endif CXXWRAP_CPPFLAGS=-I $(CXXWRAP_PREFIX)/include -I . --std=c++17 LDLIBS +=-L $(CXXWRAP_PREFIX)/lib -lcxxwrap_julia -lcxxwrap_julia_stl @@ -47,7 +56,7 @@ clean: -$(RM) -r $(PRODUCTS) $(BUILD_DIR)/libHello/src/jl%.cxx: %.wit - $(WRAPIT) --force --output-prefix $(BUILD_DIR) $< + $(WRAPIT) $(WRAPIT_OPT) --force --output-prefix $(BUILD_DIR) $< $(BUILD_DIR)/%.o: $(BUILD_DIR)/libHello/src/%.cxx -mkdir -p $(BUILD_DIR) diff --git a/examples/ex001-HelloWorld/install.jl b/examples/ex001-HelloWorld/install.jl index 3fd1fae..bcd952f 100755 --- a/examples/ex001-HelloWorld/install.jl +++ b/examples/ex001-HelloWorld/install.jl @@ -8,7 +8,7 @@ build_location = "build" wrapper_module = "Hello" #Generate the wrapper code and compile: -run(`cmake -B $build_location`) +run(`cmake --fresh -B $build_location`) run(`cmake --build $build_location`) #Install the wrapport module: diff --git a/examples/ex002-ROOT/Makefile b/examples/ex002-ROOT/Makefile index 13ee66a..e1aaf71 100644 --- a/examples/ex002-ROOT/Makefile +++ b/examples/ex002-ROOT/Makefile @@ -1,3 +1,4 @@ + BUILD_DIR=build JL_SHARE = $(shell julia -e 'print(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia"))') @@ -7,7 +8,16 @@ CXXFLAGS += $(patsubst -std=gnu%,,$(shell $(JL_SHARE)/julia-config.jl --cflags)) LDFLAGS += $(shell $(JL_SHARE)/julia-config.jl --ldflags) LDLIBS += $(shell $(JL_SHARE)/julia-config.jl --ldlibs) WRAPIT = $(shell which wrapit) -CXXWRAP_PREFIX=$(shell ../../buildtools/print-cxxwrap-path.jl) +WIT_FILE = ROOT.wit +CXXWRAP_VERSION=$(shell julia -e "import TOML; print(get(TOML.parse(open(\"$(WIT_FILE)\")), \"cxxwrap_version\", \"\"));") + +ifeq ($(CXXWRAP_VERSION),) +CXXWRAP_PREFIX=$(shell mkdir -p $(BUILD_DIR); julia --project=$(BUILD_DIR) -e "import Pkg; Pkg.add(\"CxxWrap\"); Pkg.resolve(); import CxxWrap; print(CxxWrap.prefix_path())") +CXXWRAP_VERSION:=$(shell julia --project=${BUILD_DIR} -e "import CxxWrap; print(pkgversion(CxxWrap));") +WRAPIT_OPT=--add-cfg cxxwrap_version=\"$(CXXWRAP_VERSION)\" +else +CXXWRAP_PREFIX=$(shell mkdir -p $(BUILD_DIR); julia --project=$(BUILD_DIR) -e "import Pkg; Pkg.add(name=\"CxxWrap\", version=\"$(CXXWRAP_VERSION)\"); Pkg.resolve(); import CxxWrap; print(CxxWrap.prefix_path())") +endif CXXWRAP_CPPFLAGS=-I $(CXXWRAP_PREFIX)/include -I . --std=c++17 LDLIBS += $(shell $(JL_SHARE)/julia-config.jl --ldlibs) @@ -72,24 +82,27 @@ run_demo2: all LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) JULIA_LOAD_PATH=.:@:@v#.#:@stdlib julia -i demo_TGraph.jl test: all - LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia demo_ROOT.jl + LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) \ +JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) demo_ROOT.jl cmp demo_ROOT.png demo_ROOT-ref.png - LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia demo_TGraph.jl + LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) \ +JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) demo_TGraph.jl cmp demo_TGraph.png demo_TGraph-ref.png - LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia TTree_examples/write_tree1.jl \ -&& JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia TTree_examples/read_tree1.jl - LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia TTree_examples/write_tree2.jl \ -&& JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia TTree_examples/read_tree2.jl - LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia TTree_examples/write_tree3.jl \ -&& JULIA_LOAD_PATH=:`pwd`/build/ROOT/src julia TTree_examples/read_tree3.jl + LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) \ +JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) TTree_examples/write_tree1.jl \ +&& JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) TTree_examples/read_tree1.jl + LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) \ +JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) TTree_examples/write_tree2.jl \ +&& JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) -e 'import Pkg; Pkg.activate(;temp=true); Pkg.add("UnROOT"); include("TTree_examples/read_tree2.jl")' + LD_LIBRARY_PATH=libROOT:$(shell root-config --libdir) \ +JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) TTree_examples/write_tree3.jl \ +&& JULIA_LOAD_PATH=`pwd`/build/ROOT/src:$(JULIA_LOAD_PATH): julia --project=$(BUILD_DIR) TTree_examples/read_tree3.jl %/libROOT/src/generated_cxx %/ROOT/Project.toml %/ROOT/src/ROOT-export.jl: %/ROOT-generated.wit jlROOT-veto.h $(WRAPIT) TBranchPtr.h Templates.h $(MAKE) $*/ROOT-generated.wit - $(WRAPIT) --force --update --output-prefix $* $< + $(WRAPIT) $(WRAPIT_OPT) --force --update --output-prefix $* $< $(eval GENERATED_CXX:=$(file < $(BUILD_DIR)/libROOT/src/generated_cxx)) $(eval OBJS:=$(addprefix $(BUILD_DIR)/libROOT/build/, $(patsubst %.cxx,%.o, $(GENERATED_CXX)))) -# cp -a TBranchPtr.h $(BUILD_DIR)/libROOT/src - $(BUILD_DIR)/libROOT/build/%.o: $(BUILD_DIR)/libROOT/src/%.cxx $(BUILD_DIR) [ -d $(BUILD_DIR)/libROOT/build ] || mkdir -p $(BUILD_DIR)/libROOT/build diff --git a/examples/ex002-ROOT/ROOT.wit b/examples/ex002-ROOT/ROOT.wit index 6d1f3b4..ed3b8d9 100644 --- a/examples/ex002-ROOT/ROOT.wit +++ b/examples/ex002-ROOT/ROOT.wit @@ -16,6 +16,9 @@ veto_list = "jlROOT-veto.h" fields_and_variables = false +# Currently not working with CxxWrap 0.15.0, use 0.14.x +cxxwrap_version = "0.14" + #auto_veto = false vetoed_copy_ctor_classes = [ "TTreeReader", "RDataFrame" ] diff --git a/src/CodeTree.cpp b/src/CodeTree.cpp index 4e80ec1..117edac 100644 --- a/src/CodeTree.cpp +++ b/src/CodeTree.cpp @@ -21,6 +21,7 @@ #include "libclang-ext.h" #include "FileTimeRestorer.h" #include "utils.h" +#include "cxxwrap_version.h" extern const char* version; @@ -184,6 +185,53 @@ std::string CodeTree::wrapper_classsname(const std::string& classname) const{ return s; } +std::ostream& +CodeTree::generate_version_check_cxx(std::ostream& o) const{ + auto [jllmin, jllmax] = version_libcxxwrap_bounds(cxxwrap_version_); + + indent(o,0) << "//method from libcxxwrap returning its version\n"; + indent(o,0) << "extern \"C\" JLCXX_API const char* cxxwrap_version_string();\n\n"; + indent(o, 0) << "//Check the code is compiled with a compatible version of libcxxwrap:\n"; + indent(o,0) << "static_assert(1000*1000*JLCXX_VERSION_MAJOR " + " + 1000 * JLCXX_VERSION_MINOR + JLCXX_VERSION_PATCH >= " << jllmin << "\n"; + indent(o, 1) << "&& 1000 * 1000 * JLCXX_VERSION_MAJOR " + " + 1000 * JLCXX_VERSION_MINOR + JLCXX_VERSION_PATCH < " << jllmax << ",\n"; + indent(o,1) << "\"The code was generated with WrapIt! for \"\n"; + indent(o,1) << "\"a different CxxWrap version (controlled with the cxxwrap_version parameter).\");\n\n"; + indent(o, 0) << "//Check the version of loaded libcxxwrap library:\n"; + indent(o,0) << "void throw_if_version_incompatibility(){\n"; + indent(o,1) << "std::string version_str = cxxwrap_version_string();\n"; + indent(o,1) << "static std::regex r(\"([[:digit:]]{1,3})\\\\.([[:digit:]]{1,3})\\\\.([[:digit:]]{1,3})\");\n"; + indent(o,1) << "std::smatch matches;\n"; + indent(o,1) << "if(!std::regex_match(version_str, matches, r)){\n"; + indent(o,2) << "std::cerr << \"Warning: Failed to check libcxxwrap version.\";\n"; + indent(o,1) << "} else{"; + indent(o,2) << "long version_int = 1000*1000*strtol(matches[1].str().c_str(), 0, 10)\n"; + indent(o,2) << " + 1000*strtol(matches[2].str().c_str(), 0, 10)\n"; + indent(o,2) << " + strtol(matches[3].str().c_str(), 0, 10);\n"; + indent(o,2) << "if(version_int < " << jllmin << " || version_int >= " << jllmax << "){\n"; + indent(o,3) << "throw std::runtime_error(std::string(\"Found libcxxwrap_jll version \")\n"; + indent(o,3) << " + version_str + \", while module " << module_name_ << " requires a version in \"\n"; + indent(o,3) << "\"[" << version_int_to_string(jllmin) << ", " << version_int_to_string(jllmax) << ").\"\n"; + indent(o,3) << "\" Note: if the module was installed with the package manager, the Project.toml file \"\n"; + indent(o,3) << "\"of the package is probably missing a compat specification that would have prevented \"\n"; + indent(o,3) << "\"the inconsistency.\");\n"; + indent(o,2) << "}\n"; + indent(o,1) << "}\n"; + indent(o,0) << "}\n"; + +// indent(o, 1) << "long libcxxwrap_vers = 1000*100* JLCXX_VERSION_MAJOR + 1000 * JLCXX_VERSION_MINOR + JLCXX_VERSION_PATCH;\n"; +// indent(o, 1) << "if(libcxxwrap_vers < " << jllmin << " || libcxxwrap_vers >= " << jllmax << "){\n"; +// indent(o, 2) << "throw std::runtime_error(\"Found libcxxwrap_jll version \" " +// << " JLCXX_VERSION_STRING \", module " << module_name_ +// << " needs a vesion in [" +// << version_int_to_string(jllmin) << ", " +// << version_int_to_string(jllmax) << ")" +// ". Note: if the module was installed with the package manager, the Project.toml file of the package is probably missing a compat specification that would have prevented the inconsistency.\");\n"; +// indent(o, 1) << "}\n\n"; + return o; +} + std::ostream& CodeTree::generate_template_add_type_cxx(std::ostream& o, const TypeRcd& type_rcd, @@ -259,7 +307,8 @@ CodeTree::generate_template_add_type_cxx(std::ostream& o, #ifdef DEFINE_TEMPLATE_METHODS_IN_CTOR if(type_rcd.default_ctor){ FunctionWrapper::gen_ctor(o, 2, "t", type_rcd.template_parameters.empty(), - type_rcd.finalize, std::string()); + type_rcd.finalize, std::string(), + cxxwrap_version_); } #endif @@ -399,7 +448,8 @@ CodeTree::generate_cxx_for_type(std::ostream& o, //Generate a wrapper for the implicit default ctor if needed if(t.default_ctor){ FunctionWrapper::gen_ctor(o, 2, "t", t.template_parameters.empty(), - t.finalize, std::string()); + t.finalize, std::string(), + cxxwrap_version_); } } @@ -530,8 +580,9 @@ CodeTree::generate_cxx(){ "#include \"jlcxx/functions.hpp\"\n" "#include \"jlcxx/stl.hpp\"\n\n"; - o << "#include \"jl" << module_name_ << ".h\"\n"; - + o << "#include \"jl" << module_name_ << ".h\"\n\n" + "#include \n\n"; + // for(const auto& include: forced_headers_){ // o << "#include \"" << include << "\"\n"; // } @@ -609,8 +660,13 @@ CodeTree::generate_cxx(){ } + generate_version_check_cxx(o); + o << "\n\nJLCXX_MODULE define_julia_module(jlcxx::Module& jlModule){\n"; + + indent(o, 1) << "\nthrow_if_version_incompatibility();\n\n"; + indent(o, 1) << "std::vector> wrappers = {\n"; std::string sep; for(const auto& w: wrappers){ @@ -785,7 +841,8 @@ CodeTree::generate_accessor_cxx(std::ostream& o, const TypeRcd* type_rcd, const CXCursor& cursor, bool getter_only, int nindents){ - FunctionWrapper helper(MethodRcd(cursor), type_rcd, type_map_, "", "", nindents); + FunctionWrapper helper(MethodRcd(cursor), type_rcd, type_map_, + cxxwrap_version_, "", "", nindents); int ngens = 0; helper.gen_accessors(o, getter_only, &ngens); @@ -912,11 +969,12 @@ CodeTree::method_cxx_decl(std::ostream& o, const MethodRcd& method, TypeRcd* pTypeRcd = find_class_of_method(method.cursor); - FunctionWrapper wrapper(method, pTypeRcd, type_map_, varname, classname, nindents, - templated); + FunctionWrapper wrapper(method, pTypeRcd, type_map_, cxxwrap_version_, varname, + classname, nindents, templated); //FIXME: check that code below is needed. Should now be vetoed upstream - if(std::find(veto_list_.begin(), veto_list_.end(), wrapper.signature()) != veto_list_.end()){ + if(std::find(veto_list_.begin(), veto_list_.end(), + wrapper.signature()) != veto_list_.end()){ if(verbose > 0){ std::cerr << "Info: " << "func " << wrapper.signature() << " vetoed\n"; } @@ -1010,7 +1068,7 @@ CodeTree::generate_methods_of_templated_type_cxx(std::ostream& o, // wrapped.constructor<>(); if(t.default_ctor){ FunctionWrapper::gen_ctor(o, 3, "wrapped", /*templated=*/true, - t.finalize, std::string()); + t.finalize, std::string(), cxxwrap_version_); } // wrapped.method("get_first", [](const T& a) -> T1 { return a.get_first(); }); @@ -1216,7 +1274,7 @@ CodeTree::visit_global_function(CXCursor cursor){ } - FunctionWrapper wrapper(MethodRcd(cursor), nullptr, type_map_); + FunctionWrapper wrapper(MethodRcd(cursor), nullptr, type_map_, cxxwrap_version_); if(in_veto_list(wrapper.signature())){ // if(std::find(veto_list_.begin(), veto_list_.end(), wrapper.signature()) != veto_list_.end()){ if(verbose > 0){ @@ -1670,7 +1728,7 @@ CodeTree::visit_member_function(CXCursor cursor){ TypeRcd* pTypeRcd = find_class_of_method(cursor); - FunctionWrapper wrapper(MethodRcd(cursor), pTypeRcd, type_map_); + FunctionWrapper wrapper(MethodRcd(cursor), pTypeRcd, type_map_, cxxwrap_version_); if(in_veto_list(wrapper.signature())){ // if(std::find(veto_list_.begin(), veto_list_.end(), wrapper.signature()) != veto_list_.end()){ @@ -1768,7 +1826,8 @@ CodeTree::inform_missing_types(std::vector missing_types, }; if(verbose > 0){ - std::string funcname = FunctionWrapper(methodRcd, classRcd, type_map_).signature(); + std::string funcname = FunctionWrapper(methodRcd, classRcd, type_map_, + cxxwrap_version_).signature(); std::cerr << "Warning: missing definition of type " << (missing_types.size() > 1 ? "s" : "") << " " @@ -1867,7 +1926,8 @@ CodeTree::visit_class_constructor(CXCursor cursor){ TypeRcd* pTypeRcd = find_class_of_method(cursor); - FunctionWrapper wrapper(MethodRcd(cursor), pTypeRcd, type_map_); + FunctionWrapper wrapper(MethodRcd(cursor), pTypeRcd, type_map_, + cxxwrap_version_); if(in_veto_list(wrapper.signature())){ //if(std::find(veto_list_.begin(), veto_list_.end(), wrapper.signature()) != veto_list_.end()){ @@ -2942,4 +3002,9 @@ void CodeTree::generate_projet_file(std::ostream& o, o << "\n[deps]\n" "CxxWrap = \"1f15a43c-97ca-5a2a-ae31-89f07a497df4\"\n" ""; + + int version_depth = version_major(cxxwrap_version_) == 0 ? 2 : 1; + o << "\n[compat]\n" + "CxxWrap = \"" << version_int_to_string(cxxwrap_version_, version_depth) + << "\"\n"; } diff --git a/src/CodeTree.h b/src/CodeTree.h index 3bfbbdd..9e6d0c0 100644 --- a/src/CodeTree.h +++ b/src/CodeTree.h @@ -71,7 +71,7 @@ namespace codetree{ std::vector include_files; std::vector types_; - std::vector types_sorted_indices_; + std::vector types_sorted_indices_; std::vector functions_; std::vector enums_; std::vector typedefs_; @@ -113,7 +113,7 @@ namespace codetree{ } static std::string resolve_clang_resource_dir_path(std::string relpath); - + bool has_cursor(const std::vector vec, const CXCursor& cursor){ for(const auto& c: vec){ if(clang_equalCursors(c, cursor)) return true; @@ -145,7 +145,7 @@ namespace codetree{ bool isAccessible(CXType type) const; CXType resolve_private_typedef(CXType type) const; - + static std::string libclangdir(); //Set clang resource directory. @@ -182,7 +182,7 @@ namespace codetree{ void generate_projet_file(std::ostream&o, const std::string& uuid, const std::string& version); - + //To be called before the generate_xx functions. //Sorts the types such that a parent type appears in the list @@ -259,6 +259,11 @@ namespace codetree{ void add_export_veto_word(const std::string& s) { export_blacklist_.insert(s); } + //Sets version of CxxWrap the code should be generated for. + //Version is coded as + //10^6 * major_number + 1000 * minor_number + patch_number; + void set_cxxwrap_version(long val){ cxxwrap_version_ = val; } + void set_n_classes_per_file(int n_classes_per_file) { n_classes_per_file_ = n_classes_per_file; } void set_out_cxx_dir(const std::string& val) { out_cxx_dir_ = val; } @@ -268,7 +273,7 @@ namespace codetree{ void set_module_name(const std::string& val){ module_name_ = val;} void set_force_mode(bool forced){ out_open_mode_ = forced ? std::ios_base::out : std::ios_base::app; } - + protected: enum class accessor_mode_t {none, getter, both }; @@ -281,7 +286,7 @@ namespace codetree{ // and if it is the case, marks the element type as requiring // std::vector and std:valarray support. void check_for_stl(const CXType& type); - + bool in_veto_list(const std::string signature) const; //Check is a field or variable is veto status for accessor generation @@ -437,6 +442,8 @@ namespace codetree{ //before calling this function void update_wrapper_filenames(); + std::ostream& generate_version_check_cxx(std::ostream& o) const; + std::ostream& generate_type_wrapper_header(std::ostream& o) const; //Try to open file path for writing. Exit application if @@ -451,10 +458,10 @@ namespace codetree{ bool is_natively_supported(const CXType& type, int* params = nullptr) const; - + bool is_natively_supported(const std::string& type_fqn, int* nparams = nullptr) const; - + private: std::string clang_resource_dir_; @@ -462,8 +469,10 @@ namespace codetree{ std::string cmake_; + long cxxwrap_version_; + bool update_mode_; - + std::string header_file_path_; std::string module_name_; @@ -514,12 +523,12 @@ namespace codetree{ bool import_setindex_; Graph type_dependencies_; - + std::vector towrap_type_filenames_; std::set towrap_type_filenames_set_; TypeMapper type_map_; - + struct { unsigned enums = 0; unsigned types = 0; diff --git a/src/FunctionWrapper.cpp b/src/FunctionWrapper.cpp index abd4a59..e0b1ac2 100644 --- a/src/FunctionWrapper.cpp +++ b/src/FunctionWrapper.cpp @@ -11,11 +11,12 @@ #include #include "TypeMapper.h" +#include "cxxwrap_version.h" std::ostream& FunctionWrapper::gen_ctor(std::ostream& o){ int nargsmin = std::max(1, method.min_args); //no-arg ctor, aka "default ctor" generate elsewhere with direct call to gen_ctor(std::ostream& o, int nindents, - // const std::string& varname, bool templated, bool finalize, const std::string& arg_list) + // const std::string& varname_, bool templated, bool finalize, const std::string& arg_list) int nargsmax = clang_getNumArgTypes(method_type); indent(o << "\n", nindents) @@ -28,7 +29,8 @@ FunctionWrapper::gen_ctor(std::ostream& o){ for(int nargs = nargsmin; nargs <= nargsmax; ++nargs){ std::stringstream buf; gen_arg_list(buf, nargs, "", /*argtypes_only = */ true); - gen_ctor(o, nindents, varname, templated_, finalize, buf.str()); + gen_ctor(o, nindents, varname_, templated_, finalize, buf.str(), + cxxwrap_version_); } generated_jl_functions_.insert(name_jl_); return o; @@ -36,13 +38,25 @@ FunctionWrapper::gen_ctor(std::ostream& o){ std::ostream& FunctionWrapper::gen_ctor(std::ostream& o, int nindents, - const std::string& varname, bool templated, + const std::string& varname_, bool templated, bool finalize, - const std::string& arg_list){ - indent(o, nindents) << varname << "." << (templated ? "template " : "") + const std::string& arg_list, long cxxwrap_version_){ + + const char* finalize_true; + const char* finalize_false; + if(cxxwrap_version_ < cxxwrap_v0_15){ + finalize_true = "true"; + finalize_false = "false"; + } else{ + finalize_true = "jlcxx::finalize_policy::yes"; + finalize_false = "jlcxx::finalize_policy::no"; + } + + indent(o, nindents) << varname_ << "." << (templated ? "template " : "") << "constructor<" << arg_list - << ">(/*finalize=*/" << (finalize?"true":"false") << ");\n"; + << ">(/*finalize=*/" + << (finalize?finalize_true:finalize_false) << ");\n"; return o; } @@ -159,7 +173,7 @@ FunctionWrapper::gen_accessors(std::ostream& o, bool getter_only, int* ngens) { //tA.method("x", [](const ns::A& a) -> const T& { return a.x; }); // or //tA.method("x", [](const ns::A& a) -> T { return a.x; }); - indent(o, nindents) << varname << ".method(\"" << target_name_jl << "\", [](" + indent(o, nindents) << varname_ << ".method(\"" << target_name_jl << "\", [](" << "const " << classname << ref << " a) -> " << (return_by_copy ? copy_return_type(target_type) : @@ -173,7 +187,7 @@ FunctionWrapper::gen_accessors(std::ostream& o, bool getter_only, int* ngens) { //tA.method("x", [](ns::A& a) -> T& { return a.x; }); // or //tA.method("x", [](ns::A& a) -> T { return a.x; }); - indent(o, nindents) << varname << ".method(\"" << target_name_jl << "\", [](" + indent(o, nindents) << varname_ << ".method(\"" << target_name_jl << "\", [](" << classname << ref << " a) -> " << (return_by_copy ? copy_return_type(target_type) @@ -197,7 +211,7 @@ FunctionWrapper::gen_accessors(std::ostream& o, bool getter_only, int* ngens) { << "to provide write access to the field " << target_name << " (\" __HERE__ \")\");\n"; //tA.method("x!", [](ns::A& a, int val) -> T& { return a.x = val; }); - indent(o, nindents) << varname << ".method(\"" << target_name_jl << "!\", [](" + indent(o, nindents) << varname_ << ".method(\"" << target_name_jl << "!\", [](" << classname << ref << " a, " << const_type(target_type) << " val) -> " << non_const_type_or_pod(target_type) << " { return a" << op << target_name << " = val; });\n"; @@ -216,13 +230,13 @@ FunctionWrapper::gen_accessors(std::ostream& o, bool getter_only, int* ngens) { auto rtype = gen_setters ? non_const_type_or_pod(target_type) : const_type(target_type); //types.method("ns!A!x", []() -> T& { return ns::A::x; }); - indent(o, nindents) << varname << ".method(\"" << target_name_jl << "\", []()" + indent(o, nindents) << varname_ << ".method(\"" << target_name_jl << "\", []()" << "-> " << rtype << " { return " << fqn << "; });\n"; if(gen_setters){ //types.method("ns!A!x!", [](int val) -> T& { return ns::A::x = val; }); - indent(o, nindents) << varname << ".method(\"" << target_name_jl << "!\", [](" + indent(o, nindents) << varname_ << ".method(\"" << target_name_jl << "!\", [](" << const_type(target_type) << " val)" << "-> " << rtype << " { return " << fqn << " = val; });\n"; @@ -251,8 +265,7 @@ FunctionWrapper::gen_setindex(std::ostream& o) const{ << " to wrap " << signature() << " (\" __HERE__ \")\");\n" << "// defined in " << clang_getCursorLocation(method.cursor) << "\n"; - - indent(o, nindents) << varname << ".method(\"setindex!\",\n"; + indent(o, nindents) << varname_ << ".method(\"setindex!\",\n"; indent(o, nindents+1) << "[](" << classname << "& a, " << short_arg_list_cxx << " i, " << std::regex_replace(fix_template_type(fully_qualified_name(return_type_)), std::regex("\\s*&\\s*$"), " const &") @@ -279,7 +292,7 @@ FunctionWrapper::gen_getindex(std::ostream& o, << " (\" __HERE__ \")\");\n"; indent(o, nindents) << "// defined in " << clang_getCursorLocation(method.cursor) << "\n"; - indent(o, nindents) << varname << ".method(\"getindex\",\n"; + indent(o, nindents) << varname_ << ".method(\"getindex\",\n"; indent(o, nindents + 1) << "[](" << classname << "& a, " << short_arg_list_cxx << " i){\n"; indent(o, nindents + 1) << "return a[i];\n"; @@ -337,7 +350,7 @@ FunctionWrapper::gen_func_with_cast(std::ostream& o){ msg2 = std::string("of class ") + classname; } - indent(o, nindents) << varname << ".method("; + indent(o, nindents) << varname_ << ".method("; if(name_jl_.size() > 0){ //note: operator() is a special case @@ -394,7 +407,7 @@ FunctionWrapper::gen_func_with_lambdas(std::ostream& o){ for(int itype = 0; itype < ntypes; ++itype){ for(int nargs = nargsmin; nargs <= nargsmax; ++nargs){ - indent(o, nindents) << varname << ".method(\"" << name_jl_<< "\", []("; + indent(o, nindents) << varname_ << ".method(\"" << name_jl_<< "\", []("; std::string sep; if(!is_static_ && classname.size() > 0){ o << classname << cv << ref_types[itype] << " a"; @@ -467,13 +480,16 @@ FunctionWrapper::arg_decl(int iarg, bool argtypes_only) const{ } -FunctionWrapper::FunctionWrapper(const MethodRcd& method, const TypeRcd* pTypeRcd, +FunctionWrapper::FunctionWrapper(const MethodRcd& method, + const TypeRcd* pTypeRcd, const TypeMapper& type_map, + long cxxwrap_version, std::string varname, std::string classname, int nindents, bool templated): method(method), - varname(varname), + varname_(varname), classname(classname), + cxxwrap_version_(cxxwrap_version), nindents(nindents), all_lambda_(false), pTypeRcd(pTypeRcd), @@ -502,11 +518,6 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method, const TypeRcd* pTypeRc class_prefix = this->classname + "::"; } - if(varname.size() == 0){ - this->varname = "t"; - } - - is_static_ = clang_Cursor_getStorageClass(cursor) == CX_SC_Static; return_type_ = clang_getResultType(method_type); inaccessible_type = !isAccessible(return_type_); @@ -516,9 +527,19 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method, const TypeRcd* pTypeRc //cursor name of global functions are not fully qualified. name_cxx = fully_qualified_name(cursor); } - + std::string name_jl_suffix = jl_type_name(name_cxx); + is_static_ = clang_Cursor_getStorageClass(cursor) == CX_SC_Static; + + if(name_cxx == "operator new" || name_cxx == "operator delete" + || name_cxx == "operator new[]" || name_cxx == "operator delete[]" + ) is_static_ = true; + + if(varname.size() == 0){ + varname_ = is_static_ ? "module_" : "t"; + } + static std::regex opregex("(^|.*::)operator[[:space:]]*(.*)$"); std::cmatch m; override_base_ = false; @@ -616,17 +637,13 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method, const TypeRcd* pTypeRc setindex_ = true; } } - - if(name_cxx == "operator new" || name_cxx == "operator delete" - || name_cxx == "operator new[]" || name_cxx == "operator delete[]" - ) is_static_ = true; - - + if(is_static_){ - if(templated_) + if(templated_){ name_jl_ = jl_type_name(pTypeRcd->type_name + "::") + name_jl_suffix; - else - name_jl_ = jl_type_name(class_prefix) + name_jl_suffix; + } else{ + name_jl_ = jl_type_name(class_prefix) + name_jl_suffix; + } } else{ //FIXME: Add prefix. The namespace? name_jl_ = name_jl_suffix; diff --git a/src/FunctionWrapper.h b/src/FunctionWrapper.h index 6885bf3..76c70f1 100644 --- a/src/FunctionWrapper.h +++ b/src/FunctionWrapper.h @@ -26,8 +26,10 @@ class TypeMapper; // class FunctionWrapper{ public: - FunctionWrapper(const MethodRcd& method, const TypeRcd* pTypeRcd, + FunctionWrapper(const MethodRcd& method, + const TypeRcd* pTypeRcd, const TypeMapper& type_mapper, + long cxxwrap_version, std::string varname = std::string(), std::string classname = std::string(), int nindents = 0, @@ -55,7 +57,8 @@ class FunctionWrapper{ gen_ctor(std::ostream& o, int nindents, const std::string& varname, bool templated, bool finalize, - const std::string& arg_list); + const std::string& arg_list, + long cxxwrap_version); /// Tells if the Base module must be overriden. /// This is the case for operators. @@ -133,8 +136,9 @@ class FunctionWrapper{ private: + long cxxwrap_version_; MethodRcd method; - std::string varname; + std::string varname_; std::string classname; std::string class_prefix; int nindents; diff --git a/src/cxxwrap_version.cpp b/src/cxxwrap_version.cpp new file mode 100644 index 0000000..4783144 --- /dev/null +++ b/src/cxxwrap_version.cpp @@ -0,0 +1,14 @@ +#include "cxxwrap_version.h" + +//according to https://github.com/JuliaInterop/libcxxwrap-julia/releases +//we have breaking changes with libcxxwrap_jll versions, +//v0.10.0 (not used by any CxxWrap release), +//v0.11.0 (CxxWrap v0.14.0), +//v0.12.0 (CxxWrap v0.15.0) +std::tuple version_libcxxwrap_bounds(long cxxwrap_version){ + if(cxxwrap_version < cxxwrap_v0_15){ + return std::tuple(11*1000, 12*1000); + } else{ + return std::tuple(12*1000, 13*1000); + } +} diff --git a/src/cxxwrap_version.h b/src/cxxwrap_version.h new file mode 100644 index 0000000..dfd046e --- /dev/null +++ b/src/cxxwrap_version.h @@ -0,0 +1,11 @@ +#include + +//Default value for the cxxwrap_version config parameter +static const char* default_cxxwrap_version = "0.15.0"; + +static const char* supported_cxxwrap_versions = "0.14.x, 0.15.x"; + +static int cxxwrap_v0_15 = 15 * 1000; //v0.15 + +std::tuple version_libcxxwrap_bounds(long cxxwrap_version); + diff --git a/src/main.cpp b/src/main.cpp index fac36a7..c48923d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ using namespace std::string_view_literals; #include "CodeTree.h" #include "utils.h" #include "uuid_utils.h" +#include "cxxwrap_version.h" using namespace codetree; @@ -76,13 +77,17 @@ find_include_path(const fs::path& p, const std::vector& inc_dirs){ } +void append_to_table(toml::table& dest, toml::table& src){ + for (const auto& [key, value]: src) { + dest.insert_or_assign(key, value); + } +} int main(int argc, char* argv[]){ - std::srand(std::time(nullptr)); - cxxopts::Options option_list("wrapit", "Generates wrappers from a c++ header file for Cxx.jl.\n"); + // clang-format off option_list.add_options() ("h,help", "Display this help and exit") @@ -95,6 +100,12 @@ int main(int argc, char* argv[]){ "configuration file. It defines two variables WRAPIT_INPUTS and " "WRAPIT_PRODUCTS repectively with the list of input header files " "the result depends on and the list of produced files") + ("get", "Retrieves a configuration parameter. Retrieval of only few " + "parameters are currently supported.", + cxxopts::value()) + ("add-cfg", "Set a configuration parameter on top of what is defined in the" + " configuration file.", + cxxopts::value>()) ("output-prefix", "Prefix inserted to output paths", cxxopts::value()->default_value("")) ("resource-dir", std::string("Change the clang resource directory path (see clang " @@ -150,6 +161,45 @@ int main(int argc, char* argv[]){ } }; + if(options["add-cfg"].count() > 0){ + auto params= options["add-cfg"].as>(); + for(const auto& p: params){ + toml::table to_append; + try{ + to_append = toml::parse(p); + } catch (const toml::parse_error& err) { + std::cerr << "Error parsing --add-cfg parameter, '" << p << "': " + << err.what() << "\n"; + exit(1); + } + try{ + append_to_table(toml_config, to_append); + } catch (const toml::parse_error& err) { + std::cerr << "Failed to set parameter specified by --add-cfg, '" + << p << "': " << err.what() << "\n"; + exit(1); + } + } + } + + auto cxxwrap_version_str = toml_config["cxxwrap_version"].value_or(std::string("")); + std::string mess; + if(cxxwrap_version_str.size() == 0){//default version + mess = "Error. Bug found in wrapit. Cxx wrap version string defined in the file cxxwrap_version.h is not valid."; + cxxwrap_version_str = default_cxxwrap_version; + } else{ + std::stringstream buf; + buf << "Error. The format of the value of the cxxwrap_version " + "configuration parameter (\"" << cxxwrap_version_str + << "\") is not valid. Expected format: x.y.z, x.y, or x"; + mess = buf.str(); + } + auto cxxwrap_version = version_string_to_int(cxxwrap_version_str); + if(cxxwrap_version < 0){ + std::cerr << mess; + exit(1); + } + auto include_dirs = read_vpath("include_dirs", { fs::path(".")} ); //auto to_parse = read_vpath_include("input", include_dirs); auto to_parse = read_vstring("input"); @@ -234,14 +284,44 @@ int main(int argc, char* argv[]){ } - auto open_mode = std::ofstream::out; - if(options.count("force") == 0){ - open_mode |= std::ofstream::app; + auto version = toml_config["version"].value_or(std::string()); + + if(options.count("get")){ + auto param = options["get"].as(); + if(param == "cxxwrap_version") std::cout << cxxwrap_version_str << "\n"; + else if(param == "module_name") std::cout << module_name << "\n"; + else if(param == "version") std::cout << version << "\n"; + else if(param == "lib_basname") std::cout << lib_basename << "\n"; + else if(param == "export_jl_fname") std::cout << out_export_jl_fname << "\n"; + else if(param == "module_jl_fname") std::cout << out_jl_fname << "\n"; + else std::cerr << "Retrieval of parameter '" << param + << "' is not supported"; + exit(0); } + auto uuid = toml_config["uuid"].value_or(std::string()); - bool in_err = false; + if(uuid.size() == 0){ + uuid = gen_uuid(); + std::cerr << "The configuration file misses the uuid parameter. Following " + "generated uuid will be used for the generate Julia project. Add the " + "following line in the .wit configuration file to use same uuid " + "for next versions of the code.\n" + "uuid = \"" << uuid << "\"\n\n"; + } else if (!validate_uuid(uuid)){ + std::cerr << "The value \"" << uuid << "\" of the uuid parameter found in " + "the .wit configuration file is not valid. You can run first with an " + "empty string to generate a new uuid, which will be displayed to the " + "output (stderr).\n"; + return 1; + } + bool in_err = false; + auto open_mode = std::ofstream::out; + if(options.count("force") == 0){ + open_mode |= std::ofstream::app; + } + auto open_file = [&](const std::string& fname){ std::ofstream f(fname, open_mode); if(f.tellp()!=0){ @@ -282,24 +362,6 @@ int main(int argc, char* argv[]){ if(in_err) return -1; - auto uuid = toml_config["uuid"].value_or(std::string()); - - if(uuid.size() == 0){ - uuid = gen_uuid(); - std::cerr << "The configuration file misses the uuid parameter. Folloing " - "generated uuid will be used for the generate Julia project. Add the " - "following line in the .wit configuration file to use same uuid " - "for next versions of the code.\n" - "uuid = \"" << uuid << "\"\n\n"; - } else if (!validate_uuid(uuid)){ - std::cerr << "The value \"" << uuid << "\" of the uuid parameter found in " - "the .wit configuration file is not valid. You can run first with an " - "empty string to generate a new uuid, which will be displayed to the " - "output (stderr).\n"; - return 1; - } - - auto version = toml_config["version"].value_or(std::string()); verbose = verbosity; @@ -307,6 +369,8 @@ int main(int argc, char* argv[]){ tree.set_force_mode(options.count("force") > 0); + tree.set_cxxwrap_version(cxxwrap_version); + tree.add_std_option(cxx_std); tree.auto_veto(auto_veto); diff --git a/src/utils.cpp b/src/utils.cpp index 49ec86e..adf8da7 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -19,6 +19,42 @@ int verbose = 0; CXPrintingPolicy pp = nullptr; +long version_int_base = 1000; + +long version_string_to_int(const std::string& version_string){ + static std::regex r("v?([[:digit:]]{1,3}\\.)?([[:digit:]]{1,3}\\.)?([[:digit:]]{1,3})"); + std::smatch matches; + if(!std::regex_match(version_string, matches, r)){ + return -1; + } else{ + if(matches[2].str().size() > 0){ //x.y.z + return strtol(matches[3].str().c_str(), 0, 0) + + version_int_base * strtol(matches[2].str().c_str(), 0, 0) + + version_int_base * version_int_base * strtol(matches[1].str().c_str(), 0, 0); + } else if(matches[1].str().size() > 0){ //x.y + //Beware that it is the second ()? group of the regex that + //will be omitted and we have x in matches[1]; y in matches[3] + return version_int_base * strtol(matches[3].str().c_str(), 0, 0) + + version_int_base * version_int_base * strtol(matches[1].str().c_str(), 0, 0); + } else{ //x + std::cerr << "=>C\n"; + return version_int_base * version_int_base * strtol(matches[3].str().c_str(), 0, 0); + } + } +} + +std::string version_int_to_string(long iversion, int depth){ + std::stringstream buf; + buf << (iversion / version_int_base / version_int_base); + if(depth>1) buf << "." << (iversion / version_int_base) % version_int_base; + if(depth>2) buf << "." << iversion % version_int_base; + return buf.str(); +} + +int version_major(long version_int){ + return version_int / version_int_base / version_int_base; +} + std::ostream& indent(std::ostream& o, int n){ for(int i = 0; i < n; ++i){ diff --git a/src/utils.h b/src/utils.h index c9e10e6..b56f490 100644 --- a/src/utils.h +++ b/src/utils.h @@ -15,9 +15,22 @@ static const char* one_indent = " "; extern int verbose; +extern long version_int_base; std::ostream& indent(std::ostream& o, int n); +//Converts version string x.y.z (optionnally prefixed by the letter v) +//to an int which is larger for newer version +//and is defined as 10^6*x + 10^3*y + z +//return -1 if the text format was not recognised or one of the +//version subnumber includes than 3 digits. +//(long return type ensures the type has a minimum of 32 bits. +long version_string_to_int(const std::string& version_string); + +std::string version_int_to_string(long version_int, int depth = 3); + +int version_major(long version_int); + template bool has(const T& collection, const U& element){ return std::find(collection.begin(), collection.end(), element) != collection.end(); diff --git a/src/uuid_utils.cpp b/src/uuid_utils.cpp index 99afcbe..c7f9208 100644 --- a/src/uuid_utils.cpp +++ b/src/uuid_utils.cpp @@ -3,14 +3,18 @@ #include std::string gen_uuid(){ + unsigned char buf[32]; //unsigned is essential for the modulo + // operation done later in the code. + arc4random_buf(buf, sizeof(buf)); const char* v = "0123456789abcdef"; //3fb17ebc-bc38-4939-bc8b-74f2443281d4 //8 dash 4 dash 4 dash 4 dash 12 std::string s; s.resize(36); + auto p = buf; for(int i = 0; i < 36; ++i) { if(i != 8 && i != 13 && i != 18 && i != 23){ - s[i] = v[rand()%16]; + s[i] = v[*(p++)%16]; } else{ s[i] = '-'; } diff --git a/test/Project.toml b/test/Project.toml deleted file mode 100644 index 739c096..0000000 --- a/test/Project.toml +++ /dev/null @@ -1,6 +0,0 @@ -[deps] -CxxWrap = "1f15a43c-97ca-5a2a-ae31-89f07a497df4" -Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/TestAccessAndDelete/runTestAccessAndDelete.jl b/test/TestAccessAndDelete/runTestAccessAndDelete.jl index b5b22b7..8b5e003 100644 --- a/test/TestAccessAndDelete/runTestAccessAndDelete.jl +++ b/test/TestAccessAndDelete/runTestAccessAndDelete.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestAccessAndDelete") using TestAccessAndDelete function runtest() diff --git a/test/TestAutoAdd/runTestAutoAdd.jl b/test/TestAutoAdd/runTestAutoAdd.jl index 1a4cab0..126ab82 100644 --- a/test/TestAutoAdd/runTestAutoAdd.jl +++ b/test/TestAutoAdd/runTestAutoAdd.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestAutoAdd") using TestAutoAdd function runtest() diff --git a/test/TestCtorDefVal/runTestCtorDefVal.jl b/test/TestCtorDefVal/runTestCtorDefVal.jl index cf9d8f5..9d709a0 100644 --- a/test/TestCtorDefVal/runTestCtorDefVal.jl +++ b/test/TestCtorDefVal/runTestCtorDefVal.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestCtorDefVal") using TestCtorDefVal function runtest() diff --git a/test/TestEmptyClass/runTestEmptyClass.jl b/test/TestEmptyClass/runTestEmptyClass.jl index dc6b1a1..e1434a6 100644 --- a/test/TestEmptyClass/runTestEmptyClass.jl +++ b/test/TestEmptyClass/runTestEmptyClass.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestEmptyClass") using TestEmptyClass function runtest() diff --git a/test/TestEnum/runTestEnum.jl b/test/TestEnum/runTestEnum.jl index 82676ad..01b795e 100644 --- a/test/TestEnum/runTestEnum.jl +++ b/test/TestEnum/runTestEnum.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestEnum") using TestEnum function runtest() diff --git a/test/TestInheritance/runTestInheritance.jl b/test/TestInheritance/runTestInheritance.jl index b017b4f..4bbcfef 100644 --- a/test/TestInheritance/runTestInheritance.jl +++ b/test/TestInheritance/runTestInheritance.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestInheritance") using TestInheritance function runtest() diff --git a/test/TestNamespace/runTestNamespace.jl b/test/TestNamespace/runTestNamespace.jl index 6977c2b..550b508 100644 --- a/test/TestNamespace/runTestNamespace.jl +++ b/test/TestNamespace/runTestNamespace.jl @@ -2,7 +2,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestNamespace") using TestNamespace function runtest() diff --git a/test/TestNoFinalizer/runTestNoFinalizer.jl b/test/TestNoFinalizer/runTestNoFinalizer.jl index 3ea0fdd..9f2ed56 100644 --- a/test/TestNoFinalizer/runTestNoFinalizer.jl +++ b/test/TestNoFinalizer/runTestNoFinalizer.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestNoFinalizer") using TestNoFinalizer function runtest() diff --git a/test/TestOperators/runTestOperators.jl b/test/TestOperators/runTestOperators.jl index 1658e72..0573c69 100644 --- a/test/TestOperators/runTestOperators.jl +++ b/test/TestOperators/runTestOperators.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestOperators") using TestOperators # Test an operator executing exp. diff --git a/test/TestOrder/runTestOrder.jl b/test/TestOrder/runTestOrder.jl index 746c5c2..06621b7 100644 --- a/test/TestOrder/runTestOrder.jl +++ b/test/TestOrder/runTestOrder.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestOrder") if "-s" in ARGS #Serialize mode Test.TESTSET_PRINT_ENABLE[] = false diff --git a/test/TestPointers/runTestPointers.jl b/test/TestPointers/runTestPointers.jl index 826fe50..ddce675 100644 --- a/test/TestPointers/runTestPointers.jl +++ b/test/TestPointers/runTestPointers.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestPointers") using TestPointers using CxxWrap: CxxPtr, ConstCxxPtr diff --git a/test/TestPropagation/Makefile b/test/TestPropagation/Makefile index 2114f24..7b28fe8 100644 --- a/test/TestPropagation/Makefile +++ b/test/TestPropagation/Makefile @@ -9,7 +9,11 @@ CXXFLAGS += $(patsubst -std=gnu%,,$(shell $(JL_SHARE)/julia-config.jl --cflags)) LDFLAGS += $(shell $(JL_SHARE)/julia-config.jl --ldflags) LDLIBS += $(shell $(JL_SHARE)/julia-config.jl --ldlibs) + CXXWRAP_PREFIX=$(shell julia -e 'import Pkg; haskey(Pkg.dependencies(), Base.UUID("1f15a43c-97ca-5a2a-ae31-89f07a497df4")) || Pkg.add("CxxWrap"); using CxxWrap; print(CxxWrap.prefix_path())') +CXXWRAP_VERSION:=$(shell julia --project=${BUILD_DIR} -e "import CxxWrap; print(pkgversion(CxxWrap));") +WRAPIT_OPT=--add-cfg cxxwrap_version=\"$(CXXWRAP_VERSION)\" + CXXWRAP_CPPFLAGS=-I $(CXXWRAP_PREFIX)/include -std=c++20 LDLIBS +=-L $(CXXWRAP_PREFIX)/lib -lcxxwrap_julia @@ -57,13 +61,13 @@ clean: #use of % instead of $(BUILD_DIR) prevents make to run the recipee multiple times when running with -j %/libTestPropagation1/src/jlTestPropagation1.cxx %/libTestPropagation1/src/jlTestPropagation1.h %/TestPropagation1/src/TestPropagation1.jl: TestPropagation1.wit - wrapit --force --output-prefix $* $< + wrapit $(WRAPIT_OPT) --force --output-prefix $* $< %/libTestPropagation2/src/jlTestPropagation2.cxx %/libTestPropagation2/src/jlTestPropagation2.h %/TestPropagation2/src/TestPropagation2.jl: TestPropagation2.wit - wrapit --force --output-prefix $* $< + wrapit $(WRAPIT_OPT) --force --output-prefix $* $< %/libTestPropagation3/src/jlTestPropagation3.cxx %/libTestPropagation3/src/jlTestPropagation3.h %/TestPropagation3/src/TestPropagation3.jl: TestPropagation3.wit - wrapit --force --output-prefix $* $< + wrapit $(WRAPIT_OPT) --force --output-prefix $* $< $(BUILD_DIR)/libTestPropagation1/build/%.o: $(BUILD_DIR)/libTestPropagation1/src/%.cxx [ -d $(@D) ] || mkdir -p $(@D) diff --git a/test/TestPropagation/runTestPropagation1.jl b/test/TestPropagation/runTestPropagation1.jl index c459271..1438f3c 100644 --- a/test/TestPropagation/runTestPropagation1.jl +++ b/test/TestPropagation/runTestPropagation1.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestPropagation1") import TestPropagation1 function runtest() diff --git a/test/TestPropagation/runTestPropagation2.jl b/test/TestPropagation/runTestPropagation2.jl index f8075e5..066b8b0 100644 --- a/test/TestPropagation/runTestPropagation2.jl +++ b/test/TestPropagation/runTestPropagation2.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestPropagation2") import TestPropagation2 function runtest() diff --git a/test/TestPropagation/runTestPropagation3.jl b/test/TestPropagation/runTestPropagation3.jl index d27f370..cfdb675 100644 --- a/test/TestPropagation/runTestPropagation3.jl +++ b/test/TestPropagation/runTestPropagation3.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestPropagation3") import TestPropagation3 function runtest() diff --git a/test/TestSizet/runTestSizet.jl b/test/TestSizet/runTestSizet.jl index 2bcef9b..7ee9a21 100644 --- a/test/TestSizet/runTestSizet.jl +++ b/test/TestSizet/runTestSizet.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestSizet") using TestSizet function runtest() diff --git a/test/TestStdString/runTestStdString.jl b/test/TestStdString/runTestStdString.jl index 38db2fb..0a29000 100644 --- a/test/TestStdString/runTestStdString.jl +++ b/test/TestStdString/runTestStdString.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestStdString") using TestStdString function runtest() diff --git a/test/TestStdVector/TestStdVector.wit b/test/TestStdVector/TestStdVector.wit index dccf133..76a3f3c 100644 --- a/test/TestStdVector/TestStdVector.wit +++ b/test/TestStdVector/TestStdVector.wit @@ -10,5 +10,8 @@ export = "all" fields_and_variables = false +# Currently not working with CxxWrap 0.15.0, use 0.14.x +cxxwrap_version = "0.14" + # all generated code in a single file: #n_classes_per_file = 0 diff --git a/test/TestStdVector/runTestStdVector.jl b/test/TestStdVector/runTestStdVector.jl index eb91718..8815574 100644 --- a/test/TestStdVector/runTestStdVector.jl +++ b/test/TestStdVector/runTestStdVector.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestStdVector") using TestStdVector using CxxWrap: StdVector diff --git a/test/TestStringView/runTestStringView.jl b/test/TestStringView/runTestStringView.jl index 91c9a48..b32ea23 100644 --- a/test/TestStringView/runTestStringView.jl +++ b/test/TestStringView/runTestStringView.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestStringView") using TestStringView function runtest() diff --git a/test/TestTemplate1/runTestTemplate1.jl b/test/TestTemplate1/runTestTemplate1.jl index bfa735d..18a13f3 100644 --- a/test/TestTemplate1/runTestTemplate1.jl +++ b/test/TestTemplate1/runTestTemplate1.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path=joinpath(@__DIR__, "build", "TestTemplate1")) using TestTemplate1 function runtest() diff --git a/test/TestTemplate2/runTestTemplate2.jl b/test/TestTemplate2/runTestTemplate2.jl index b362049..8a596f0 100644 --- a/test/TestTemplate2/runTestTemplate2.jl +++ b/test/TestTemplate2/runTestTemplate2.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestTemplate2") using TestTemplate2 diff --git a/test/TestUsingType/TestUsingType.wit b/test/TestUsingType/TestUsingType.wit index bc2b014..1ff991b 100644 --- a/test/TestUsingType/TestUsingType.wit +++ b/test/TestUsingType/TestUsingType.wit @@ -12,6 +12,10 @@ export = "all" auto_veto = true +# Currently not working with CxxWrap 0.15.0, use 0.14.x +cxxwrap_version = "0.14" + + # all generated code in a single file: n_classes_per_file = 0 diff --git a/test/TestUsingType/runTestUsingType.jl b/test/TestUsingType/runTestUsingType.jl index 69c8eb9..32de2a2 100644 --- a/test/TestUsingType/runTestUsingType.jl +++ b/test/TestUsingType/runTestUsingType.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestUsingType") using TestUsingType function runtest() diff --git a/test/TestVarField/runTestVarField.jl b/test/TestVarField/runTestVarField.jl index 681eef0..71d4628 100755 --- a/test/TestVarField/runTestVarField.jl +++ b/test/TestVarField/runTestVarField.jl @@ -1,11 +1,18 @@ #!/usr/bin/env julia using Test using Serialization +import Pkg function runtest() + args = collect(Base.julia_cmd()) + push!(args, "") + iscript = length(args) + push!(args, "-s") + ENV["JULIA_PROJECT"] = Pkg.project().path @testset verbose=true "TestVarField" begin - for test_script in ["runTestVarFieldOn.jl" "runTestVarFieldOff.jl"] - Test.record(Test.get_testset(), deserialize(open(Cmd(`julia --project=.. $test_script -s`, dir=@__DIR__)))) + for testname in ["TestVarFieldOn" "TestVarFieldOff"] + args[iscript] = "run" * testname * ".jl" + Test.record(Test.get_testset(), deserialize(open(Cmd(Cmd(args), dir=@__DIR__)))) end end end diff --git a/test/TestVarField/runTestVarFieldOff.jl b/test/TestVarField/runTestVarFieldOff.jl index 6c522d0..41c209b 100644 --- a/test/TestVarField/runTestVarFieldOff.jl +++ b/test/TestVarField/runTestVarFieldOff.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestVarFieldOff") using TestVarFieldOff function runtest() diff --git a/test/TestVarField/runTestVarFieldOn.jl b/test/TestVarField/runTestVarFieldOn.jl index 1fc635d..229070a 100644 --- a/test/TestVarField/runTestVarFieldOn.jl +++ b/test/TestVarField/runTestVarFieldOn.jl @@ -1,7 +1,9 @@ using Test using Serialization -push!(LOAD_PATH, "$(@__DIR__)/build") +import Pkg +Pkg.activate(;temp=true) +Pkg.develop(path="$(@__DIR__)/build/TestVarFieldOn") using TestVarFieldOn function runtest() diff --git a/test/WrapitTestSetup.cmake b/test/WrapitTestSetup.cmake index 912d4fa..6e5261c 100644 --- a/test/WrapitTestSetup.cmake +++ b/test/WrapitTestSetup.cmake @@ -1,12 +1,9 @@ -## Standard pieces for setting up WrapIt tests with CMake +# Standard pieces for setting up WrapIt tests with CMake # This files needs: # - wrapit executable, which will be looked for in the standard program path. # Its location can be specified with -DWRAPIT=... # -# - print-cxx-path.jl, which will be looked for in the same directory containing -# this file -# # Assumptions: # # - the test script is named run.jl, with the cmake projet name @@ -20,39 +17,13 @@ # It will be replaced by the value define by the project() CMakeLists.txt statement when generating # the actual .wit file from the .wit.in -# Path for CxxWrap cmake files: -find_program(PRINT_CXXWRAP_PATH print-cxxwrap-path.jl - PATHS "${CMAKE_CURRENT_LIST_DIR}/../buildtools") - -if(PRINT_CXXWRAP_PATH STREQUAL PRINT_CXXWRAP_PATH-NOTFOUND) - message(FATAL_ERROR "Failed to find the print-cxxwarp-path.jl build tool.") -endif() - -execute_process( - COMMAND "${PRINT_CXXWRAP_PATH}" - OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE CXXWRAP_PREFIX - RESULT_VARIABLE result) - -if(NOT result EQUAL 0) - message(FATAL_ERROR "Execution of ${PRINT_CXXWRAP_PATH} failed") -endif() - -list(APPEND CMAKE_PREFIX_PATH ${CXXWRAP_PREFIX}) +# julia is used to retrieve the CxxWrap library paths +find_program(JULIA julia REQUIRED) # Some standard options and paths set(CMAKE_MACOSX_RPATH 1) #set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}/deps") -# Loaction of JLCxx - this should be injected via DCMAKE_PREFIX_PATH -# from the CxxWrap.prefix_path() in Julia -find_package(JlCxx) -get_target_property(JlCxx_location JlCxx::cxxwrap_julia LOCATION) -get_filename_component(JlCxx_location ${JlCxx_location} DIRECTORY) -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib;${JlCxx_location}") - -message(STATUS "Found JlCxx at ${JlCxx_location}") - # C++ standard set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -78,11 +49,76 @@ set(WRAPIT_WIT_FILE "${CMAKE_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.wit") set(WRAPPER_LIB "jl${CMAKE_PROJECT_NAME}") set(WRAPPER_JULIA_PACKAGE_DIR "${CMAKE_PROJECT_NAME}") set(WRAPPER_JULIA_PACKAGE_FILE "${CMAKE_PROJECT_NAME}.jl") + +# julia is used to retrieve the CxxWrap library paths +find_program(JULIA julia REQUIRED) + +message(STATUS "Wrapit configuration file: ${WRAPIT_WIT_FILE}") + +execute_process( + COMMAND ${JULIA} "--project=${CMAKE_BINARY_DIR}" -e "import TOML; print(get(TOML.parse(open(\"${WRAPIT_WIT_FILE}\")), \"cxxwrap_version\", \"\"));" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CXXWRAP_REQUESTED_VERSION + RESULT_VARIABLE result + ) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to parse ${WRAPIT_WIT_FILE}") +endif() + +if("${CXXWRAP_REQUESTED_VERSION}" STREQUAL "") + execute_process( + COMMAND ${JULIA} --project=${CMAKE_BINARY_DIR} -e "import Pkg; Pkg.add(\"CxxWrap\"); import CxxWrap; print(pkgversion(CxxWrap));" + OUTPUT_VARIABLE CXXWRAP_INSTALLED_VERSION + RESULT_VARIABLE result + ) + #if no CxxWrap version requirement was specified in the .wit file, + #we align it to the version that was installed + set(WRAPIT_OPT --add-cfg "cxxwrap_version=\"${CXXWRAP_INSTALLED_VERSION}\"") + message(STATUS ${WRAPIT}) +else() + execute_process( + COMMAND ${JULIA} --project=${CMAKE_BINARY_DIR} -e "import Pkg; import CxxWrap; Pkg.add(name=\"CxxWrap\", version=\"${CXXWRAP_REQUESTED_VERSION}\"); Pkg.resolve(); print(pkgversion(CxxWrap));" + OUTPUT_VARIABLE CXXWRAP_INSTALLED_VERSION + RESULT_VARIABLE result) +endif() + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to install CxxWrap") +elseif("${CXXWRAP_REQUESTED_VERSION}" STREQUAL "") + message(STATUS "CxxWrap version requested to be compatible with any version, using v${CXXWRAP_INSTALLED_VERSION}") +else() + message(STATUS "CxxWrap version requested to be compatible with ${CXXWRAP_REQUESTED_VERSION}, using version: ${CXXWRAP_INSTALLED_VERSION}") +endif() + +execute_process( + COMMAND "${JULIA}" --project=${CMAKE_BINARY_DIR} -e "import CxxWrap; print(CxxWrap.prefix_path())" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CXXWRAP_PREFIX + RESULT_VARIABLE result) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to retrieve CxxWrap library path") +else() + message(STATUS "CxxWrap library path prefix: ${CXXWRAP_PREFIX}") +endif() + +#This find_package(JlCxx...) command modifies CMAKE_CXX_STANDARD. +#So we save our value and restore it. +set(SAVED_CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD}) +find_package(JlCxx PATHS ${CXXWRAP_PREFIX}) +set(CMAKE_CXX_STANDARD ${SAVED_CMAKE_CXX_STANDARD}) + +get_target_property(JlCxx_location JlCxx::cxxwrap_julia LOCATION) +get_filename_component(JlCxx_location ${JlCxx_location} DIRECTORY) +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib;${JlCxx_location}") + + #configure_file(${CMAKE_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.wit.in ${CMAKE_BINARY_DIR}/${WRAPIT_WIT_FILE} @ONLY) # Generate the wrapper code. This is done at configure time. execute_process( - COMMAND "${WRAPIT}" -v "${WRAPIT_VERBOSITY}" --force --update --cmake --output-prefix "${CMAKE_BINARY_DIR}" "${WRAPIT_WIT_FILE}" + COMMAND "${WRAPIT}" ${WRAPIT_OPT} -v "${WRAPIT_VERBOSITY}" --force --update --cmake --output-prefix "${CMAKE_BINARY_DIR}" "${WRAPIT_WIT_FILE}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" COMMAND_ECHO STDERR RESULT_VARIABLE result) @@ -132,4 +168,4 @@ enable_testing() uncapitalize(${PROJECT_NAME} TEST_SCRIPT) set(TEST_SCRIPT "${CMAKE_SOURCE_DIR}/${TEST_SCRIPT}.jl") set(TEST_NAME ${CMAKE_PROJECT_NAME}) -add_test(NAME ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND env JULIA_LOAD_PATH=:${CMAKE_BINARY_DIR}/${WRAPPER_JULIA_PACKAGE_DIR}/src julia --project=.. "${TEST_SCRIPT}") +add_test(NAME ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND env JULIA_LOAD_PATH=:${CMAKE_BINARY_DIR}/${WRAPPER_JULIA_PACKAGE_DIR}/src julia --project=${CMAKE_BINARY_DIR} "${TEST_SCRIPT}") diff --git a/test/make.rules b/test/make.rules index 29e7991..23c5073 100644 --- a/test/make.rules +++ b/test/make.rules @@ -13,7 +13,15 @@ CXXFLAGS += $(patsubst -std=gnu%,,$(shell $(JL_SHARE)/julia-config.jl --cflags)) LDFLAGS += $(shell $(JL_SHARE)/julia-config.jl --ldflags) LDLIBS += $(shell $(JL_SHARE)/julia-config.jl --ldlibs) WRAPIT = $(shell which wrapit) -CXXWRAP_PREFIX=$(shell ../../buildtools/print-cxxwrap-path.jl) +WIT_FILE = $(MODULE_NAME).wit + +ifeq ($(CXXWRAP_VERSION),) +CXXWRAP_PREFIX=$(shell mkdir -p $(BUILD_DIR); julia --project=$(BUILD_DIR) -e "import Pkg; Pkg.add(\"CxxWrap\"); Pkg.resolve(); import CxxWrap; print(CxxWrap.prefix_path())") +CXXWRAP_VERSION=$(shell julia -e "import CxxWrap; print(pkgversion(CxxWrap));") +WRAPIT_OPT=--add-cfg cxxwrap_version=\"$(CXXWRAP_VERSION)\" +else +CXXWRAP_PREFIX=$(shell mkdir -p $(BUILD_DIR); julia --project=$(BUILD_DIR) -e "import Pkg; Pkg.add(name=\"CxxWrap\", version=\"$(CXXWRAP_VERSION)\"); Pkg.resolve(); import CxxWrap; print(CxxWrap.prefix_path())") +endif WRAPIT_PRODUCTS+=jl$(MODULE_NAME).cxx WRAPIT_ALL_PRODUCTS=lib$(MODULE_NAME)/src/$(WRAPIT_PRODUCTS) lib$(MODULE_NAME)/src/jl$(MODULE_NAME).h $(MODULE_NAME)/src/$(MODULE_NAME).jl @@ -57,7 +65,7 @@ clean: # Use of % instead of $(BUILD_DIR) instructs make that the recipee produces all the targets at once and prevent multiple executions with the -j N option. $(addprefix %,$(WRAPIT_ALL_PRODUCTS)): $(MODULE_NAME).wit - $(WRAPIT) -v $(WRAPIT_VERBOSITY) --force --output-prefix $(BUILD_DIR) $< + $(WRAPIT) $(WRAPIT_OPT) -v $(WRAPIT_VERBOSITY) --force --output-prefix $(BUILD_DIR) $< $(BUILD_DIR)/lib$(MODULE_NAME)/src/%.cxx: %.cxx [ -d $(@D) ] || mkdir -p $(@D) diff --git a/test/runtests.jl b/test/runtests.jl index 1ffe587..246345f 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,10 +1,5 @@ #!/usr/bin/env julia # -# Bootstrap environment for the first run -using Pkg -Pkg.activate(@__DIR__) -Pkg.instantiate() - using Test using Serialization tests = [ "TestSizet", "TestCtorDefVal", "TestAccessAndDelete", "TestNoFinalizer", "TestInheritance", @@ -18,7 +13,7 @@ testExamples = true #List of test can also be specified on the command line if length(ARGS) > 0 - tests = filter(x->x ≠ "examples", ARGS) + tests = replace.(filter(x->x ≠ "examples", ARGS), r"/$" => "") if length(ARGS) == length(tests) testExamples = false end @@ -29,7 +24,7 @@ ncores=Sys.CPU_THREADS @testset verbose=true "Tests" begin for t in tests - @testset "$t" failfast=true begin + @testset "$t" failfast=false begin source_dir = t build_dir = joinpath(source_dir, "build") test_script = "run" * t * ".jl" @@ -39,11 +34,12 @@ ncores=Sys.CPU_THREADS println("Try to run cmake...") #cmake based build # Configure and build with CMake - run(`cmake -S $source_dir -B $build_dir`) + run(`cmake --fresh -S $source_dir -B $build_dir`) run(`cmake --build $build_dir -j $ncores`) else println("source_dir:", source_dir) #assumes plain make build + run(`make -C $source_dir clean`) run(`make -C $source_dir -j $ncores`) end true diff --git a/test/test_examples.jl b/test/test_examples.jl index 71b6661..3a7501c 100755 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -4,6 +4,8 @@ using Test import Pkg Pkg.activate(; temp=true) +#use current project also for spawned julia processes: +ENV["JULIA_PROJECT"]=Pkg.project().path ex_basedir = normpath(joinpath(pwd(), "../examples")) @@ -31,11 +33,6 @@ catch println(stderr, "ROOT software not found. The ROOT example won't be tested.") end -#FIXME. -#Temporary trick to get the 'make test' command run in an environment -#with CxxWrap installed until. Also a nice way to get them run in -#a clean environment. -ENV["JULIA_PROJECT"]=Pkg.project().path if run_exroot ex = "ex002-ROOT"