From ee9a70be07561e70657d8c5f733d834f01905698 Mon Sep 17 00:00:00 2001 From: Christopher Dembia Date: Sun, 12 Mar 2017 15:36:12 -0700 Subject: [PATCH 1/2] CMake OPENSIM_PYTHON_STANDALONE. Allow copying all OpenSim, Simbody, and BTK libraries into the python package so that it is standalone. Also make the necessary changes to RPATH to accomodate this new location of shared libraries. --- Bindings/Python/CMakeLists.txt | 26 +++++----- Bindings/Python/setup.py | 4 +- CMakeLists.txt | 46 ++++++++++++++++-- .../simmFileWriterDLL/CMakeLists.txt | 1 + cmake/OpenSimMacros.cmake | 47 ++++++++++++++++++- 5 files changed, 104 insertions(+), 20 deletions(-) diff --git a/Bindings/Python/CMakeLists.txt b/Bindings/Python/CMakeLists.txt index e2d46d5d7a..ae3e6fbac2 100644 --- a/Bindings/Python/CMakeLists.txt +++ b/Bindings/Python/CMakeLists.txt @@ -2,16 +2,6 @@ # Find python. # ============ -# PythonInterp is supposed to come before PythonLibs. -find_package(PythonInterp 2.7 REQUIRED) -find_package(PythonLibs 2.7 REQUIRED) - -# We may need to update the python install dir to include the python version, -# now that we know it. We replace the token "VERSION" with the actual python -# version. -string(REPLACE "VERSION" "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" - OPENSIM_INSTALL_PYTHONDIR "${OPENSIM_INSTALL_PYTHONDIR}") - # Location of the opensim python package in the build directory, for testing. if(MSVC OR XCODE) # Multi-configuration generators like MSVC and XCODE use one build tree for @@ -159,11 +149,21 @@ macro(OpenSimAddPythonModule) endif() if(${OPENSIM_USE_INSTALL_RPATH}) + # TODO @loader_path only makes sense on macOS, so we need to revisit + # for Linux (use $ORIGIN). + # We always set a relative RPATH but only use an absolute RPATH if the + # python package is not standalone, as the libraries are not copied + # into the python package. + # TODO consider using a relative path from the python package directory + # to the OpenSim library directory. + set(run_path_list "\@loader_path/") + if(NOT OPENSIM_PYTHON_STANDALONE) + list(APPEND run_path_list + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + endif() # Use APPEND to include the BTK RPATH, set in CMAKE_INSTALL_RPATH. - # TODO test that NOT using APPEND causes BTK to not be found. set_property(TARGET ${_libname} APPEND PROPERTY - INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" - ) + INSTALL_RPATH "${run_path_list}") endif() # Copy files into the build tree python package. diff --git a/Bindings/Python/setup.py b/Bindings/Python/setup.py index cb1bb17439..431552d5fb 100644 --- a/Bindings/Python/setup.py +++ b/Bindings/Python/setup.py @@ -15,7 +15,9 @@ url='http://opensim.stanford.edu/', license='Apache 2.0', packages=['opensim'], - package_data={'opensim': ['_*.*']}, + # The last 3 entries are for if OPENSIM_PYTHON_STANDALONE is ON. + # The asterisk after the extension is to handle version numbers on Linux. + package_data={'opensim': ['_*.*', '*.dylib', '*.dll', '*.so*']}, include_package_data=True, classifiers=[ 'Intended Audience :: Science/Research', diff --git a/CMakeLists.txt b/CMakeLists.txt index 38d001149c..4619a44102 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,20 @@ for both OpenSim and Simbody. To make a distributable installation on macOS, this should be set to ON to avoid using absolute paths for RPATHs to Simbody libraries." ON) +option(OPENSIM_PYTHON_STANDALONE "Make the Python package standalone, meaning +the OpenSim (and Simbody and BTK) shared libraries it depends on are copied +into the package. If you are building OpenSim on the same machine on which you +plan to use it, you can leave this OFF. If you are distributing OpenSim to +other computers, you should turn this ON. If macOS, this option affects how +RPATH is set for the SWIG-generated shared libraries. This variable has no +effect if BUILD_PYTHON_WRAPPING is OFF." OFF) +# There are many potential ways to distribute the python bindings; here's how +# you might want to set the above option for different ways of distributing: +# simtk.org GUI distribution, macOS: ON (since no env. vars are set) +# simtk.org GUI distrubution, Windows: OFF (PATH is set) +# pypi: ON (there is no way to install the C++ libraries otherwise) +# debian, homebrew, conda: OFF (can have a separate C++ library package) + option(BUILD_API_ONLY "Build/install only headers, libraries, wrapping, tests; not applications (opensim, ik, rra, etc.)." OFF) @@ -431,6 +445,21 @@ if(${BUILD_JAVA_WRAPPING}) endif() endif() +if(${BUILD_PYTHON_WRAPPING}) + # PythonInterp is supposed to come before PythonLibs. + find_package(PythonInterp 2.7 REQUIRED) + find_package(PythonLibs 2.7 REQUIRED) + + # We update the python install dir to include the python version, + # now that we know it. We replace the token "VERSION" with the actual python + # version. + string(REPLACE "VERSION" "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" + OPENSIM_INSTALL_PYTHONDIR "${OPENSIM_INSTALL_PYTHONDIR}") + # This must be done before adding the OpenSim libraries, since + # OPENSIM_INSTALL_PYTHONDIR is used in OpenSimAddLibrary (in + # OpenSimMacros.cmake). +endif() + if(WIN32) add_definitions(-D_CRT_SECURE_NO_DEPRECATE) endif() @@ -526,6 +555,10 @@ if(WITH_BTK) include(${BTK_USE_FILE}) add_definitions(-DWITH_BTK) CopyDependencyDLLsForWin(BTK ${BTK_INSTALL_PREFIX}) + if(BUILD_PYTHON_WRAPPING AND OPENSIM_PYTHON_STANDALONE) + OpenSimInstallDependencyLibraries(BTK "${BTK_INSTALL_PREFIX}" + "${BTK_LIBRARY_DIRS}" "${OPENSIM_INSTALL_PYTHONDIR}/opensim") + endif() # TODO this should be handled separately between macOS and Linux. set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${BTK_LIBRARY_DIRS}") endif() @@ -623,12 +656,11 @@ if(${OPENSIM_COPY_SIMBODY}) # is only needed for programmers goes in sdk/lib. # TODO use SimbodyConfig.cmake variables instead of these globs. if(WIN32) - file(GLOB SIMBODY_BIN_FILES - ${Simbody_BIN_DIR}/*.dll - ${Simbody_BIN_DIR}/*.exe) - file(GLOB SIMBODY_ARCHIVE_FILES - ${Simbody_LIB_DIR}/*.lib) + file(GLOB SIMBODY_BIN_FILES ${Simbody_BIN_DIR}/*.dll + ${Simbody_BIN_DIR}/*.exe) + file(GLOB SIMBODY_ARCHIVE_FILES ${Simbody_LIB_DIR}/*.lib) else() + # TODO simbody-visualizer executable probably isn't even there. file(GLOB SIMBODY_BIN_FILES ${Simbody_BIN_DIR}/simbody*) # executables only (e.g., visualizer). # If the LIB_DIR is some common place for libraries (e.g., /usr/lib/), @@ -689,6 +721,10 @@ if(${OPENSIM_COPY_SIMBODY}) endforeach() endif() endif() +if(BUILD_PYTHON_WRAPPING AND OPENSIM_PYTHON_STANDALONE) + OpenSimInstallDependencyLibraries(SimTK "${Simbody_BIN_DIR}" + "${Simbody_LIB_DIR}" "${OPENSIM_INSTALL_PYTHONDIR}/opensim") +endif() # Other than Windows we can debug without debuggable SimTK libraries diff --git a/OpenSim/Utilities/simmFileWriterDLL/CMakeLists.txt b/OpenSim/Utilities/simmFileWriterDLL/CMakeLists.txt index 4fe254f24d..3d4c5c11d9 100644 --- a/OpenSim/Utilities/simmFileWriterDLL/CMakeLists.txt +++ b/OpenSim/Utilities/simmFileWriterDLL/CMakeLists.txt @@ -5,6 +5,7 @@ file(GLOB INCLUDES *.h) OpenSimAddLibrary( KIT SimmFileWriter AUTHORS "Peter_Loan" + EXCLUDEFROMPYTHON LINKLIBS osimCommon osimSimulation osimActuators ${Simbody_LIBRARIES} INCLUDES ${INCLUDES} SOURCES ${SOURCES} diff --git a/cmake/OpenSimMacros.cmake b/cmake/OpenSimMacros.cmake index 912697799d..5cf5935449 100644 --- a/cmake/OpenSimMacros.cmake +++ b/cmake/OpenSimMacros.cmake @@ -5,6 +5,7 @@ include(CMakeParseArguments) # argument. Otherwise, omit. # LOWERINCLUDEDIRNAME: When installing the headers for this library, make the # name of the library all lower-case (e.g., Lepton -> lepton). +# EXCLUDEFROMPYTHON: Do not install this library into the python package. # KIT: Name of the library (e.g., Common). # AUTHORS: A string listing authors of the library. # LINKLIBS: List of libraries (targets) to link against. @@ -33,7 +34,7 @@ function(OpenSimAddLibrary) # Parse arguments. # ---------------- # http://www.cmake.org/cmake/help/v2.8.9/cmake.html#module:CMakeParseArguments - set(options VENDORLIB LOWERINCLUDEDIRNAME) + set(options VENDORLIB LOWERINCLUDEDIRNAME EXCLUDEFROMPYTHON) set(oneValueArgs KIT AUTHORS) set(multiValueArgs LINKLIBS INCLUDES SOURCES TESTDIRS INCLUDEDIRS) cmake_parse_arguments( @@ -92,6 +93,16 @@ function(OpenSimAddLibrary) RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${OPENSIM_INSTALL_ARCHIVEDIR}") + if(BUILD_PYTHON_WRAPPING AND OPENSIM_PYTHON_STANDALONE + AND NOT OSIMADDLIB_EXCLUDEFROMPYTHON) + if (WIN32) + install(TARGETS ${OSIMADDLIB_LIBRARY_NAME} + RUNTIME DESTINATION "${OPENSIM_INSTALL_PYTHONDIR}/opensim") + else() + install(TARGETS ${OSIMADDLIB_LIBRARY_NAME} + LIBRARY DESTINATION "${OPENSIM_INSTALL_PYTHONDIR}/opensim") + endif() + endif() # Install headers. # ---------------- @@ -145,6 +156,8 @@ function(OpenSimAddLibrary) # list(APPEND run_path_list # "\@loader_path/${lib_dir_to_simbody_lib_dir}") #endif() + # It is possible that on UNIX (APPLE and Linux), we will install + # Simbody and BTK beside OpenSim, rather than in their own directories. # Use APPEND to include the BTK RPATH, set in CMAKE_INSTALL_RPATH. set_property(TARGET ${OSIMADDLIB_LIBRARY_NAME} APPEND PROPERTY INSTALL_RPATH "${run_path_list}" @@ -299,6 +312,38 @@ function(OpenSimAddApplication) endfunction() +# Function to install shared libraries (any platform) from a dependency install +# directory into the OpenSim installation. One use case is to install libraries +# into the python package. +# PREFIX: A common part of the library file names (e.g., 'SimTK' or 'BTK'). +# This is to avoid copying unrelated files from a folder like /usr/lib. +# DEP_LIBS_DIR_WIN: Directory to search for the dependency's library, on +# Windows. +# DEP_LIBS_DIR_UNIX: Directory to search for the dependency's library, on +# UNIX (APPLE and Linux). Specify only the lib directory to avoid +# searching all of /usr/local (if the dependency is installed to a +# system location like this). +# OSIM_DESTINATION: Destination of the libraries within OpenSim's installation. +function(OpenSimInstallDependencyLibraries PREFIX DEP_LIBS_DIR_WIN + DEP_LIBS_DIR_UNIX OSIM_DESTINATION) + if(WIN32) + file(GLOB_RECURSE LIBS "${DEP_LIBS_DIR_WIN}/${PREFIX}*.dll") + else() + if(APPLE) + set(lib_ext "dylib") + else() + set(lib_ext "so*") # Trailing * for version #s. + endif() + file(GLOB_RECURSE LIBS "${DEP_LIBS_DIR_UNIX}/lib${PREFIX}*.${lib_ext}") + endif() + if(NOT LIBS) + message(FATAL_ERROR "Zero shared libraries found in directory " + "${DEP_INSTALL_DIR}.") + endif() + install(FILES ${LIBS} DESTINATION "${OSIM_DESTINATION}") +endfunction() + + # Function to copy DLL files from dependency install directory into OpenSim # build and install directories. This is a Windows specific function enabled # only for Windows platform. Intention is to allow runtime loader to find all From a75edbdd16fb31a225f6f2914ca3f83106ee7bcb Mon Sep 17 00:00:00 2001 From: Christopher Dembia Date: Sun, 12 Mar 2017 18:01:08 -0700 Subject: [PATCH 2/2] Fix type. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4619a44102..8ccdb3d062 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -490,7 +490,7 @@ endif() ## RPATH -# If it is necesasry to not put RPATHS in the installed binaries, set +# If it is necessary to not put RPATHS in the installed binaries, set # set CMAKE_SKIP_INSTALL_RPATH to OFF. if(APPLE) # CMake 2.8.12 introduced the ability to set RPATH for shared libraries