This is a complete example of how to create a Modern CMake C++ Project with the SWIG code generator to generate wrapper and package for Python, .Net and Java.
This project should run on GNU/Linux, MacOS and Windows.
Python 2 | Python 3 | .Net | Java- GNU/Linux wrapper
- MacOS wrapper
- Windows wrapper
- GNU/Linux wrapper
- MacOS wrapper
- Windows wrapper
- GNU/Linux wrapper
- MacOS wrapper
- Windows wrapper
- GNU/Linux wrapper
- MacOS wrapper
- Windows wrapper
To complexify a little, the CMake project is composed of three libraries (Foo, Bar and FooBar)
with the following dependencies:
Foo:
Bar:
FooBar: PUBLIC Foo PRIVATE Bar
FooBarApp: PRIVATE FooBarThe project layout is as follow:
-
CMakeLists.txt Top-level for CMake based build.
-
cmake Subsidiary CMake files.
-
Foo Root directory for
Foolibrary.- CMakeLists.txt for
Foo. - include public folder.
- python
- CMakeLists.txt for
FooPython. - foo.i SWIG Python wrapper.
- CMakeLists.txt for
- dotnet
- CMakeLists.txt for
Foo.Net. - foo.i SWIG .Net wrapper.
- CMakeLists.txt for
- java
- CMakeLists.txt for
FooJava. - java/foo.i SWIG Java wrapper.
- CMakeLists.txt for
- src private folder.
- CMakeLists.txt for
-
Bar Root directory for
Barlibrary.- CMakeLists.txt for
Bar. - include public folder.
- python
- CMakeLists.txt for
BarPython. - bar.i SWIG Python wrapper.
- CMakeLists.txt for
- dotnet
- CMakeLists.txt for
Bar.Net. - bar.i SWIG .Net wrapper.
- CMakeLists.txt for
- java
- CMakeLists.txt for
BarJava. - java/bar.i SWIG Java wrapper.
- CMakeLists.txt for
- src private folder.
- CMakeLists.txt for
-
FooBar Root directory for
FooBarlibrary.- CMakeLists.txt for
FooBar. - include public folder.
- python
- CMakeLists.txt for
FooBarPython. - foobar.i SWIG Python wrapper.
- CMakeLists.txt for
- dotnet
- CMakeLists.txt for
FooBar.Net. - foobar.i SWIG .Net wrapper.
- CMakeLists.txt for
- java
- CMakeLists.txt for
FooBarJava. - java/foobar.i SWIG Java wrapper.
- CMakeLists.txt for
- src private folder.
- CMakeLists.txt for
-
FooBarApp Root directory for
FooBarAppexecutable.- CMakeLists.txt for
FooBarApp. - src private folder.
- CMakeLists.txt for
To build the C++ project, as usual:
cmake -S. -Bbuild
cmake --build build --target allSince we want to use the CMAKE_BINARY_DIR to generate the wrapper package (e.g. python wheel package) as well as be able to test from the build directory. We need to enable:
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)And have a finely tailored rpath for each library.
For Foo and Bar which depend on nothing:
set(CMAKE_INSTALL_RPATH "$ORIGIN")For FooBar which depend on Foo and Bar:
set(CMAKE_INSTALL_RPATH "$ORIGIN:$ORIGIN/../Foo:$ORIGIN/../Bar")For FooBarApp which depend on FooBar:
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN/../FooBar")Using swig to generate wrapper it's easy thanks to the modern UseSWIG module (CMake >= 3.14).
Creating a Python binary package containing all .py and .so (with good rpath) is not so easy...
note: SWIG automatically put its target(s) in all, thus make will also call
swig and generate _module.so.
Since python use the directory name where __init__.py file is located.
We would like to have pyFoo.py generated file in build/Foo and not in build/Foo/python.
You can use CMAKE_SWIG_DIR to change the output directory for the .py file e.g.:
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/..)And you can use CMAKE_LIBRARY_OUTPUT_DIRECTORY to change the output directory for the .so file e.g.:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..)[optional]You can use SWIG_OUTFILE_DIR to change the output directory for the .cxx file e.g.:
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/..)Then you only need to create a __init__.py file in build/Foo to be able to use
the build directory to generate the Python package.
note: you allways need $ORIGIN/../${PROJECT_NAME}/.libs since _pyFoo.so will depend on libFoo.so
(which will be built in the same directory see above).
To avoid to put hardcoded path to SWIG .so generated files,
we could use $<TARGET_FILE_NAME:tgt> to retrieve the file (and also deal with Mac/Windows suffix, and target dependencies).
In order for setup.py to use
cmake generator expression
(e.g. $<TARGET_FILE_NAME:_pyFoo>). We need to generate it at build time (e.g. using
add_custom_command()).
note: This will also add automatically a dependency between the command and the TARGET !
The CONTRIBUTING.md file contains instructions on how to file the Contributor License Agreement before sending any pull requests (PRs). Of course, if you're new to the project, it's usually best to discuss any proposals and reach consensus before sending your first PR.
Apache 2. See the LICENSE file for details.
This is not an official Google product, it is just code that happens to be owned by Google.