Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions .github/workflows/build_libraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,13 @@ jobs:

- name: Build libraries
working-directory: lib/
run: |
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
run: ./build.ps1

- name: Upload output folder
uses: actions/upload-artifact@v7
with:
name: libespp_windows
path: lib/pc
path: install

build_linux:

Expand Down Expand Up @@ -67,7 +63,7 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: libespp_linux
path: lib/pc
path: install

build_macos:

Expand Down Expand Up @@ -100,4 +96,4 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: libespp_macos
path: lib/pc
path: install
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ dependencies.lock

# weird mac folders...
.DS_Store
lib/pc
# local espp staging install prefix produced by lib/build.sh (find_package tree
# + python package); consumed by pc/build.sh via CMAKE_PREFIX_PATH.
/install/
_build/
__pycache__/
managed_components/
Expand Down
4 changes: 3 additions & 1 deletion components/rtps_embedded/interop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ cd components/rtps_embedded/interop
Requires docker. One container (`ros:jazzy-ros-base` = FastDDS + rmw_fastrtps +
`ros2` CLI) runs everything in a single network namespace, so RTPS multicast works
unconditionally. The repo is bind-mounted and copied to a container-local tree
before building, so your host `lib/pc` artifacts are never touched.
before building (espp is installed into a container-local staging prefix and the
pc tests `find_package` it from there), so your host build artifacts are never
touched.

## Matrix

Expand Down
12 changes: 6 additions & 6 deletions components/rtps_embedded/interop/run_interop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
#
# NOTE: no `set -u` - ROS 2's setup.bash references unset variables.

# Work on a container-local copy: the pc tests link the lib installed into
# lib/pc inside the source tree, which on the bind mount holds the developer's
# host-platform (e.g. macOS) artifacts. Building in-place would either link
# incompatible objects or clobber them with linux ones.
# Work on a container-local copy: the build installs espp into a staging prefix
# (/tmp/espp/install) and the pc tests find_package it from there. Doing this on
# the bind mount would either mix in the developer's host-platform (e.g. macOS)
# artifacts or clobber them with linux ones, so copy to a container-local tree.
echo "===== Copy sources to container-local tree ====="
rsync -a --delete --exclude '.git/' --exclude 'build/' --exclude 'build-*/' --exclude 'managed_components/' --exclude 'docs/' --exclude 'dependencies.lock' /work/ /tmp/espp/
cd /tmp/espp
Expand All @@ -23,9 +23,9 @@ result() { # name exit_code
}

note "Build espp lib + host binaries (linux)"
cmake -S lib -B lib/build -DCMAKE_BUILD_TYPE=Release > /tmp/cmake_lib.log 2>&1 \
cmake -S lib -B lib/build -DCMAKE_BUILD_TYPE=Release -DESPP_INSTALL=ON -DCMAKE_INSTALL_PREFIX=/tmp/espp/install > /tmp/cmake_lib.log 2>&1 \
&& cmake --build lib/build -j"$(nproc)" --target install > /tmp/build_lib.log 2>&1 \
&& cmake -S pc -B pc/build -DCMAKE_BUILD_TYPE=Release > /tmp/cmake.log 2>&1 \
&& cmake -S pc -B pc/build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/tmp/espp/install > /tmp/cmake.log 2>&1 \
&& cmake --build pc/build -j"$(nproc)" --target \
rtps_embedded_pubsub rtps_embedded_golden rtps_facade_pubsub rtps_typed_pubsub \
rtps_facade_frag rtps_facade_backlog rtps_facade_frag_sizes rtps_service_loopback \
Expand Down
156 changes: 125 additions & 31 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,23 +1,83 @@
cmake_minimum_required(VERSION 3.20)
# 3.21+ for a reliable PROJECT_IS_TOP_LEVEL (used to default ESPP_INSTALL to ON
# only for a top-level build, so a FetchContent/CPM consumer's `cmake --install`
# never installs espp into the parent's prefix unless it opts in).
cmake_minimum_required(VERSION 3.21)

