Skip to content
Merged
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
25 changes: 19 additions & 6 deletions .github/workflows/debian-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y build-essential cmake dpkg-dev libcurl4-openssl-dev unzip
run: sudo apt-get update && sudo apt-get install -y build-essential cmake dpkg-dev libcurl4-openssl-dev unzip bubblewrap python3 fonts-dejavu-core
- name: Bootstrap host tools
run: |
cmake -S . -B build-deb -DDMOD_TOOLS_NAME=arch/x86_64 \
Expand All @@ -36,15 +36,28 @@ jobs:
-DDMOD_BUILD_TESTS=OFF -DDMOD_BUILD_TOOLS=OFF -DDMOD_BUILD_TEMPLATES=OFF
cmake --build build-modules --target example_app dmodex --parallel 2
- name: Validate package
run: bash tests/integration/test_debian_package.sh build-deb/dmod_*.deb build-modules/dmf/example_app.dmf
- name: Install and remove package
run: |
sudo apt-get install -y ./build-deb/dmod_*.deb
bash tests/integration/test_debian_package.sh build-deb/dmod_*.deb build-modules/dmf/example_app.dmf
bash tests/integration/test_development_package.sh build-deb/dmod_*.deb build-deb/dmod-dev_*.deb
- name: Install both packages and test the SDK without a checkout
run: |
sudo apt-get install -y ./build-deb/dmod_*.deb ./build-deb/dmod-dev_*.deb
/usr/bin/dmod_loader --help
/usr/bin/dmf-get --help
sudo apt-get remove -y dmod
DMOD_CACHE_DIR="$RUNNER_TEMP/asset-cache" bash tests/integration/prepare_sdk_assets.sh /usr "$RUNNER_TEMP/asset-tools" arch/x86_64
sudo env DMOD_SDK_TEST_ASSETS="$RUNNER_TEMP/asset-tools" bash tests/integration/test_installed_sdk.sh /usr -DDMOD_TOOLS_NAME=arch/x86_64
- name: Cross compile with the installed SDK
if: matrix.examples == 'OFF'
run: |
sudo apt-get install -y --no-install-recommends gcc-arm-none-eabi libnewlib-arm-none-eabi gdb-multiarch
sudo env DMOD_SDK_TEST_ASSETS="$RUNNER_TEMP/asset-tools" bash tests/integration/test_installed_sdk.sh /usr --cross-profile arch/armv7/cortex-m7
sudo bash tests/integration/test_installed_sdk.sh /usr --cross
- name: Remove packages
run: |
sudo apt-get remove -y dmod-dev dmod
test ! -e /usr/bin/dmod_loader
test ! -e /usr/share/cmake/dmod/dmodConfig.cmake
- uses: actions/upload-artifact@v4
with:
name: dmod-deb-examples-${{ matrix.examples }}
path: build-deb/dmod_*.deb
path: build-deb/*.deb
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ if(DMOD_MODULE AND DEFINED DMOD_PACKAGE_NAME AND NOT "${DMOD_PACKAGE_NAME}" STRE
endif()
endif()

if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND DMOD_SYSTEM)
include(scripts/install-sdk.cmake)
endif()

if(DMOD_BUILD_DEB)
include(scripts/debian-package.cmake)
endif()
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,11 @@ tools and `dmod_loader` using `DMOD_BUILD_DEB=ON` and CMake's `package` target.
See [Building and installing the Debian package](docs/debian-package.md) for
prerequisites, clean-build commands and installation instructions.

The same build produces a `dmod-dev` SDK package. With both packages installed,
module projects can use `find_package(dmod CONFIG REQUIRED)` followed by
`dmod_add_library` or `dmod_add_executable`, without fetching the DMOD sources.
See [Building modules with dmod-dev](docs/development-package.md).

#### Installing with CMake

To install the tools using CMake, build the project in SYSTEM mode with tools enabled, then use the install target:
Expand Down
45 changes: 45 additions & 0 deletions cmake/dmodConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@PACKAGE_INIT@

if(DEFINED DMOD_MODE AND NOT DMOD_MODE STREQUAL "DMOD_MODULE")
set(dmod_FOUND FALSE)
set(dmod_NOT_FOUND_MESSAGE "The installed DMOD SDK supports DMOD_MODULE builds only")
return()
endif()
set(DMOD_MODE DMOD_MODULE)
set(DMOD_MODULE ON)
set(DMOD_SYSTEM OFF)
set_and_check(DMOD_DIR "@PACKAGE_DMOD_SDK_INSTALL_DIR@")
set_and_check(DMOD_INC_DIR "@PACKAGE_DMOD_SDK_INCLUDE_DIR@")
set(DMOD_SCRIPTS_DIR "${DMOD_DIR}/scripts")
set(DMOD_SRC_DIR "${DMOD_DIR}/src")
set(DMOD_TOOLS_BIN_DIR "${PACKAGE_PREFIX_DIR}/bin")
set(DMOD_CONFIGS_DIR "${DMOD_DIR}/configs")
# Like the source build, select the compiler/profile before enabling languages.
include("${DMOD_DIR}/cmake/select-profile.cmake")
set(DMOD_MODULE_HEADER_SOURCE_IN_FILE_PATH "${DMOD_SCRIPTS_DIR}/dmod_header.c.in")
set(DMOD_VERSION_IN_FILE_PATH "${DMOD_SCRIPTS_DIR}/version.txt.in")
set(dmod_VERSION_MAJOR @PROJECT_VERSION_MAJOR@)
set(dmod_VERSION_MINOR @PROJECT_VERSION_MINOR@)
set(DMOD_BUILD_DIR "${CMAKE_BINARY_DIR}/dmod-sdk" CACHE PATH "DMOD runtime build directory")
set(DMOD_DMF_DIR "${CMAKE_BINARY_DIR}/dmf" CACHE PATH "DMF output directory")
set(DMOD_DMFC_DIR "${CMAKE_BINARY_DIR}/dmfc" CACHE PATH "Compressed DMF output directory")
set(DMOD_REPO_DIR "${DMOD_DMF_DIR}" CACHE PATH "Module installation directory")
set(DMOD_ARRAY_SEP ":")
set(DMOD_PATH_SEP "/")
set(DMOD_REPO_PATHS "${DMOD_DMF_DIR}:${DMOD_DMFC_DIR}" CACHE STRING "Module search paths")
include("${DMOD_DIR}/dmod-defaults.cmake")
include("${DMOD_SCRIPTS_DIR}/CMakeLists.txt")

# These programs run on the build host, including when the module is compiled
# with a cross toolchain whose program search is rooted in a target sysroot.
find_program(TODMFC todmfc HINTS "${DMOD_TOOLS_BIN_DIR}" NO_CMAKE_FIND_ROOT_PATH REQUIRED)
find_program(TODMD todmd HINTS "${DMOD_TOOLS_BIN_DIR}" NO_CMAKE_FIND_ROOT_PATH REQUIRED)
find_program(TODMM todmm HINTS "${DMOD_TOOLS_BIN_DIR}" NO_CMAKE_FIND_ROOT_PATH REQUIRED)
find_program(MKDMRPKG mkdmrpkg HINTS "${DMOD_TOOLS_BIN_DIR}" NO_CMAKE_FIND_ROOT_PATH REQUIRED)
find_program(DMF_GET dmf-get HINTS "${DMOD_TOOLS_BIN_DIR}" NO_CMAKE_FIND_ROOT_PATH REQUIRED)
find_program(DMOD_LOADER dmod_loader HINTS "${DMOD_TOOLS_BIN_DIR}" NO_CMAKE_FIND_ROOT_PATH REQUIRED)

if(NOT TARGET dmod)
add_subdirectory("${DMOD_DIR}/cmake/runtime" "${DMOD_BUILD_DIR}")
endif()
check_required_components(dmod)
29 changes: 29 additions & 0 deletions cmake/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Build only the module-side runtime, using the consumer's compiler and flags.
if(NOT CMAKE_C_COMPILER_LOADED)
# Mirrors the nested project used by FetchContent_MakeAvailable(dmod).
project(dmod_sdk LANGUAGES C CXX)
endif()
set(DMOD_GENERATE_MAKEFILES OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_compile_definitions(DMOD_MODULE ON=1 OFF=0)
add_compile_options(-ffunction-sections -fdata-sections -fno-builtin -fno-stack-protector)
configure_file("${DMOD_DIR}/dmod-config.h.in" "${DMOD_BUILD_DIR}/dmod-config.h")

add_library(dmod_inc INTERFACE)
target_include_directories(dmod_inc INTERFACE "${DMOD_INC_DIR}" "${DMOD_BUILD_DIR}")
target_compile_definitions(dmod_inc INTERFACE DMOD_MODULE ON=1 OFF=0)
if(DEFINED DMOD_ARCH)
target_compile_definitions(dmod_inc INTERFACE DMOD_ARCH="${DMOD_ARCH}")
endif()
if(DEFINED DMOD_CPU)
target_compile_definitions(dmod_inc INTERFACE DMOD_CPU="${DMOD_CPU}")
endif()
add_subdirectory("${DMOD_SRC_DIR}/common" common)
add_subdirectory("${DMOD_SRC_DIR}/module" module)

add_library(dmod INTERFACE)
target_link_libraries(dmod INTERFACE dmod_inc dmod_common dmod_module)
if(DMOD_USE_FASTLZ)
add_subdirectory("${DMOD_DIR}/lib/third-party/FastLZ" fastlz)
target_link_libraries(dmod INTERFACE dmod_fastlz)
endif()
30 changes: 30 additions & 0 deletions cmake/select-profile.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Use the same profiles as the source/FetchContent build, without modifying them.
set(_dmod_profile "")
if(DEFINED DMOD_TOOLS AND NOT DMOD_TOOLS STREQUAL "")
get_filename_component(_dmod_profile "${DMOD_TOOLS}" ABSOLUTE BASE_DIR "${CMAKE_SOURCE_DIR}")
elseif(DEFINED DMOD_TOOLS_NAME AND NOT DMOD_TOOLS_NAME STREQUAL "")
set(_dmod_profile "${DMOD_CONFIGS_DIR}/${DMOD_TOOLS_NAME}/tools-cfg.cmake")
endif()

if(NOT _dmod_profile STREQUAL "")
if(NOT EXISTS "${_dmod_profile}")
message(FATAL_ERROR "DMOD tools configuration does not exist: ${_dmod_profile}")
endif()
get_property(_dmod_loaded_profile GLOBAL PROPERTY DMOD_SDK_LOADED_PROFILE)
if(_dmod_loaded_profile AND NOT _dmod_loaded_profile STREQUAL _dmod_profile)
message(FATAL_ERROR "All modules in a build must use the same DMOD tools configuration")
endif()
if(NOT _dmod_loaded_profile)
if(CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED)
message(FATAL_ERROR
"DMOD_TOOLS_NAME/DMOD_TOOLS selects the compiler. Move find_package(dmod CONFIG REQUIRED) before project(), as with the source build's FetchContent_MakeAvailable(dmod).")
endif()
if(DEFINED DMOD_SDK_CONFIGURED_PROFILE AND NOT DMOD_SDK_CONFIGURED_PROFILE STREQUAL _dmod_profile)
message(FATAL_ERROR "Changing DMOD tools configuration requires a fresh build directory")
endif()
set(DMOD_SDK_CONFIGURED_PROFILE "${_dmod_profile}" CACHE INTERNAL "SDK tools configuration")
set(DMOD_TOOLS "${_dmod_profile}")
include("${DMOD_TOOLS}")
set_property(GLOBAL PROPERTY DMOD_SDK_LOADED_PROFILE "${_dmod_profile}")
endif()
endif()
7 changes: 6 additions & 1 deletion docs/debian-package.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Debian package

DMOD can build a single native Debian package named `dmod`, containing:
DMOD builds a native Debian package named `dmod`, containing:

- `dmod_loader`
- `dmf-get`, `dmf-man`, `whereisdmf`
Expand All @@ -10,6 +10,11 @@ Executables are installed in `/usr/bin`, with the license and these instructions
in `/usr/share/doc/dmod`. SDK headers, static libraries and example modules are
not included. Ordinary CMake installation still defaults to `/usr/local`.

The same build also produces `dmod-dev_<version>_all.deb`, containing the
target-neutral module SDK. Install it alongside `dmod` to build modules with
`find_package(dmod CONFIG REQUIRED)` without a source checkout. See
[the development package guide](development-package.md).

## Build

Use a native Linux build on Debian or Ubuntu. Install the build dependencies:
Expand Down
109 changes: 109 additions & 0 deletions docs/development-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Building modules with dmod-dev

The `dmod-dev` Debian package supplies headers, CMake functions, linker scripts,
templates and module-side runtime sources. It depends on the matching `dmod`
host-tools package, CMake and the native C/C++ build tools. Install both locally
built packages with:

```sh
sudo apt-get install ./build-deb/dmod_*.deb ./build-deb/dmod-dev_*.deb
```

Both packages are produced by the existing `DMOD_BUILD_DEB=ON` configuration and
`cmake --build build-deb --target package`; see [the package build guide](debian-package.md).
The SDK package has Debian architecture `all`: its runtime is compiled from
source using the consuming project's compiler. No DMOD checkout or network
access is needed to build a self-contained module.

## Minimal project

```cmake
cmake_minimum_required(VERSION 3.18)
find_package(dmod 1.0 CONFIG REQUIRED)
project(my_module LANGUAGES C CXX)

dmod_add_library(my_module "0.1" src/my_module.c)
```

```sh
cmake -S . -B build
cmake --build build --parallel
```

The normal DMOD functions are available, including `dmod_add_executable`,
`dmod_add_test` and `dmod_link_modules`. Outputs default to `build/dmf` (`.dmf`
and `.dmd`) and `build/dmfc` (`.dmfc`). `dmod-config.h`, generated API headers
and compiled runtime objects are created in the consumer's build directory;
the installed SDK remains read-only. Existing module API declaration rules
still apply; see the [CMake function reference](cmake-functions.md).

Set DMOD options before `find_package`, for example `DMOD_USE_FASTLZ`,
`DMOD_DMF_DIR`, `DMOD_DMFC_DIR`, `DMOD_REPO_DIR` and `DMOD_ARCH`. Set per-module
options such as `DMOD_AUTHOR_NAME`, `DMOD_STACK_SIZE`, `DMOD_MAL_IMPLS` and
`DMOD_DMR_PATH` before calling `dmod_add_library` or `dmod_add_executable`.
`dmod_link_modules` uses the installed `dmf-get` to obtain dependency headers;
registry downloads still require network access unless the resources are local
or already cached.

To migrate an existing CMake module, replace the DMOD `FetchContent` block,
`include(.../paths.cmake)` and `dmod_setup_external_module()` with
`find_package(dmod CONFIG REQUIRED)` before `project()`. Retain the module's
sources, options, API declarations and calls to the module-building functions.
The installed SDK supports MODULE mode; building the SYSTEM runtime/firmware
or using the legacy Make integration still requires the source checkout.

## Cross compilation and custom prefixes

All existing `DMOD_TOOLS_NAME` profiles are installed with the SDK. They load
the **same** `configs/<name>/tools-cfg.cmake` as source/FetchContent builds,
selecting the compiler, binutils, architecture, CPU/FPU flags and DMOD feature
settings, and selecting the dependency registry configuration. For example:

```sh
cmake -S . -B build-arm -DDMOD_TOOLS_NAME=arch/armv7/cortex-m7
cmake --build build-arm --parallel
```

Place `find_package` before `project()`, just as the existing flow places
`FetchContent_MakeAvailable(dmod)` before `project()`. CMake must choose the
compiler before enabling languages. Selecting a profile afterwards reports an
error instead of silently building with the host compiler. Repeated
`find_package` calls after initialization are supported. Use a fresh build
directory when changing profiles.

The existing `DMOD_TOOLS` override can select a custom tools configuration.
Profile-specific overrides such as `FPU_FLAGS`, `CROSS_COMPILE` and
`CPUCONFIG_CFLAGS` retain their original profile semantics. Install the chosen
compiler separately; `build-essential` only supplies the native compiler, and
ARM profiles also look for the target debugger or `gdb-multiarch`.

Alternatively, omit `DMOD_TOOLS_NAME`/`DMOD_TOOLS` and select your own compiler
with `CMAKE_TOOLCHAIN_FILE`. In that case `find_package` may also follow
`project()`. Set CPU/FPU flags in the toolchain and `DMOD_ARCH`/`DMOD_CPU` if
needed to match the firmware's module architecture.

When a toolchain searches only a target sysroot for packages, set
`dmod_DIR=/usr/share/cmake/dmod` explicitly to select this target-neutral SDK.
Compression and dependency tools continue to run on the host. Cross-built
modules must be tested on their target, not with the host `dmod_loader`.

For a source installation under a different prefix, use `cmake --install`
with `--prefix`, then pass that prefix through `CMAKE_PREFIX_PATH`. SDK paths
are resolved relative to the installed CMake configuration, so the prefix can
also be relocated without embedding a source-checkout path.

## Assets and existing projects

The SDK includes the current shared `dmod_setup_assets` implementation:
`DMOD_ASSETS_PATHS` converts views with `todmv`, images with `todmvi` and the
appropriate decoder, and fonts with `todmvf`, including font `tracking` and
multi-section INI files. The older `DMOD_FIXTURES_PATHS` name still works with
its existing deprecation warning. `.dmr` packaging receives `DMOD_VIEWS_DIR`.
Asset tools run on the **host**, selected by `DMOD_VIEWS_TOOLS_NAME` (default
`arch/x86_64`), independently of the target's `DMOD_TOOLS_NAME`. The SDK locates
the host `dmf-get` and `dmod_loader` outside cross-compilation sysroots.

Using the SDK is optional. Existing projects using a checkout, FetchContent,
`paths.cmake`/`dmod_setup_external_module`, or Make continue to use their
existing flow without any source changes. Installing the packages does not
redirect or replace those flows.
7 changes: 6 additions & 1 deletion scripts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ function( create_library_makefile targetName )
# the generated Makefile for non-CMake builds.
dmod_set_current_allocator(${targetName})

# Installed SDK sources are read-only and only used by CMake consumers.
if(DEFINED DMOD_GENERATE_MAKEFILES AND NOT DMOD_GENERATE_MAKEFILES)
return()
endif()

# Configure the Makefile
if(DMOD_MODE STREQUAL "DMOD_SYSTEM")
set(DMOD_SYSTEM ON)
Expand Down Expand Up @@ -1406,4 +1411,4 @@ function(dmod_add_tool toolName)
RUNTIME DESTINATION bin
COMPONENT tools
)
endfunction()
endfunction()
22 changes: 15 additions & 7 deletions scripts/debian-package.cmake
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
# Package only the existing tools install component, excluding SDK headers,
# static libraries, examples and any install rules from test dependencies.
# Keep host executables and the target-neutral SDK in separate packages.
set(CPACK_GENERATOR DEB)
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_ALL tools)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
set(CPACK_COMPONENTS_ALL tools development)
set(CPACK_COMPONENTS_GROUPING IGNORE)
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")

set(CPACK_PACKAGE_NAME "dmod")
set(CPACK_PACKAGE_VENDOR "Choco Technologies")
set(CPACK_PACKAGE_CONTACT "Patryk Kubiak <patryk.kubiak90@gmail.com>")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "DMOD host tools and dynamic module loader")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Dynamic Modules (DMOD)")
set(CPACK_PACKAGE_DESCRIPTION
"Tools for creating, compressing, locating and installing DMOD modules, and the dmod_loader host runtime.")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/choco-technologies/dmod")
Expand All @@ -19,9 +18,18 @@ set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
# Resolve libc/libcurl (and any other linked libraries) on the build distro.
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_TOOLS_PACKAGE_NAME "dmod")
set(CPACK_DEBIAN_TOOLS_PACKAGE_SHLIBDEPS ON)
# dmf-get invokes unzip when installing module and resource archives.
set(CPACK_DEBIAN_PACKAGE_DEPENDS "unzip")
set(CPACK_DEBIAN_TOOLS_PACKAGE_DEPENDS "unzip")

set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_NAME "dmod-dev")
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_ARCHITECTURE "all")
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_SHLIBDEPS OFF)
set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_DEPENDS
"dmod (= ${CPACK_PACKAGE_VERSION}), cmake (>= 3.18), build-essential")
set(CPACK_DEBIAN_DEVELOPMENT_DESCRIPTION
"DMOD module development SDK\n Headers, CMake functions, linker scripts and target-neutral runtime sources for building DMOD modules with find_package(dmod CONFIG REQUIRED).")

install(FILES "${DMOD_DIR}/license.md"
DESTINATION share/doc/dmod RENAME copyright COMPONENT tools)
Expand Down
Loading
Loading