Skip to content

Commit f8909d2

Browse files
committed
Merge branch 'master' into feature/readme
2 parents a775e7b + 60bed89 commit f8909d2

File tree

10 files changed

+310
-3
lines changed

10 files changed

+310
-3
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@
3333

3434
# Directories
3535
.ipynb_checkpoints/*
36-
build/*
36+
**/build/*
3737
example/__pycache__

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ Project template using GTSAM + python wrapping
2222
- Run `make`, and the wrapped module will be installed to a `cython` directory.
2323
- Navigate to the `cython` directory and run `python setup.py install`.
2424

25-
For more detailed information, please see
26-
[this link](https://github.com/borglab/gtsam/tree/develop/cython)
25+
## DOCUMENTATION
26+
27+
For more detailed information, please refer to the [tutorial](TUTORIAL.md).

TUTORIAL.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Tutorial
2+
3+
This is a tutorial on how to wrap your own C++ projects using GTSAM's python wrapper.
4+
5+
# Prerequisites
6+
7+
We assume you have Python 3 installed. We support Python 3.6 and up.
8+
9+
We also assume some knowledge of how Python packaging and setup works. If you understand how to write your own basic `setup.py` file, you should be fine.
10+
Using this template project, you should only need to update the metadata information about your project. Check out the [python packaging website](https://packaging.python.org/tutorials/packaging-projects/) to learn more.
11+
12+
As a bonus, if you understand Cython's build process, this tutorial should be fairly intuitive.
13+
14+
**NOTE** This tutorial has been tested using GTSAM version 4.0.x and above.
15+
16+
# Project Setup
17+
18+
As a set of minimal requirements, the project should be set up as follows:
19+
20+
```
21+
top-level-directory
22+
|
23+
|- CMakeLists.txt
24+
|- <project>.h
25+
|- __init__.py.in
26+
|- setup.py
27+
|- src/
28+
29+
```
30+
31+
The files are
32+
33+
1. `CMakeLists.txt`: The cmake definition file.
34+
2. `<project>.h`: The header file which specifies all the code components to be wrapped.
35+
3. `__init__.py.in`: Template __init__.py file used by cmake.
36+
4. `setup.py`: The file used by setuptools to generate the egg/wheel.
37+
5. `src/`: All your C++ source code goes here.
38+
39+
40+
# CMake Configuration
41+
42+
In this section, we will go through a step-by-step process of defining the `CMakeLists.txt` file which will generated our wrapped code.
43+
44+
An illustrative example is provided in the `src` directory of this repository.
45+
46+
1. Define project name.
47+
2. Optionally, set the Python version you'd like to target. This should ideally be the same as the version you used to build the wrapper.
48+
3. Include `GTSAM` package. This allows use to use the cython install path automatically. CMake will take care of the rest.
49+
50+
```cmake
51+
find_package(GTSAM REQUIRED)
52+
include_directories(${GTSAM_CYTHON_INSTALL_PATH})
53+
include_directories(${GTSAM_EIGENCY_INSTALL_PATH})
54+
```
55+
56+
4. The second package is `GTSAMCMakeTools`. This gives us access to the wrapping functions which we will use later on.
57+
58+
```cmake
59+
find_package(GTSAMCMakeTools CONFIG)
60+
include(GtsamCythonWrap) # Automatic Cython wrapper generation
61+
```
62+
63+
5. These next few steps should be familiar for CMake users. We first include the project source directory.
64+
65+
```cmake
66+
include_directories(BEFORE "${PROJECT_SOURCE_DIR}")
67+
```
68+
69+
6. Now we can specify the building and linking of our project code as a shared library.
70+
71+
```cmake
72+
add_library(${PROJECT_NAME} SHARED src/greeting.h src/greeting.cpp)
73+
target_link_libraries(${PROJECT_NAME} gtsam)
74+
```
75+
76+
7. And finally, we can install the shared library.
77+
78+
```cmake
79+
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
80+
```
81+
82+
8. Now we get to the wrapping part. To specify our project as a package we need to include an `__init__.py` file at the top level. This will allow python imports to work correctly. We can use the basic `__init__.py.in` template in this repo since it is pretty generic.
83+
84+
```cmake
85+
configure_file(${PROJECT_SOURCE_DIR}/__init__.py.in ${PROJECT_BINARY_DIR}/cython/${PROJECT_NAME}/__init__.py)
86+
```
87+
88+
9. To help build and install the wrapped project, we make use of a `setup.py` file. This file can be customized as per your requirements. **NOTE** This command only copies over the `setup.py` file, so make sure you make any updates **BEFORE** you run `cmake`.
89+
90+
```cmake
91+
configure_file(${PROJECT_SOURCE_DIR}/setup.py ${PROJECT_BINARY_DIR}/cython/setup.py COPYONLY)
92+
```
93+
94+
10. Finally, we specify the wrapping function so that the GTSAM wrapper can do its job. We require only one function `wrap_and_install_library_cython` which takes the following 5 arguments:
95+
96+
1. Interface Header: A `.h` file which defines what classes, functions, etc., are to be wrapped.
97+
2. Extra Imports: This is a set of `cython` imports included in the generated Cython files. You can use this to specify any additional imports your project may be dependent on.
98+
3. Install Path: This is the location where the wrapped package will be installed on running `make install`.
99+
4. Libraries: A semi-colon separated list of libraries which the project will be linked against. At the very least, you should link against `gtsam` and the generated shared object file.
100+
5. Dependencies: This is a semi-colon separated list of dependency targets that need to be built before the code can be compiled and wrapped. This is nothing but a list of CMake targets.
101+
102+
```cmake
103+
wrap_and_install_library_cython("example.h" # interface_header
104+
"" # extra imports
105+
"./${PROJECT_NAME}" # install path
106+
"gtsam;${PROJECT_NAME}" # library to link with
107+
"wrap;gtsam" # dependencies which need to be built before wrapping
108+
)
109+
```
110+
111+
# Compiling
112+
113+
To compile and wrap the code, the familiar CMake process is followed. Starting from the directory where the `setup.py` file is located, we create a build directory and run `cmake` and `make`.
114+
115+
```sh
116+
mkdir build && cd build
117+
cmake .. && make
118+
```
119+
120+
Finally, we go into the generated `cython` directory where the `setup.py` file is present, and run `python setup.py build` to generate the final package.
121+
122+
# Installing
123+
124+
To install the package, in the `cython` directory we can run `python setup.py build`.

example/CMakeLists.txt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# This file should be used as a template for creating new projects with Python wrapping using the CMake tools
2+
3+
###################################################################################
4+
# 1. To create your own project, replace "example" with the actual name of your project
5+
cmake_minimum_required(VERSION 3.0)
6+
project(example CXX C)
7+
8+
###################################################################################
9+
# 2. Set the python version
10+
set(GTSAM_PYTHON_VERSION "3.6")
11+
12+
###################################################################################
13+
# 3. Find GTSAM components so we have access to the GTSAM Cython install path
14+
find_package(GTSAM REQUIRED) # Uses installed package
15+
# Note: Since Jan-2019, GTSAMConfig.cmake defines exported CMake targets
16+
# that automatically do include the include_directories() without the need
17+
# to call include_directories(), just target_link_libraries(NAME gtsam)
18+
#include_directories(${GTSAM_INCLUDE_DIR})
19+
20+
# Include the required GTSAM Cython libraries
21+
include_directories(${GTSAM_CYTHON_INSTALL_PATH})
22+
include_directories(${GTSAM_EIGENCY_INSTALL_PATH})
23+
24+
###################################################################################
25+
# 4. Get the wrapping functions
26+
# Include GTSAM CMake tools
27+
find_package(GTSAMCMakeTools CONFIG)
28+
#include(GtsamBuildTypes) # Load build type flags and default to Debug mode
29+
#include(GtsamTesting) # Easy functions for creating unit tests and scripts
30+
include(GtsamCythonWrap) # Automatic Cython wrapper generation
31+
32+
###################################################################################
33+
# 5. Add the local source directory for CMake
34+
# Ensure that local folder is searched before library folders
35+
include_directories(BEFORE "${PROJECT_SOURCE_DIR}")
36+
37+
38+
###################################################################################
39+
# 6. Build static library from common sources
40+
add_library(${PROJECT_NAME} SHARED src/greeting.h src/greeting.cpp)
41+
target_link_libraries(${PROJECT_NAME} gtsam)
42+
43+
###################################################################################
44+
# 7. Install library
45+
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
46+
47+
###################################################################################
48+
# # Build tests (CMake tracks the dependecy to link with GTSAM through our project's static library)
49+
# gtsamAddTestsGlob("${PROJECT_NAME}" "tests/test*.cpp" "" "${PROJECT_NAME}")
50+
51+
###################################################################################
52+
# # Build scripts (CMake tracks the dependecy to link with GTSAM through our project's static library)
53+
# gtsamAddExamplesGlob("*.cpp" "" "${PROJECT_NAME}")
54+
55+
###################################################################################
56+
# 8. Copy the __init__.py file so Cython recognizes this as a package.
57+
# This function also updates the contents to use the correct package name.
58+
configure_file(${PROJECT_SOURCE_DIR}/__init__.py.in ${PROJECT_BINARY_DIR}/cython/${PROJECT_NAME}/__init__.py)
59+
60+
###################################################################################
61+
# 9. (Strict) Copy over the setup.py file so we can build and install the package.
62+
configure_file(${PROJECT_SOURCE_DIR}/setup.py ${PROJECT_BINARY_DIR}/cython/setup.py COPYONLY)
63+
64+
###################################################################################
65+
# 10. Build Cython wrapper (CMake tracks the dependecy to link with GTSAM through our project's static library)
66+
wrap_and_install_library_cython("example.h" # interface_header
67+
"" # extra imports
68+
"./${PROJECT_NAME}" # install path
69+
"gtsam;${PROJECT_NAME}" # library to link with
70+
"wrap;gtsam" # dependencies which need to be built before wrapping
71+
)

example/__init__.py

Whitespace-only changes.

example/__init__.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .${PROJECT_NAME} import *

example/example.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* ----------------------------------------------------------------------------
2+
3+
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
4+
* Atlanta, Georgia 30332-0415
5+
* All Rights Reserved
6+
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7+
8+
* See LICENSE for the license information
9+
10+
* -------------------------------------------------------------------------- */
11+
12+
/**
13+
* @file example.h
14+
* @brief Example wrapper interface file for Python
15+
* @author Varun Agrawal
16+
*/
17+
18+
// This is an interface file for automatic Python wrapper generation. See
19+
// gtsam.h for full documentation and more examples.
20+
21+
#include <src/greeting.h>
22+
23+
namespace example {
24+
25+
class Greeting {
26+
Greeting();
27+
void sayHello() const;
28+
void sayGoodbye() const;
29+
};
30+
31+
}