# building PC c++ library and python binding
message(STATUS "Building for PC: C++ & Python")
# ---------------------------------------------------------------------------
# Package version, derived from the latest git tag (strip a leading 'v' and keep
# the MAJOR.MINOR.PATCH numeric core). This flows into PROJECT_VERSION and thus
# esppConfigVersion.cmake, so `find_package(espp X.Y.Z)` version checks work.
# Falls back to 0.0.0 when git or a tag is unavailable (e.g. tarball builds) so
# configure still succeeds.
#
# NOTE: the python wheel gets its version independently from setuptools_scm (see
# pyproject.toml [tool.setuptools_scm] / SKBUILD_PROJECT_VERSION); this git-tag
# derivation is only for the CMake / find_package package version.
# ---------------------------------------------------------------------------
set(ESPP_VERSION "0.0.0")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE _espp_git_tag
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE _espp_git_result)
if(_espp_git_result EQUAL 0 AND _espp_git_tag)
string(REGEX REPLACE "^v" "" _espp_git_tag "${_espp_git_tag}")
if(_espp_git_tag MATCHES "^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?")
set(ESPP_VERSION "${CMAKE_MATCH_0}")
endif()
endif()
endif()

# Build the PC (host) C++ static library. Python bindings are opt-in (see the
# ESPP_BUILD_PYTHON option below); a plain `cmake -S lib` is C++-only.
project(espp VERSION ${ESPP_VERSION})
message(STATUS "espp package version: ${PROJECT_VERSION} (from git tag; 0.0.0 = no tag)")

project(espp)
# Build the espp Python bindings (_espp)? Default OFF so a plain
# `cmake -S lib` produces just the C++ static library + its install/export
# (find_package(espp) / espp::espp) with no pybind11 dependency. The standalone
# scripts (build.sh / build.ps1) pass -DESPP_BUILD_PYTHON=ON to also build and
# install the python package (the `espp/` package) alongside the C++ package
# under CMAKE_INSTALL_PREFIX (this is what CI publishes). Wheel builds via
# scikit-build-core take the SKBUILD path below regardless of this option.
option(ESPP_BUILD_PYTHON "Build the espp Python bindings module (_espp)" OFF)

# Install the standard, relocatable CMake package (install(TARGETS ... EXPORT),
# esppConfig.cmake, flattened headers) so a separate project can
# `find_package(espp)` and link `espp::espp`. This is THE install path (there is
# no longer a legacy source-tree lib/pc install). Defaults to ON for a top-level
# build (build.sh passes an explicit CMAKE_INSTALL_PREFIX, so no /usr/local
# surprise) and OFF when espp is a subproject, so a FetchContent/CPM consumer's
# `cmake --install` does not drag espp into the parent's prefix unless asked.
option(ESPP_INSTALL "Install espp as a find_package-able package (standard install/export under CMAKE_INSTALL_PREFIX)" ${PROJECT_IS_TOP_LEVEL})

# Report what this configure will actually build (Python is opt-in).
if(SKBUILD OR ESPP_BUILD_PYTHON)
message(STATUS "Building espp for PC: C++ static library + Python bindings (_espp)")
else()
message(STATUS "Building espp for PC: C++ static library only (set -DESPP_BUILD_PYTHON=ON for Python bindings)")
endif()

# Prefer an installed pybind11 (provided as a build requirement when building
# python wheels via pip / scikit-build-core); fall back to fetching it with CPM
# for standalone CMake builds (e.g. ./build.sh).
find_package(pybind11 CONFIG QUIET)
if(NOT pybind11_FOUND)
include(cmake/CPM.cmake)
CPMAddPackage(
NAME pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
VERSION 3.0.4
)
if(NOT pybind11_ADDED)
message(FATAL_ERROR "pybind11 not found. Please ensure it is available in the specified version.")
# for standalone CMake builds (e.g. ./build.sh). Only needed when the python
# bindings are actually being built.
if(SKBUILD OR ESPP_BUILD_PYTHON)
find_package(pybind11 CONFIG QUIET)
if(NOT pybind11_FOUND)
include(cmake/CPM.cmake)
CPMAddPackage(
NAME pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
VERSION 3.0.4
)
if(NOT pybind11_ADDED)
message(FATAL_ERROR "pybind11 not found. Please ensure it is available in the specified version.")
endif()
endif()
endif()

Expand All @@ -27,18 +87,18 @@ include(espp.cmake)

include_directories(${ESPP_INCLUDE_DIRS})

set(LINK_ARG "--whole-archive")

# settings for Windows / MSVC
if(MSVC)
add_compile_options(/utf-8 /D_USE_MATH_DEFINES /bigobj)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()

