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:

Maintained Native/F407/F470 consumer preparation; template code is not a shell command.
def prepare_consumer(prefix, directory, name, run_checked, *, fixture=False,
                     arm=False, chip="stm32", expected_revision=None,
                     version="1.0.0", contracts=False):
    """One public consumer template shared by qualification and boundary tests."""
    prefix = Path(prefix)
    sdk = prefix / "share/nexus/src"
    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.

Source SDK preparation, relocation and independent consumer verification

FIG-10 shows the current software consumption flow. Runtime and MCU link evidence remain separate from physical qualification.