Skip to content

Commit

Permalink
Squashed 'wrap/' changes from bf9646235..a9ad4f504
Browse files Browse the repository at this point in the history
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

git-subtree-dir: wrap
git-subtree-split: a9ad4f504703d182ed82ce88be75655a7666f724
  • Loading branch information
varunagrawal committed Jan 2, 2021
1 parent 4bdc723 commit 3315726
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 173 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__pycache__/
.vscode/
*build*
*dist*
*.egg-info
cmake/PybindWrap.cmake
22 changes: 12 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ project(GTwrap VERSION 1.0)
# ##############################################################################
# General configuration

# Sets the path to the interface parser.
set(GTWRAP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})

set(WRAP_PYTHON_VERSION
"Default"
CACHE STRING "The Python version to use for wrapping")
Expand All @@ -24,13 +21,19 @@ else()
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake")
endif()

configure_file(cmake/PybindWrap.cmake.in
${CMAKE_CURRENT_SOURCE_DIR}/cmake/PybindWrap.cmake @ONLY)
# Install scripts
install(FILES cmake/PybindWrap.cmake cmake/gtwrapConfig.cmake
# 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(
Expand All @@ -46,7 +49,6 @@ else()
set(_pip_args "--user")
endif()

# We install in development mode (-e flag) so any updates to the package are
# automatically propagated.
execute_process(COMMAND ${Python_EXECUTABLE} -m pip install -e . ${_pip_args}
# Finally install the gtwrap python package.
execute_process(COMMAND ${Python_EXECUTABLE} setup.py install ${_pip_args}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
35 changes: 4 additions & 31 deletions cmake/GtwrapUtils.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Utilities to help with wrapping.

function(get_python_version)
macro(get_python_version)
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
# Use older version of cmake's find_python
find_package(PythonInterp)
Expand Down Expand Up @@ -37,23 +37,13 @@ function(get_python_version)
"Cannot find Python interpreter. Please install Python>=3.6.")
endif()

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)

endif()
endfunction()
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.
function(gtwrap_get_python_version WRAP_PYTHON_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)
Expand Down Expand Up @@ -81,21 +71,4 @@ function(gtwrap_get_python_version WRAP_PYTHON_VERSION)
EXACT REQUIRED)
endif()

# Set variables' scope so we can access them from the calling function.
set(WRAP_PYTHON_VERSION
${WRAP_PYTHON_VERSION}
PARENT_SCOPE)
set(Python_FOUND
${Python_FOUND}
PARENT_SCOPE)
set(Python_EXECUTABLE
${Python_EXECUTABLE}
PARENT_SCOPE)
set(Python_INCLUDE_DIRS
${Python_INCLUDE_DIRS}
PARENT_SCOPE)
set(Python_LIBRARY_DIRS
${Python_LIBRARY_DIRS}
PARENT_SCOPE)

endfunction()
endmacro()
10 changes: 4 additions & 6 deletions cmake/PybindWrap.cmake.in → cmake/PybindWrap.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
set(PYBIND11_PYTHON_VERSION ${WRAP_PYTHON_VERSION})

add_subdirectory(@GTWRAP_SOURCE_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 @@ -40,7 +38,7 @@ function(pybind_wrap

add_custom_command(OUTPUT ${generated_cpp}
COMMAND ${PYTHON_EXECUTABLE}
@GTWRAP_SOURCE_DIR@/gtwrap/pybind_wrapper.py
${CMAKE_INSTALL_FULL_BINDIR}/pybind_wrap.py
--src
${interface_header}
--out
Expand All @@ -64,9 +62,9 @@ function(pybind_wrap
# ~~~
add_custom_command(OUTPUT ${generated_cpp}
DEPENDS ${interface_header}
@GTWRAP_SOURCE_DIR@/gtwrap/interface_parser.py
@GTWRAP_SOURCE_DIR@/gtwrap/pybind_wrapper.py
@GTWRAP_SOURCE_DIR@/gtwrap/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
9 changes: 9 additions & 0 deletions cmake/gtwrapConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,14 @@ else()
set(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake")
endif()

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

# Load the pybind11 code from the library installation path
add_subdirectory(${CMAKE_INSTALL_FULL_LIBDIR}/pybind11 pybind11)

# Load all the CMake scripts from the standard location
include(${SCRIPT_INSTALL_DIR}/gtwrap/PybindWrap.cmake)
include(${SCRIPT_INSTALL_DIR}/gtwrap/GtwrapUtils.cmake)
52 changes: 1 addition & 51 deletions gtwrap/matlab_wrapper.py
Original file line number Diff line number Diff line change
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 @@ -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)
74 changes: 0 additions & 74 deletions gtwrap/pybind_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
Code generator for wrapping a C++ module with Pybind11
Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar and Frank Dellaert
"""
import argparse
import re
import textwrap

Expand Down Expand Up @@ -319,76 +318,3 @@ def wrap(self):
wrapped_namespace=wrapped_namespace,
boost_class_export=boost_class_export,
)


def 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 Python module to be generated and "
"used in the Python `import` statement.",
)
arg_parser.add_argument(
"--out",
type=str,
required=True,
help="Name of the output pybind .cc file",
)
arg_parser.add_argument(
"--use-boost",
action="store_true",
help="using boost's shared_ptr instead of std's",
)
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.",
)
arg_parser.add_argument("--template", type=str, help="The module template file")
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()
module = parser.Module.parseString(content)
instantiator.instantiate_namespace_inplace(module)

with open(args.template, "r") as f:
template_content = f.read()
wrapper = PybindWrapper(
module=module,
module_name=args.module_name,
use_boost=args.use_boost,
top_module_namespaces=top_module_namespaces,
ignore_classes=args.ignore,
module_template=template_content,
)

cc_content = wrapper.wrap()
with open(args.out, "w") as f:
f.write(cc_content)


if __name__ == "__main__":
main()
68 changes: 68 additions & 0 deletions scripts/matlab_wrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3

"""
Helper script to wrap C++ to Matlab.
This script is installed via CMake to the user's binary directory
and invoked during the wrapping by CMake.
"""

import argparse
import os

import gtwrap.interface_parser as parser
import gtwrap.template_instantiator as instantiator
from gtwrap.matlab_wrapper import MatlabWrapper, generate_content

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 3315726

Please sign in to comment.