Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support conda packaging #406

Merged
merged 9 commits into from
Oct 25, 2021
73 changes: 33 additions & 40 deletions scenario/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,10 @@ if(NOT CMAKE_CONFIGURATION_TYPES)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${SCENARIO_BUILD_TYPES})
endif()

# This new build mode configures the CMake project to be compatible with the pipeline to
# create the PyPI linux wheel
include(AddNewBuildMode)
add_new_build_mode(NAME "PyPI" TEMPLATE "Release")

# Expose shared or static compilation
set(SCENARIO_BUILD_SHARED_LIBRARY TRUE
CACHE BOOL "Compile libraries as shared libraries")

if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "PyPI")
# Apply the user choice
set(BUILD_SHARED_LIBS ${SCENARIO_BUILD_SHARED_LIBRARY})
else()
# Check that is Linux
if(NOT (UNIX AND NOT APPLE))
message(FATAL_ERROR "PyPI packages can be only created for Linux at the moment")
endif()

if(SCENARIO_BUILD_SHARED_LIBRARY)
message(WARNING "Enabling static compilation, required by the PyPI build mode")
endif()

# Force static compilation
set(BUILD_SHARED_LIBS FALSE)
endif()
set(BUILD_SHARED_LIBS ${SCENARIO_BUILD_SHARED_LIBRARY})

# Use -fPIC even if statically compiled
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
Expand Down Expand Up @@ -94,27 +73,44 @@ if(${CMAKE_VERSION} VERSION_GREATER 3.15)
cmake_policy(SET CMP0094 NEW)
endif()

# Find virtualenv's before system's interpreters
set(Python3_FIND_VIRTUALENV "FIRST" CACHE STRING
"Configure the detection of virtual environments")
set(Python3_FIND_VIRTUALENV_TYPES "FIRST" "ONLY" "STANDARD")
mark_as_advanced(Python3_FIND_VIRTUALENV)
set_property(CACHE Python3_FIND_VIRTUALENV PROPERTY STRINGS ${Python3_FIND_VIRTUALENV_TYPES})
# Enable custom options required to package the CMake project from
# either setup.py or tools like pypa/pip or pypa/build.
set(SCENARIO_CALL_FROM_SETUP_PY FALSE
CACHE BOOL "Configure the project to be compiled from setuptools")

# Find Python3
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
message(STATUS "Using Python: ${Python3_EXECUTABLE}")
find_package(SWIG 4.0 QUIET)
option(SCENARIO_ENABLE_BINDINGS "Enable SWIG bindings" ${SWIG_FOUND})

# Find Python only if requested or if SWIG was automatically found
if(SCENARIO_ENABLE_BINDINGS OR SCENARIO_CALL_FROM_SETUP_PY)
# Find virtualenv's before system's interpreters
set(Python3_FIND_VIRTUALENV "FIRST" CACHE STRING
"Configure the detection of virtual environments")
set(Python3_FIND_VIRTUALENV_TYPES "FIRST" "ONLY" "STANDARD")
mark_as_advanced(Python3_FIND_VIRTUALENV)
set_property(CACHE Python3_FIND_VIRTUALENV PROPERTY STRINGS ${Python3_FIND_VIRTUALENV_TYPES})

# Find Python3
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
message(STATUS "Using Python: ${Python3_EXECUTABLE}")
endif()

# Select the appropriate install prefix used throughout the project.
# Select the appropriate install prefix used throughout the project
set(SCENARIO_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR})
set(SCENARIO_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR})
set(SCENARIO_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})

if(NOT CMAKE_BUILD_TYPE STREQUAL "PyPI")
# Adjust RPATH dependending on the installation type
if(SCENARIO_ENABLE_BINDINGS AND NOT SCENARIO_CALL_FROM_SETUP_PY)
# Convert folder separators to CMake style (could be necessary in Windows)
file(TO_CMAKE_PATH "${Python3_SITELIB}" python3_sitelib_cleaned)

# Add the libraries installed in the Python site-package folder
set(EXTRA_RPATH_DIRS
"${Python3_SITELIB}"
"${Python3_SITELIB}/scenario/bindings")
"${python3_sitelib_cleaned}"
"${python3_sitelib_cleaned}/scenario/bindings")

unset(python3_sitelib_cleaned)
else()
# Add the libraries installed in the Python site-package folder
# (that in this case is CMAKE_INSTALL_PREFIX)
Expand Down Expand Up @@ -186,7 +182,7 @@ if(SCENARIO_USE_IGNITION AND "${IGNITION_DISTRIBUTION}" STREQUAL "")
message(FATAL_ERROR "Failed to find a compatible Ignition Gazebo distribution")
endif()

# Alias the targets
# Alias the Ignition targets so that we can link against different distributions
if(SCENARIO_USE_IGNITION)
include(ImportTargets${IGNITION_DISTRIBUTION})
endif()
Expand All @@ -205,13 +201,10 @@ add_subdirectory(src)
# ========

# Require to find Ignition libraries when packaging for PyPI
if(CMAKE_BUILD_TYPE STREQUAL "PyPI" AND NOT USE_IGNITION)
if(SCENARIO_CALL_FROM_SETUP_PY AND NOT USE_IGNITION)
message(FATAL_ERROR "Found no Ignition distribution for PyPI package")
endif()

find_package(SWIG 4.0 QUIET)
option(SCENARIO_ENABLE_BINDINGS "Enable SWIG bindings" ${SWIG_FOUND})

if(SCENARIO_ENABLE_BINDINGS)
add_subdirectory(bindings)
endif()
Expand Down
5 changes: 3 additions & 2 deletions scenario/bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ set(BINDINGS_INSTALL_PREFIX "${BINDINGS_INSTALL_PREFIX}"
CACHE STRING "Installation prefix of the bindings")