# settings for MacOS
if(APPLE)
set(LINK_ARG "-all_load")
endif()
# NOTE: whole-archive linking (so global-ctor / registration code such as the
# Windows timer-period adjustment in espp.hpp is not stripped) is now the
# CONSUMER's responsibility, applied where espp::espp is linked (see
# pc/CMakeLists.txt, which wraps it with $<LINK_LIBRARY:WHOLE_ARCHIVE,...>). It
# is a no-op on the static archive itself (ar ignores link flags), so it does
# not belong here.

if(SKBUILD)
# Building a python wheel via scikit-build-core (pip install). Only build the
Expand All @@ -51,7 +111,8 @@ if(SKBUILD)
RUNTIME DESTINATION espp)
else()
# Standalone build (./build.sh / ./build.ps1): build the C++ static library
# and the python package, and install both into ./pc for local use.
# (and, with -DESPP_BUILD_PYTHON=ON, the python package) and install the
# find_package-able package into CMAKE_INSTALL_PREFIX.
set(TARGET_NAME "espp_pc")

# main library (which can be built for pc, android, and iOS)
Expand All @@ -61,20 +122,53 @@ else()
STATIC
# Provides a relative path to your source file(s).
${ESPP_SOURCES} )
# Namespaced ALIAS so build-tree (FetchContent / CPM add_subdirectory) and
# install-tree (find_package) consumers link the SAME name: espp::espp.
add_library(espp::espp ALIAS ${TARGET_NAME})
# Export the target as `espp` (not `espp_pc`) so find_package consumers link
# `espp::espp` too -- the SAME name as the build-tree alias above. The on-disk
# archive name stays libespp_pc.a (OUTPUT_NAME unchanged) for pc/ and CI.
set_target_properties(${TARGET_NAME} PROPERTIES EXPORT_NAME espp)
set_property(TARGET ${TARGET_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_options(${TARGET_NAME} PRIVATE "${LINK_ARG}")
target_link_libraries(${TARGET_NAME} ${ESPP_EXTERNAL_LIBS})
# PUBLIC so the system link deps (pthread, or ws2_32/winmm/iphlpapi on Windows)
# propagate to consumers of the exported target.
target_link_libraries(${TARGET_NAME} PUBLIC ${ESPP_EXTERNAL_LIBS})
if(WIN32)
target_link_libraries(${TARGET_NAME} winmm)
target_link_libraries(${TARGET_NAME} PUBLIC winmm)
endif()
target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23)
# PUBLIC/INTERFACE so consumers of espp::espp automatically compile as C++23
# (the espp headers require it).
target_compile_features(${TARGET_NAME} PUBLIC cxx_std_23)
# PUBLIC so find_package(espp) consumers compile the rtps headers with the SAME
# limits/fragmentation profile the archive was built with (ABI-critical; see
# espp.cmake). These export into esppTargets.cmake as INTERFACE definitions.
target_compile_definitions(${TARGET_NAME} PUBLIC ${ESPP_RTPS_COMPILE_DEFINITIONS})

# install build output and headers
install(TARGETS ${TARGET_NAME}
ARCHIVE DESTINATION ${PROJECT_SOURCE_DIR}/pc)
# ---------------------------------------------------------------------------
# Usage requirements (modern, target-based). Expose every include dir espp.cmake
# collects as a BUILD_INTERFACE dir (so FetchContent/CPM consumers using the
# build tree get them), and <prefix>/include as the INSTALL_INTERFACE dir.
# ---------------------------------------------------------------------------
include(GNUInstallDirs)
foreach(_dir IN LISTS ESPP_INCLUDE_DIRS)
target_include_directories(${TARGET_NAME} PUBLIC $<BUILD_INTERFACE:${_dir}>)
endforeach()
target_include_directories(${TARGET_NAME}
PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)

espp_install_includes(${PROJECT_SOURCE_DIR}/pc)
# ---------------------------------------------------------------------------
# Proper install + export so `find_package(espp)` works from a separate
# project. Installs into GNUInstallDirs under CMAKE_INSTALL_PREFIX. This is THE
# install path; ESPP_INSTALL defaults ON for a top-level build (build.sh passes
# an explicit prefix) and OFF for a subproject.
# ---------------------------------------------------------------------------
if(ESPP_INSTALL)
espp_install_cmake_package(${TARGET_NAME})
endif()

# Build and install the python package (espp/ with the _espp extension inside)
espp_install_python_module(${PROJECT_SOURCE_DIR}/pc)
# into CMAKE_INSTALL_PREFIX (put <prefix> on PYTHONPATH to `import espp`).
if(ESPP_BUILD_PYTHON)
espp_install_python_module()
endif()
endif()
57 changes: 44 additions & 13 deletions lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,57 @@ PyPI on each release.

## Building for PC (C++ & Python)

To build the library for use on PC (with C++ and Python), simply build with
cmake:
To build the library for use on PC (with C++ and Python), install it into a
staging prefix with cmake:

``` sh
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
-DESPP_INSTALL=ON -DESPP_BUILD_PYTHON=ON \
-DCMAKE_INSTALL_PREFIX=../install
cmake --build build --config Release --target install --parallel 4
```

This is conveniently scripted up for you into [./build.sh](./build.sh) and
[./build.ps1](./build.ps1) scripts you can simply run from your terminal.
[./build.ps1](./build.ps1) scripts you can simply run from your terminal; they
install into `<repo>/install`.

This installs a standard, relocatable CMake package plus the python package
into the prefix:

* `<prefix>/lib/libespp_pc.a` - C++ static library.
* `<prefix>/include/` - all the header files needed to use the library from C++.
* `<prefix>/lib/cmake/espp/` - `esppConfig.cmake` + friends, so another project
can `find_package(espp)` and link the `espp::espp` target (see below).
* `<prefix>/espp/` - the `espp` python package (pure-python files, type stubs,
and the compiled `espp._espp` pybind11 extension) - add `<prefix>` to your
`PYTHONPATH` / `sys.path` to `import espp` from python code.

This will build and install the following files:
The package version is derived from the latest git tag at configure time (a
leading `v` is stripped), and falls back to `0.0.0` for tarball / no-git builds.
The python wheel's version comes separately from `setuptools_scm`.

* `./pc/libespp_pc` - C++ static library for use with other C++ code.
* `./pc/include` - All the header files need for using the library from C++ code.
* `./pc/espp/` - The `espp` python package (pure-python files, type stubs, and
the compiled `espp._espp` pybind11 extension) - add `./pc` to your
`PYTHONPATH` / `sys.path` to `import espp` from python code.
### Using espp from another C++ project (find_package)

Point `CMAKE_PREFIX_PATH` at the install prefix and link the `espp::espp`
target - it carries the include dirs, the C++23 requirement, and the PUBLIC
system libraries, so nothing else is needed:

``` cmake
find_package(espp REQUIRED)
target_link_libraries(my_app PRIVATE espp::espp)
# If your app relies on espp's global-ctor / registration code (e.g. the Windows
# timer-period adjustment), whole-archive it (CMake 3.24+):
# target_link_libraries(my_app PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,espp::espp>")
```

``` sh
cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/install
```

The same `espp::espp` target is also available without installing, via
`FetchContent` / CPM `add_subdirectory` of `lib/` (build-tree consumers get the
component headers directly). The [../pc](../pc) example tests consume the
installed package this way.

## Updating the python bindings

Expand Down
32 changes: 16 additions & 16 deletions lib/build.ps1
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# powershell script to build the project using cmake
# powershell script to build espp and install the find_package-able package
# (C++ static library + headers + esppConfig.cmake) together with the python
# `espp` package into a local staging prefix (<repo>/install). Point ../pc (and
# any external consumer) at it with -DCMAKE_PREFIX_PATH=<repo>/install.

# Create build directory if it doesn't exist
$buildDir = "build"
if (-not (Test-Path -Path $buildDir)) {
New-Item -ItemType Directory -Path $buildDir
}
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = Split-Path -Parent $scriptDir
$prefix = Join-Path $repoRoot "install"
$buildDir = Join-Path $scriptDir "build"

# Change to the build directory
Set-Location -Path $buildDir
# ESPP_BUILD_PYTHON=ON also builds/installs the python package (what CI
# publishes). ESPP_INSTALL=ON installs the find_package package into $prefix.
cmake -S $scriptDir -B $buildDir `
-DCMAKE_BUILD_TYPE=Release `
-DESPP_INSTALL=ON `
-DESPP_BUILD_PYTHON=ON `
-DCMAKE_INSTALL_PREFIX=$prefix

# Run cmake
cmake ..

# Run cmake --build . --config Release --target install
cmake --build . --config Release --target install --parallel 4

# Change back to the original directory
Set-Location -Path ..
cmake --build $buildDir --config Release --target install --parallel 4
Loading
Loading