Skip to content

Commit

Permalink
Squashed 'wrap/' changes from dfa624e77..09f8bbf71
Browse files Browse the repository at this point in the history
09f8bbf71 Merge pull request #25 from borglab/fix/function-name
0dbfb6c13 fix function name to be the correct one
f69f8b01f Merge pull request #24 from borglab/fix/pip
6519a6627 use pip install to overcome superuser issues
b11ecf4e8 Merge pull request #23 from borglab/fix/remove-pip-args
813030108 remove pip-args since we are using setup.py
498d233e0 Merge pull request #22 from borglab/fix/package-install
846212ac3 set correct flags for installing gtwrap package
62161cd20 Merge pull request #21 from borglab/feature/script-vars
93be1d9f8 set script variables and move pybind11 loading so gtwrap can be used under gtsam
8770e3c7e Merge pull request #20 from borglab/fix/pybind-include
8c3c83618 proper placement of pybind11 include
a9ad4f504 Merge pull request #19 from borglab/feature/package
99d8a12c7 added more documentation
4cbec1579 change to macro so we don't have to deal with function scopes
b83e405b8 updates to completely install the package
38a64b3de new scripts which will be installed to bin directory
bf9646235 Merge pull request #18 from borglab/fix/cmake-min
c7c280099 Consistent cmake minimum required
42df58f62 Merge pull request #17 from borglab/fix/cleanup
e580b282d version bump
4ccd66fa5 More finegrained handling of Python version
6476fd710 Merge pull request #16 from borglab/feature/better-find-python
8ac1296a0 use setup.py to install dependencies
e9ac473be install dependencies and support versions of CMake<3.12
cf272dbd2 Merge pull request #15 from borglab/feature/utils
ffc9cc4f7 new utils to reduce boilerplate
20e8e8b7a Merge pull request #11 from borglab/feature/package
04b844bd6 use new version of FindPython and be consistent
3f9d7a32a Merge pull request #13 from borglab/add_license
c791075a6 Add LICENSE
517b67c46 correct working directory for setup.py
1b22b47ae move matlab.h to root directory
37b407214 Proper source directory path for use in other projects
61696dd5d configure PybindWrap within the cmake directory
1b91fc9af add config file so we can use find_package
a1e6f4f53 small typo
da9f351be updated README and housekeeping
64b8f78d5 files needed to allow for packaging
bddda7f54 package structure

git-subtree-dir: wrap
git-subtree-split: 09f8bbf7172ba8b1bd3d2484795743f16e1a5893
  • Loading branch information
varunagrawal committed Jan 4, 2021
1 parent 16418e2 commit 7cc7232
Show file tree
Hide file tree
Showing 18 changed files with 424 additions and 204 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
__pycache__/
.vscode/
*build*
*dist*
*.egg-info
55 changes: 55 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
cmake_minimum_required(VERSION 3.9)

# Set the project name and version
project(GTwrap VERSION 1.0)

# ##############################################################################
# General configuration

set(WRAP_PYTHON_VERSION
"Default"
CACHE STRING "The Python version to use for wrapping")

include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GtwrapUtils.cmake)
gtwrap_get_python_version(${WRAP_PYTHON_VERSION})

# ##############################################################################
# Install the CMake file to be used by other projects
if(WIN32 AND NOT CYGWIN)
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/CMake")
else()
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake")
endif()

# Install scripts to the standard CMake script directory.
install(FILES cmake/gtwrapConfig.cmake cmake/PybindWrap.cmake
cmake/GtwrapUtils.cmake
DESTINATION "${SCRIPT_INSTALL_DIR}/gtwrap")

# Install wrapping scripts as binaries to `CMAKE_INSTALL_PREFIX/bin` so they can
# be invoked for wrapping.
install(PROGRAMS scripts/pybind_wrap.py scripts/matlab_wrap.py TYPE BIN)

# Install pybind11 directory to `CMAKE_INSTALL_PREFIX/lib/pybind11` This will
# allow the gtwrapConfig.cmake file to load it later.
install(DIRECTORY pybind11 TYPE LIB)

# ##############################################################################
# Install the Python package
find_package(
Python ${WRAP_PYTHON_VERSION}
COMPONENTS Interpreter
REQUIRED)

