Skip to content

Commit

Permalink
Added support for CxxWrap 0.15.x and several improvements.
Browse files Browse the repository at this point in the history
Code for CxxWrap versions 0.15.x to generate differs from code for vesions
0.14.x, due to a change in the API. Wrapping of STL vectors of object pointers
do not work with version 0.15.0.  More details in
JuliaInterop/CxxWrap.jl#419. The unit tests and the
ROOT example that use such vector are conifgure to use 0.14.x version for now.

The CxxWrap version the code should be generated can be selected using the
new `cxxwrap_version` configuration parameter.

Several other changes:

  o new --get command line option to retrieve values of some parameters;

  o new --add-cfg command line option to configure parameter normally
    set in the .wit configuration file;

  o fixed generation of random uuid;

  o add check of the verions of the libcxxwrap when compiling and when running
  the code.

  o Cleanup Julia package dependencies installation and version handling;
    Removed project setting from runtests.jl which is not needed anymore, as
    each test runs within its own temporary Julia project.
  • Loading branch information
grasph committed Mar 23, 2024
1 parent 5e1757c commit 8c60721
Show file tree
Hide file tree
Showing 50 changed files with 620 additions and 212 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Automatic generation of C++ -- Julia bindings

![Linux](https://github.com/grasph/wrapit/actions/workflows/test-linux.yml/badge.svg) ![macOS](https://github.com/grasph/wrapit/actions/workflows/test-macos.yml/badge.svg)

The goal of this project is the automatization of the generation of [Julia](https://julialang.org) bindings for C++ libraries.

The WrapIt! tool complements the [CxxWrap.jl](https://github.com/JuliaInterop/CxxWrap.jl) package. It generates automatically the c++ wrapper code needed by CxxWrap.jl from the c++ header files of the library to wrap.
Expand Down
5 changes: 5 additions & 0 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
100 changes: 71 additions & 29 deletions examples/ex001-HelloWorld/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.")

Expand All @@ -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)
Expand Down
13 changes: 11 additions & 2 deletions examples/ex001-HelloWorld/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion examples/ex001-HelloWorld/install.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
37 changes: 25 additions & 12 deletions examples/ex002-ROOT/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

BUILD_DIR=build

JL_SHARE = $(shell julia -e 'print(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia"))')
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions examples/ex002-ROOT/ROOT.wit
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
Expand Down
Loading

0 comments on commit 8c60721

Please sign in to comment.