Build an independent Source SDK consumer¶
This tutorial verifies that an independent C and C++ project can consume a relocated Nexus Source SDK. It runs on a Linux host with Python 3.11+, CMake 3.31+, Ninja, a C/C++ compiler and Git. It does not flash or execute an MCU.
First obtain the fixed source and recursive dependencies. The following commands run from that checkout, not an arbitrary default branch.
Run the maintained complete consumer¶
Work from the repository root with the pinned recursive dependencies initialized. Keep output outside the source checkout. This exact test calls the production packager and maintained consumer template, moves the prefix into a path containing spaces, configures independent projects, builds both languages and runs the Native executables:
python tests/contracts/test_source_sdk.py SourceSDKTests.test_relocated_c_and_cpp_consumer_build_and_run_without_original_source
Success is a nonempty unittest execution ending in OK, with exit code zero.
The test’s temporary directory is removed automatically. Failure preserves its
actual command diagnostic; do not count a missing compiler as a passed consumer.
The fixture deliberately packages a development snapshot and explicitly opts in;
this validates consumption, not a publishable software candidate.
The complete consumer is maintained in the production verifier rather than copied into this page. These literal inputs are the same templates exercised by the test:
def prepare_consumer(prefix, directory, name, run_checked, *, fixture=False,
arm=False, chip="stm32", expected_revision=None,
version=None, contracts=False):
"""One public consumer template shared by qualification and boundary tests."""
prefix = Path(prefix)
sdk = prefix / "share/nexus/src"
if version is None:
version = load_policy(sdk)["semantic_version"]
source = directory / (name + " source")
build = directory / (name + " build")
source.mkdir()
# A parent Git identity must never become the SDK source identity.
run_checked(["git", "init", "--quiet", str(source)])
(source / "CMakeLists.txt").write_text(
'cmake_minimum_required(VERSION 3.31)\n'
'project(installed_source_consumer LANGUAGES C CXX ASM)\n'
'set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n'
f'find_package(Nexus {version} EXACT CONFIG REQUIRED)\n'
'find_package(Nexus CONFIG REQUIRED)\n'
'add_executable(parent_plain parent.c)\n'
'nexus_add_firmware(installed_c SOURCES main.c)\n'
'nexus_add_firmware(installed_cpp SOURCES main.cpp LIBRARIES Nexus::Log Nexus::BMP280 Nexus::Storage)\n'
'target_compile_options(installed_cpp PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions;-fno-rtti>")\n'
'get_target_property(sdk_root Nexus::Config NEXUS_SOURCE_DIR)\n'
'file(WRITE "${CMAKE_BINARY_DIR}/resolved-sdk.txt" "${sdk_root}")\n')
(source / "parent.c").write_text(
'#ifdef NEXUS_EFFECTIVE_CONFIG\n#error SDK leaked into unrelated parent target\n#endif\n'
'int main(void) { return 0; }\n')
code = ('#include "nexus_bindings.h"\n'
'#include "nexus_factory.h"\n'
'#include "nexus_config.h"\n'
'#ifndef NEXUS_PLATFORM_VERSION\n'
'#error "Missing source interface version"\n'
'#endif\n'
'#if NEXUS_FACTORY_ABI_EPOCH != 1U\n'
'#error "Unsupported factory interface epoch"\n'
'#endif\n'
'#if __has_include("stm32f4xx.h") || __has_include("gd32f4xx.h")\n'
'#error Vendor SDK leaked into product source\n#endif\n'
'#if __has_include("stm32f407_provider.h") || __has_include("gd32f470_provider.h")\n'
'#error Concrete provider leaked into product source\n#endif\n'
'int main(void) {\n'
' if (nx_factory_gpio(NX_GPIO_ID_COUNT) != 0) return 7;\n'
' nx_platform_start_result_t result = nx_platform_start();\n'
' if (result.primary != NX_SUCCESS) return 1;\n'
' return nx_platform_stop() == NX_SUCCESS ? 0 : 2;\n}\n')
(source / "main.c").write_text(code)
(source / "main.cpp").write_text(
'#include "nexus/components/log.h"\n'
'#include "nexus/components/bmp280.h"\n'
'#include "nexus/components/storage.h"\n'
'#include <cstring>\n' + code.replace(
' nx_platform_start_result_t result',
' nx_log_sink_port_t sink;\n std::memset(&sink, 0, sizeof(sink));\n'
' if (nx_log_init(nullptr, sink, NX_LOG_INFO) != NX_ERROR_INVALID) return 3;\n'
' if (nx_bmp280_compensate(nullptr, 0, 0, nullptr) != NX_ERROR_INVALID) return 4;\n'
' nx_platform_start_result_t result'))
arguments = ["cmake", "-S", str(source), "-B", str(build), "-G", "Ninja",
"-DCMAKE_BUILD_TYPE=Release", f"-DNexus_DIR={prefix / 'lib/cmake/Nexus'}",
"-DNEXUS_BUILD_TESTS=OFF", "-DNEXUS_BUILD_WORKLOAD=OFF"]
if fixture:
arguments.append("-DNEXUS_ALLOW_SOURCE_SDK_FIXTURE=ON")
if contracts:
arguments.append("-DNEXUS_BUILD_WORKLOAD=ON")
if expected_revision:
arguments.append("-DNEXUS_EXPECTED_SOURCE_REVISION=" + expected_revision)
arm_assembly = "liangshan-baremetal.toml" if chip == "gd32" else "sky-baremetal.toml"
assembly = sdk / "tools/configure/assemblies" / (arm_assembly if arm else "native.toml")
arguments.append(f"-DNEXUS_ASSEMBLY_FILE={assembly}")
if arm:
arguments.append(f"-DCMAKE_TOOLCHAIN_FILE={sdk / 'cmake/toolchains/arm-gcc.cmake'}")
return source, build, arguments
Prepare a publishable prefix¶
From an initialized, clean, committed checkout, choose a new owned output path:
python cmake/package/package_source_sdk.py --source . --output /tmp/nexus-source-sdk
python cmake/package/package_source_sdk.py --verify /tmp/nexus-source-sdk/share/nexus/src
Move the entire prefix, including its identity and licenses, before consumption.
The Source SDK contract defines
version and interface epoch checks. Missing, extra, modified or symlinked payload
files reject configuration. A package made with --development-fixture remains
nonpublishable and requires NEXUS_ALLOW_SOURCE_SDK_FIXTURE=ON in its consumer.
Never use that opt-in to bypass release qualification.
The formal installed-prefix verifier requires the locked ARM compiler on PATH
in addition to host tools. It builds Native C/C++ and the maintained MCU consumers:
python cmake/package/verify_consumers.py --prefix /tmp/nexus-source-sdk --output /tmp/nexus-source-sdk-verification
Check verification.json, every recorded exit code, Native runtime results, MCU
ELF/BIN/map and resource reports. MCU linking does not execute hardware. The
verifier’s output is owned; an unrelated existing directory rejects replacement.
Remove only outputs you created after retaining required evidence.
Choose a product assembly¶
Products keep assembly.toml, main, workers and private Board data in an external
repository. They consume Nexus::Platform through nexus_add_firmware(); the
helper does not generate an application or start a scheduler. Use the
assembly reference to select reviewed
bindings and the factory explanation to obtain faces.
Public product examples live in
nexus-examples.
FIG-10 shows the current software consumption flow. Runtime and MCU link evidence remain separate from physical qualification.