# Detect virtualenv and set Pip args accordingly
# https://www.scivision.dev/cmake-install-python-package/
if(DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX})
set(_pip_args)
else()
set(_pip_args "--user")
endif()
#TODO add correct flags for virtualenv

# Finally install the gtwrap python package.
execute_process(COMMAND ${Python_EXECUTABLE} -m pip install . ${_pip_args}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
13 changes: 13 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright (c) 2010, Georgia Tech Research Corporation
Atlanta, Georgia 30332-0415
All Rights Reserved

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66 changes: 34 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,40 @@ It was designed to be more general than just wrapping GTSAM. For notes on creati

1. This library uses `pybind11`, which is included as a subdirectory in GTSAM.
2. The `interface_parser.py` in this library uses `pyparsing` to parse the interface file `gtsam.h`. Please install it first in your current Python environment before attempting the build.
```
python3 -m pip install pyparsing
```

```
python3 -m pip install pyparsing
```

## Getting Started

Clone this repository to your local machine and perform the standard CMake install:

```sh
mkdir build && cd build
cmake ..
make install # use sudo if needed
```

Using `wrap` in your project is straightforward from here. In you `CMakeLists.txt` file, you just need to add the following:

```cmake
include(PybindWrap)
pybind_wrap(${PROJECT_NAME}_py # target
${PROJECT_SOURCE_DIR}/cpp/${PROJECT_NAME}.h # interface header file
"${PROJECT_NAME}.cpp" # the generated cpp
"${PROJECT_NAME}" # module_name
"gtsam" # top namespace in the cpp file
"${ignore}" # ignore classes
${PROJECT_BINARY_DIR}/${PROJECT_NAME}.tpl
${PROJECT_NAME} # libs
"${PROJECT_NAME}" # dependencies
ON # use boost
)
```

For more information, please follow our [tutorial](https://github.com/borglab/gtsam-project-python).

## GTSAM Python wrapper

Expand Down Expand Up @@ -45,32 +76,3 @@ It was designed to be more general than just wrapping GTSAM. For notes on creati
python setup.py install
```
- NOTE: It's a good idea to create a virtual environment otherwise it will be installed in your system Python's site-packages.
## Old GTSAM Wrapper
*Outdated note from the original wrap.*
TODO: Update this.
It was designed to be more general than just wrapping GTSAM, but a small amount of GTSAM specific code exists in `matlab.h`, the include file that is included by the `mex` files. The GTSAM-specific functionality consists primarily of handling of Eigen Matrix and Vector classes.
For notes on creating a wrap interface, see `gtsam.h` for what features can be wrapped into a toolbox, as well as the current state of the toolbox for GTSAM. For more technical details on the interface, please read comments in `matlab.h`
Some good things to know:
OBJECT CREATION
- Classes are created by special constructors, e.g., `new_GaussianFactorGraph_.cpp`.
These constructors are called from the MATLAB class `@GaussianFactorGraph`.
`new_GaussianFactorGraph_` calls wrap_constructed in `matlab.h`, see documentation there
METHOD (AND CONSTRUCTOR) ARGUMENTS
- Simple argument types of methods, such as "double", will be converted in the
`mex` wrappers by calling unwrap<double>, defined in matlab.h
- Vector and Matrix arguments are normally passed by reference in GTSAM, but
in `gtsam.h` you need to pretend they are passed by value, to trigger the
generation of the correct conversion routines `unwrap<Vector>` and `unwrap<Matrix>`
- passing classes as arguments works, provided they are passed by reference.
This triggers a call to unwrap_shared_ptr
74 changes: 74 additions & 0 deletions cmake/GtwrapUtils.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Utilities to help with wrapping.

macro(get_python_version)
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
# Use older version of cmake's find_python
find_package(PythonInterp)

if(NOT ${PYTHONINTERP_FOUND})
message(
FATAL_ERROR
"Cannot find Python interpreter. Please install Python >= 3.6.")
endif()

find_package(PythonLibs ${PYTHON_VERSION_STRING})

set(Python_VERSION_MAJOR
${PYTHON_VERSION_MAJOR}
PARENT_SCOPE)
set(Python_VERSION_MINOR
${PYTHON_VERSION_MINOR}
PARENT_SCOPE)
set(Python_VERSION_PATCH
${PYTHON_VERSION_PATCH}
PARENT_SCOPE)
set(Python_EXECUTABLE
${PYTHON_EXECUTABLE}
PARENT_SCOPE)

else()
# Get info about the Python interpreter
# https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
find_package(Python COMPONENTS Interpreter Development)

if(NOT ${Python_FOUND})
message(
FATAL_ERROR
"Cannot find Python interpreter. Please install Python>=3.6.")
endif()

endif()
endmacro()

# Set the Python version for the wrapper and set the paths to the executable and
# include/library directories. WRAP_PYTHON_VERSION can be "Default" or a
# specific major.minor version.
macro(gtwrap_get_python_version WRAP_PYTHON_VERSION)
# Unset these cached variables to avoid surprises when the python in the
# current environment are different from the cached!
unset(Python_EXECUTABLE CACHE)
unset(Python_INCLUDE_DIRS CACHE)
unset(Python_VERSION_MAJOR CACHE)
unset(Python_VERSION_MINOR CACHE)
unset(Python_VERSION_PATCH CACHE)

# Allow override
if(${WRAP_PYTHON_VERSION} STREQUAL "Default")
# Check for Python3 or Python2 in order
get_python_version()

# Set the wrapper python version
set(WRAP_PYTHON_VERSION
"${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.${Python_VERSION_PATCH}"
CACHE STRING "The version of Python to build the wrappers against."
FORCE)

else()
# Find the Python that best matches the python version specified.
find_package(
Python ${WRAP_PYTHON_VERSION}
COMPONENTS Interpreter Development
EXACT REQUIRED)
endif()

endmacro()
35 changes: 4 additions & 31 deletions cmake/PybindWrap.cmake
Original file line number Diff line number Diff line change
@@ -1,32 +1,5 @@
# Unset these cached variables to avoid surprises when the python in the current
# environment are different from the cached!
unset(PYTHON_EXECUTABLE CACHE)
unset(PYTHON_INCLUDE_DIR CACHE)
unset(PYTHON_MAJOR_VERSION CACHE)

# Allow override from command line
if(NOT DEFINED WRAP_USE_CUSTOM_PYTHON_LIBRARY)
if(WRAP_PYTHON_VERSION STREQUAL "Default")
find_package(PythonInterp REQUIRED)
find_package(PythonLibs REQUIRED)
else()
find_package(PythonInterp
${WRAP_PYTHON_VERSION}
EXACT
REQUIRED)
find_package(PythonLibs
${WRAP_PYTHON_VERSION}
EXACT
REQUIRED)
endif()
endif()

set(DIR_OF_WRAP_PYBIND_CMAKE ${CMAKE_CURRENT_LIST_DIR})

set(PYBIND11_PYTHON_VERSION ${WRAP_PYTHON_VERSION})

add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pybind11 pybind11)

# User-friendly Pybind11 wrapping and installing function.
# Builds a Pybind11 module from the provided interface_header.
# For example, for the interface header gtsam.h, this will
Expand Down Expand Up @@ -65,7 +38,7 @@ function(pybind_wrap

add_custom_command(OUTPUT ${generated_cpp}
COMMAND ${PYTHON_EXECUTABLE}
${CMAKE_SOURCE_DIR}/wrap/pybind_wrapper.py
${PYBIND_WRAP_SCRIPT}
--src
${interface_header}
--out
Expand All @@ -89,9 +62,9 @@ function(pybind_wrap
# ~~~
add_custom_command(OUTPUT ${generated_cpp}
DEPENDS ${interface_header}
${CMAKE_SOURCE_DIR}/wrap/interface_parser.py
${CMAKE_SOURCE_DIR}/wrap/pybind_wrapper.py
${CMAKE_SOURCE_DIR}/wrap/template_instantiator.py
# @GTWRAP_SOURCE_DIR@/gtwrap/interface_parser.py
# @GTWRAP_SOURCE_DIR@/gtwrap/pybind_wrapper.py
# @GTWRAP_SOURCE_DIR@/gtwrap/template_instantiator.py
APPEND)

pybind11_add_module(${target} ${generated_cpp})
Expand Down
27 changes: 27 additions & 0 deletions cmake/gtwrapConfig.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This config file modifies CMAKE_MODULE_PATH so that the wrap cmake files may
# be included This file also allows the use of `find_package(gtwrap)` in CMake.

set(GTWRAP_DIR "${CMAKE_CURRENT_LIST_DIR}")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")

if(WIN32 AND NOT CYGWIN)
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/CMake")
else()
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake")
endif()

# Standard includes
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
include(CMakeDependentOption)

# Load all the CMake scripts from the standard location
include(${SCRIPT_INSTALL_DIR}/gtwrap/PybindWrap.cmake)
include(${SCRIPT_INSTALL_DIR}/gtwrap/GtwrapUtils.cmake)

# Set the variables for the wrapping scripts to be used in the build.
set(PYBIND_WRAP_SCRIPT "${CMAKE_INSTALL_FULL_BINDIR}/pybind_wrap.py")
set(MATLAB_WRAP_SCRIPT "${CMAKE_INSTALL_FULL_BINDIR}/matlab_wrap.py")

# Load the pybind11 code from the library installation path
add_subdirectory(${CMAKE_INSTALL_FULL_LIBDIR}/pybind11 pybind11)
Empty file added gtwrap/__init__.py
Empty file.
File renamed without changes.
58 changes: 4 additions & 54 deletions matlab_wrapper.py → gtwrap/matlab_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import argparse
import textwrap

import interface_parser as parser
import template_instantiator as instantiator
import gtwrap.interface_parser as parser
import gtwrap.template_instantiator as instantiator

from functools import reduce
from functools import partial
Expand Down Expand Up @@ -1666,7 +1666,7 @@ def wrap(self):
return self.content


def _generate_content(cc_content, path, verbose=False):
def generate_content(cc_content, path, verbose=False):
"""Generate files and folders from matlab wrapper content.
Keyword arguments:
Expand Down Expand Up @@ -1698,7 +1698,7 @@ def _debug(message):
for sub_content in c:
import sys
_debug("sub object: {}".format(sub_content[1][0][0]))
_generate_content(sub_content[1], path_to_folder)
generate_content(sub_content[1], path_to_folder)
elif type(c[1]) == list:
path_to_folder = path + '/' + c[0]

Expand Down Expand Up @@ -1726,53 +1726,3 @@ def _debug(message):

with open(path_to_file, 'w') as f:
f.write(c[1])


if __name__ == "__main__":
arg_parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
arg_parser.add_argument("--src", type=str, required=True, help="Input interface .h file.")
arg_parser.add_argument("--module_name", type=str, required=True, help="Name of the C++ class being wrapped.")
arg_parser.add_argument("--out", type=str, required=True, help="Name of the output folder.")
arg_parser.add_argument("--top_module_namespaces",
type=str,
default="",
help="C++ namespace for the top module, e.g. `ns1::ns2::ns3`. "
"Only the content within this namespace and its sub-namespaces "
"will be wrapped. The content of this namespace will be available at "
"the top module level, and its sub-namespaces' in the submodules.\n"
"For example, `import <module_name>` gives you access to a Python "
"`<module_name>.Class` of the corresponding C++ `ns1::ns2::ns3::Class`"
", and `from <module_name> import ns4` gives you access to a Python "
"`ns4.Class` of the C++ `ns1::ns2::ns3::ns4::Class`. ")
arg_parser.add_argument("--ignore",
nargs='*',
type=str,
help="A space-separated list of classes to ignore. "
"Class names must include their full namespaces.")
args = arg_parser.parse_args()

top_module_namespaces = args.top_module_namespaces.split("::")
if top_module_namespaces[0]:
top_module_namespaces = [''] + top_module_namespaces

with open(args.src, 'r') as f:
content = f.read()

if not os.path.exists(args.src):
os.mkdir(args.src)

module = parser.Module.parseString(content)

instantiator.instantiate_namespace_inplace(module)

import sys

print("Ignoring classes: {}".format(args.ignore), file=sys.stderr)
wrapper = MatlabWrapper(module=module,
module_name=args.module_name,
top_module_namespace=top_module_namespaces,
ignore_classes=args.ignore)

cc_content = wrapper.wrap()

_generate_content(cc_content, args.out)
Loading

0 comments on commit 7cc7232

Please sign in to comment.