example/setup.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import os
2+
import sys
3+
try:
4+
from setuptools import setup, find_packages
5+
except ImportError:
6+
from distutils.core import setup, find_packages
7+
8+
packages = find_packages()
9+
10+
setup(
11+
name='example',
12+
description='Simple example of wrapping projects with Python and GTSAM',
13+
url='https://gtsam.org/',
14+
version='1.0.0',
15+
author='Varun Agrawal',
16+
author_email='[email protected]',
17+
license='Simplified BSD license',
18+
keywords='wrapper tutorial example',
19+
long_description="",
20+
long_description_content_type='text/markdown',
21+
python_requires='>=3.6',
22+
# https://pypi.org/pypi?%3Aaction=list_classifiers
23+
classifiers=[
24+
'Development Status :: 5 - Production/Stable',
25+
'Intended Audience :: Education',
26+
'Intended Audience :: Developers',
27+
'Intended Audience :: Science/Research',
28+
'Operating System :: MacOS',
29+
'Operating System :: Microsoft :: Windows',
30+
'Operating System :: POSIX',
31+
'License :: OSI Approved :: BSD License',
32+
'Programming Language :: Python :: 2',
33+
'Programming Language :: Python :: 3',
34+
],
35+
36+
packages=packages,
37+
# Load the built shared object files
38+
package_data={package:
39+
[f for f in os.listdir(package.replace('.', os.path.sep)) if os.path.splitext(f)[1] in ('.so', '.pyd')]
40+
for package in packages
41+
},
42+
install_requires=[line.strip() for line in '''
43+
Cython>=0.25.2
44+
backports_abc>=0.5
45+
numpy>=1.12.0
46+
'''.splitlines() if len(line.strip()) > 0 and not line.strip().startswith('#')]
47+
)

example/src/greeting.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include "greeting.h"
2+
3+
namespace example {
4+
5+
/// Print a greeting
6+
void Greeting::sayHello() const {
7+
std::cout << "Hello from GTSAM!" << std::endl;
8+
std::cout << "Here's a Rot3 for you " << gtsam::Rot3() << std::endl;
9+
}
10+
11+
/// Print a farewell
12+
void Greeting::sayGoodbye() const {
13+
std::cout << "Goodbye, robot" << std::endl;
14+
}
15+
16+
};

example/src/greeting.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <gtsam/geometry/Rot3.h>
2+
#include <iostream>
3+
#include <string>
4+
5+
namespace example {
6+
7+
class Greeting {
8+
public:
9+
/// Print a greeting
10+
void sayHello() const;
11+
12+
/// Print a farewell
13+
void sayGoodbye() const;
14+
};
15+
16+
} // namespace example

0 commit comments

Comments
 (0)