# Final directory of the "scenario" package
if(NOT CMAKE_BUILD_TYPE STREQUAL "PyPI")
if(NOT SCENARIO_CALL_FROM_SETUP_PY)
set(SCENARIO_PACKAGE_INSTALL_DIR "${BINDINGS_INSTALL_PREFIX}/scenario")
else()
# If packaging for PyPI, install ScenarIO in the temp site-package directory
Expand All @@ -73,4 +73,5 @@ file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/scenario/bindings/__init__.py)
# Move main init.py file to package root of the install tree
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py
DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR})
DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}
COMPONENT python)
30 changes: 26 additions & 4 deletions scenario/bindings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def supported_versions_specifier_set() -> packaging.specifiers.SpecifierSet:

class InstallMode(Enum):
User = auto()
CondaBuild = auto()
Developer = auto()


Expand All @@ -36,7 +37,17 @@ def detect_install_mode() -> InstallMode:
import scenario.bindings.core

install_prefix = scenario.bindings.core.get_install_prefix()
return InstallMode.User if install_prefix == "" else InstallMode.Developer

# In conda, there are null bytes terminating the returned string
install_prefix = install_prefix.replace("\x00", "")

if "$PREFIX" in install_prefix:
return InstallMode.CondaBuild

if install_prefix == "":
return InstallMode.User
else:
return InstallMode.Developer


def setup_gazebo_environment() -> None:
Expand All @@ -49,12 +60,23 @@ def setup_gazebo_environment() -> None:
if "IGN_GAZEBO_SYSTEM_PLUGIN_PATH" in os.environ:
ign_gazebo_system_plugin_path = os.environ.get("IGN_GAZEBO_SYSTEM_PLUGIN_PATH")

# Exporting this env variable is done by the conda "libscenario" package
if detect_install_mode() is InstallMode.CondaBuild:
return

# Add the plugins path
if detect_install_mode() == InstallMode.Developer:
install_prefix = Path(scenario.bindings.core.get_install_prefix())
else:
if detect_install_mode() is InstallMode.Developer:
install_prefix = scenario.bindings.core.get_install_prefix()

# In conda, there are null bytes terminating the returned string
install_prefix = Path(install_prefix.replace("\x00", ""))

elif detect_install_mode() is InstallMode.User:
install_prefix = Path(os.path.dirname(__file__))

else:
raise ValueError(detect_install_mode())

plugin_dir = install_prefix / "lib" / "scenario" / "plugins"
ign_gazebo_system_plugin_path += f":{str(plugin_dir)}"

Expand Down
6 changes: 4 additions & 2 deletions scenario/bindings/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set_source_files_properties(${scenario_swig_name}.i PROPERTIES CPLUSPLUS ON)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../scenario/bindings)

swig_add_library(${scenario_swig_name}
TYPE SHARED
TYPE MODULE
LANGUAGE python
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/../scenario/bindings
OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/..
Expand Down Expand Up @@ -41,10 +41,12 @@ get_property(WRAPPER_PY_FILE TARGET ${scenario_swig_name} PROPERTY SWIG_SUPPORT_

install(
TARGETS ${scenario_swig_name}
COMPONENT python
LIBRARY DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings
ARCHIVE DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings
RUNTIME DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings)

install(
FILES ${WRAPPER_PY_FILE}
DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings)
DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings
COMPONENT python)
6 changes: 4 additions & 2 deletions scenario/bindings/gazebo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set_source_files_properties(${scenario_swig_name}.i PROPERTIES CPLUSPLUS ON)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../scenario/bindings)

swig_add_library(${scenario_swig_name}
TYPE SHARED
TYPE MODULE
LANGUAGE python
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/../scenario/bindings
OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/..
Expand Down Expand Up @@ -41,10 +41,12 @@ get_property(WRAPPER_PY_FILE TARGET ${scenario_swig_name} PROPERTY SWIG_SUPPORT_

install(
TARGETS ${scenario_swig_name}
COMPONENT python
LIBRARY DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings
ARCHIVE DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings
RUNTIME DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings)

install(
FILES ${WRAPPER_PY_FILE}
DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings)
DESTINATION ${SCENARIO_PACKAGE_INSTALL_DIR}/bindings
COMPONENT python)
80 changes: 0 additions & 80 deletions scenario/cmake/AddNewBuildMode.cmake

This file was deleted.

9 changes: 8 additions & 1 deletion scenario/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# This software may be modified and distributed under the terms of the
# GNU Lesser General Public License v2.1 or any later version.

import sys

import setuptools
from cmake_build_extension import BuildExtension, CMakeExtension

Expand All @@ -10,7 +12,12 @@
CMakeExtension(
name="ScenarioCMakeProject",
install_prefix="scenario",
cmake_build_type="PyPI",
cmake_build_type="Release",
cmake_configure_options=[
"-DSCENARIO_CALL_FROM_SETUP_PY:BOOL=ON",
"-DSCENARIO_BUILD_SHARED_LIBRARY:BOOL=OFF",
f"-DPython3_EXECUTABLE:PATH={sys.executable}",
],
cmake_depends_on=["idyntree"],
disable_editable=True,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define SCENARIO_CONTROLLERS_REFERENCES_H

#include <array>
#include <cstddef>
#include <vector>

namespace scenario::controllers {
Expand Down
2 changes: 1 addition & 1 deletion scenario/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ set_target_properties(CoreUtils PROPERTIES

# This definition is used by the "scenario" Python package
# to detect User / Developer installation mode
if(NOT CMAKE_BUILD_TYPE STREQUAL "PyPI")
if(NOT SCENARIO_CALL_FROM_SETUP_PY)
target_compile_options(CoreUtils PRIVATE
-DSCENARIO_CMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
endif()
Expand Down