This guide provides comprehensive instructions for building the Device Update agent, including dependency management, platform-specific guidance, and customization options.
-
Dependencies Overview - Complete dependency reference
-
Platform Compatibility - Supported platforms and versions
-
Installation Guide - Automated dependency installation
-
Build Scenarios - Choose the right build for your needs
-
Platform Instructions - Ubuntu, Debian, Yocto guidance
-
Environment Caching - Shared configuration between scripts
-
Troubleshooting - Common issues and solutions
-
Building Process - Actual build steps
-
As a standalone solution - Integration approaches
-
Dependency Strategy - Why we build from source
This section provides a comprehensive overview of all dependencies required to build and run the Device Update agent. Dependencies are organized by category with explanations of their purpose and requirements.
The following system packages are required for building the agent. These are automatically installed by scripts/install-deps.sh.
| Package | Purpose | Version Required |
|---|---|---|
build-essential |
Essential compilation tools (gcc, g++, libc6-dev, make) | Latest available |
cmake |
Cross-platform build system generator | 3.5+ |
ninja-build |
Fast parallel build tool (alternative to make) | Latest available |
make |
Build automation tool | Latest available |
git |
Version control for downloading source dependencies | Latest available |
pkg-config |
Helper tool for compiling applications and libraries | Latest available |
| Package | Purpose | Version Required |
|---|---|---|
gcc / g++ |
GNU Compiler Collection | 6.3+ (7.4+ recommended) |
Alternative: clang |
LLVM C/C++ compiler | 6.0+ |
Platform-specific compiler versions:
- Debian 9: gcc-6, g++-6 (6.3+) (NO LONGER SUPPORTED as of 10/24/2025)
- Ubuntu 18.04+: gcc-8, g++-8 (7.4+ available by default, 8+ installed by script) (NO LONGER SUPPORTED as of 10/24/2025)
- Debian 11: gcc-10, g++-10
- Debian 12: gcc-12, g++-12
- Ubuntu 20.04/22.04: gcc-10, g++-10
- Ubuntu 24.04: gcc-13 (system default)
- Debian 13 (Trixie): gcc-12, g++-12 (installed by script, system has gcc-14)
| Package | Purpose | Required For |
|---|---|---|
libcurl4-openssl-dev |
HTTP/HTTPS client library | Download operations, web requests |
libssl-dev |
OpenSSL development headers | Cryptographic operations, TLS/SSL |
curl |
Command-line download utility | Script operations |
wget |
Web file retrieval utility | Dependency downloads |
| Package | Purpose | Required For |
|---|---|---|
uuid-dev |
UUID generation library | Unique identifier generation |
libxml2-dev |
XML parsing library | Configuration and manifest parsing |
lsb-release |
Linux Standard Base information | OS version detection |
These major components are built from source during the build process:
- Repository: Azure/azure-iot-sdk-c
- Purpose: Connect to IoT Hub and call Azure IoT Plug and Play APIs
- Default Branch:
LTS_03_2025 - Required For: All Azure IoT Hub communication (MQTT, device authentication, telemetry)
- Customization: Use
--azure-iot-sdk-ref <branch/tag>to specify version
- Repository: microsoft/do-client
- Purpose: Robust, efficient download mechanism for update packages
- Default Branch:
develop - Required For: Update package downloads (can be disabled with curl fallback)
- Customization: Use
--do-ref <branch/tag>to specify version - Note: Not available on Ubuntu 24.04 or Debian 13; curl downloader is used instead
- Purpose: Upload files to Azure storage (logs, diagnostics)
- Required For: Diagnostic data upload functionality
- Purpose: Delta update functionality for efficient incremental updates
- Required For: Advanced update scenarios with delta compression
| Package | Purpose | Installation |
|---|---|---|
Catch2 |
C++ unit testing framework | Built from source |
Default Version: v3.8.0 |
Unit test execution | --catch2-ref <version> to customize |
| Package | Purpose | Installation |
|---|---|---|
clang |
C/C++ compiler and analyzer | apt install clang |
clang-tidy |
Clang-based linter | apt install clang-tidy |
cppcheck |
Static analysis tool | apt install cppcheck |
clang-format |
Code formatting | apt install clang-format |
| Tool | Purpose | Installation |
|---|---|---|
cmake-format |
CMake file formatting | pip3 install cmake-format |
shellcheck |
Shell script linting | Auto-installed by script |
doxygen |
Documentation generation | apt install doxygen (for --build-documentation) |
graphviz |
Graph visualization | apt install graphviz (for documentation) |
- Purpose: Support for SWUpdate-based system updates
- Installation:
--install-swupdate - Default Version: Latest from sbabic/swupdate
- Required Libraries:
libconfig-dev(auto-installed)
The following table shows which dependencies are required for different build and deployment scenarios:
| Dependency Category | Minimal Build | Production Build | Development | Testing | Documentation |
|---|---|---|---|---|---|
| Core Build Tools | ✅ | ✅ | ✅ | ✅ | ✅ |
| Compiler (GCC/Clang) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Network Libraries | ✅ | ✅ | ✅ | ✅ | ✅ |
| System Utilities | ✅ | ✅ | ✅ | ✅ | ✅ |
| Azure IoT C SDK | ✅ | ✅ | ✅ | ✅ | ❌ |
| Delivery Optimization | ❌* | ✅ | ✅ | ✅ | ❌ |
| Azure Storage SDK | ❌ | ✅ | ✅ | ❌ | ❌ |
| Catch2 Testing | ❌ | ❌ | ✅ | ✅ | ❌ |
| Static Analysis | ❌ | ❌ | ✅ | ✅ | ❌ |
| SWUpdate | ❌ | ❌** | ✅ | ✅ | ❌ |
| Documentation Tools | ❌ | ❌ | ❌ | ❌ | ✅ |
Legend:
- ✅ Required
- ❌ Not needed
- ❌* Optional (can use curl fallback)
- ❌** Optional (only if using SWUpdate updates)
| Scenario | Command | Purpose |
|---|---|---|
| Quick Start | ./scripts/install-deps.sh -a |
Everything needed for development |
| Minimal Build | ./scripts/install-deps.sh --install-packages-only |
System packages only |
| Production | ./scripts/install-deps.sh --install-aduc-deps --install-do --install-packages |
Core runtime dependencies |
| Development | ./scripts/install-deps.sh -a --keep-source-code |
Full setup with source preservation |
| CI/Testing | ./scripts/install-deps.sh -a --install-githooks |
Complete with testing tools |
| Documentation | sudo apt install doxygen graphviz |
Documentation generation only |
If you plan to use the ADU SDK in external applications, you'll need pkg-config installed first:
sudo apt update
sudo apt install pkgconfig# RHEL 7
sudo yum install pkgconfig
# RHEL 8+ / Fedora
sudo dnf install pkgconfigAdd to your image recipe:
IMAGE_INSTALL_append = " pkgconfig"The Device Update agent has been tested and validated on the following platforms:
| Distribution | Version | Architecture | Compiler | Status | Notes |
|---|---|---|---|---|---|
| Ubuntu | 18.04 LTS | x64, ARM32, ARM64 | GCC 7.4+ (8+ installed) | ✅ Supported | Minimum supported version |
| Ubuntu | 20.04 LTS | x64, ARM32, ARM64 | GCC 9.4+ | ✅ Supported | Recommended |
| Ubuntu | 22.04 LTS | x64, ARM32, ARM64 | GCC 11+ | ✅ Supported | Latest tested |
| Ubuntu | 24.04 LTS | x64, ARM32, ARM64 | GCC 13 | ✅ Supported | curl downloader only (DO not available) |
| Debian | 9 (Stretch) | x64, ARM32, ARM64 | GCC 6.3+ | ✅ Supported | Legacy support |
| Debian | 10 (Buster) | x64, ARM32, ARM64 | GCC 8.3+ | ✅ Supported | Stable |
| Debian | 11 (Bullseye) | x64, ARM32, ARM64 | GCC 10.2+ | ✅ Supported | Recommended |
| Debian | 12 (Bookworm) | x64, ARM32, ARM64 | GCC 12+ | ✅ Supported | Latest tested |
| Debian | 13 (Trixie) | x64, ARM32, ARM64 | GCC 12 (installed) | ✅ Supported | curl downloader only (DO not available) |
| RHEL | 8+ | x64, ARM64 | GCC 8+ | 🟡 Community | Manual setup required |
| Fedora | 33+ | x64, ARM64 | GCC 10+ | 🟡 Community | Manual setup required |
Status Legend:
- ✅ Supported: Fully tested with automated installation and official support
- 🟡 Community: Community-supported, may work but requires manual setup
- ❌ Not Supported: Known compatibility issues
- Auto-detection: Script automatically detects version and installs appropriate compiler
- Package Manager: Full apt integration with dependency resolution
- Testing: Primary CI/CD platform with extensive validation
- Support: Official Microsoft support available
- Manual Setup: Requires manual installation of build tools and dependencies
- Package Differences: Some package names differ from Debian-based distributions
- Compiler: May need to install newer GCC versions manually
- Support: Community-supported, not officially supported by Microsoft
# RHEL 8+
sudo dnf groupinstall "Development Tools"
sudo dnf install cmake openssl-devel libcurl-devel libuuid-devel- Build Host: Run dependency installation on build host, not target
- Target Integration: Include required runtime libraries in target image
- Cross-compilation: Ensure proper toolchain configuration
# Example Yocto recipe additions
DEPENDS += "openssl curl util-linux cmake-native"
IMAGE_INSTALL_append = " \
openssl \
curl \
util-linux-libuuid \
pkgconfig \
"- Memory: Minimum 256MB RAM recommended (128MB minimum)
- Compiler: Use GCC 6.3+ for compatibility, GCC 8+ recommended for optimal performance
- Testing: Extensively tested on Raspberry Pi 3/4
- Performance: Recommended for production ARM deployments
- Compatibility: Full feature parity with x64 builds
- Testing: Validated on various ARM64 SBCs and cloud instances
- Standard: Primary development and testing platform
- Performance: Best performance for development and high-throughput scenarios
The Device Update agent provides a comprehensive dependency installation script that handles all the complexity of installing dependencies across different platforms and scenarios.
For most users, this single command installs everything needed:
./scripts/install-deps.sh -aThis is equivalent to running:
./scripts/install-deps.sh --install-aduc-deps --install-do --install-packages --install-cmake --install-shellcheckThe scripts/install-deps.sh script provides extensive customization options for different build scenarios.
Environment Caching: The script automatically loads previous settings from .adu-dev/build.env if available, and caches current settings after successful installation. Command-line options always override cached defaults.
| Option | Purpose | Includes |
|---|---|---|
-a, --install-all-deps |
Complete installation (recommended) | All categories below |
--install-aduc-deps |
Agent core dependencies | Azure IoT SDK, Catch2, system packages |
--install-do |
Delivery Optimization | DO SDK from source |
-p, --install-packages |
System packages only | apt packages listed above |
--install-packages-only |
System packages without source builds | Package dependencies only |
| Option | Purpose | Default Version | Customization |
|---|---|---|---|
--install-azure-iot-sdk |
Azure IoT C SDK | LTS_03_2025 |
--azure-iot-sdk-ref <branch> |
--install-do |
Delivery Optimization SDK | develop |
--do-ref <branch/tag> |
--install-azure-storage-sdk |
Azure SDK for C++ | Latest | For blob storage features |
--install-catch2 |
Testing framework | v2.13.9 |
--catch2-ref <version> |
--install-cmake |
Build system | Platform-specific | --cmake-version <version> |
--install-shellcheck |
Shell script linting | Latest | Auto-managed |
--install-swupdate |
SWUpdate support | Latest | --swupdate-ref <version> |
--install-githooks |
Repository git hooks | N/A | Development workflow |
| Option | Purpose | Default | Example |
|---|---|---|---|
-f, --work-folder <path> |
Source code location | /tmp |
~/adu-deps |
-k, --keep-source-code |
Preserve source after build | Delete | Keep for debugging |
--use-ssh |
Use SSH for git clones | HTTPS | For authenticated repos |
--cmake-prefix <path> |
CMake install location | /tmp |
/usr/local |
--cmake-force-source |
Force CMake from source | Installer first | Override detection |
--list-deps |
Show dependency status | N/A | Diagnostic tool |
# Install only system packages, use existing dependencies
./scripts/install-deps.sh --install-packages-only# Full installation with source preservation for debugging
./scripts/install-deps.sh -a -f ~/adu-dev-deps --keep-source-code# Use specific Azure IoT SDK branch
./scripts/install-deps.sh --install-aduc-deps --azure-iot-sdk-ref v1.10.0# Install with SWUpdate support for handler testing
./scripts/install-deps.sh -a --install-swupdate# Install with githooks for automated builds
./scripts/install-deps.sh -a --install-githooks# Use SSH for repositories requiring authentication
./scripts/install-deps.sh -a --use-sshThe ADU build system automatically caches environment variables and build configuration between install-deps.sh and build.sh to ensure consistent builds and simplify the development workflow.
When you run install-deps.sh, it:
- Loads previous settings from
.adu-dev/build.envif available (e.g., work folder, SDK versions, compiler paths) - Caches current configuration after successful installation to
.adu-dev/build.env - Shares environment variables with
build.shfor consistent builds
When you run build.sh, it:
- Automatically loads cached environment from
.adu-dev/build.envif available - Uses cached values as defaults (work folder, cmake paths, compiler settings)
- Allows command-line overrides of any cached values
The following variables are automatically shared between scripts:
ADUC_WORK_FOLDER- Source code location for dependenciesADUC_CMAKE_DIR_PATH- CMake installation directoryCMAKE_PREFIX- CMake install prefix
CC,CXX- Primary compiler pathsCATCH2_CC,CATCH2_CXX- Catch2-specific compiler pathsCMAKE_BIN- CMake binary pathCMAKE_VERSION- Installed CMake version
AZURE_IOT_SDK_REF- Azure IoT SDK version/branchCATCH2_REF- Catch2 testing framework versionSWUPDATE_REF- SWUpdate versionDO_REF- Delivery Optimization version
ADUC_OS,ADUC_VERSION- Operating system detailsADUC_IS_AMD64,ADUC_IS_ARM64,ADUC_IS_ARM32- Architecture flags
- Consistency: Ensures
build.shuses the same paths and versions asinstall-deps.sh - Convenience: No need to repeatedly specify custom work folders or tool paths
- Reliability: Reduces configuration drift between dependency installation and building
- Flexibility: Command-line options always override cached values
# Display cached environment variables
cat .adu-dev/build.env# Method 1: Delete the entire cache directory
rm -rf .adu-dev/
# Method 2: Delete just the cache file
rm -f .adu-dev/build.env
# Method 3: Override with fresh installation
./scripts/install-deps.sh -a --work-folder /tmp/fresh-build# install-deps.sh: Command-line options override cached defaults
./scripts/install-deps.sh -a --work-folder ~/custom-deps # Overrides cached work folder
# build.sh: Command-line options override cached defaults
./scripts/build.sh -o ~/custom-output # Overrides cached output directory# If you've moved directories or changed system configuration
rm -f .adu-dev/build.env
./scripts/install-deps.sh -a # Recreates cache with current settings# Both scripts show cache loading messages:
# "Loading cached build environment from .adu-dev/build.env..."
# "Cached environment loaded successfully."
# If cache loading fails, check file permissions
ls -la .adu-dev/build.env- Cache Location:
.adu-dev/build.env(relative to repository root) - Git Ignore: Cache directory is automatically ignored by git
- Portability: Cache is machine-specific and should not be shared between systems
# Standard installation
sudo apt update
./scripts/install-deps.sh -a# Install pkg-config first (see platform compatibility section for details)
sudo dnf install pkgconfig # RHEL 8+/Fedora
./scripts/install-deps.sh -a # May require manual intervention# Install pkg-config in your image recipe
IMAGE_INSTALL_append = " pkgconfig"
# Then run script on target or build host- GitHub 2FA: Use personal access token (PAT) instead of password
- SSH Keys: Use
--use-sshoption for SSH-based authentication - Corporate Proxy: Configure git and curl proxy settings
# Script will prompt for sudo when needed
# Ensure user has sudo privileges for package installation# List current dependency status
./scripts/install-deps.sh --list-deps
# Clean rebuild with custom work folder
./scripts/install-deps.sh -a -f ~/clean-build --work-folder ~/clean-build# Force specific versions
./scripts/install-deps.sh --install-aduc-deps \
--azure-iot-sdk-ref LTS_01_2024 \
--catch2-ref v2.13.9This documentation is designed to stay synchronized with the scripts/install-deps.sh implementation. To ensure accuracy:
When modifying install-deps.sh, please update this documentation:
- Package Lists: Update the System Package Dependencies tables
- Script Options: Update the Script Options and Customization section
- Version Defaults: Update default versions for external dependencies
- Platform Support: Update the Platform Compatibility Matrix
Use these commands to verify documentation accuracy:
# Check current script help text
./scripts/install-deps.sh -h
./scripts/build.sh -h
# List dependency status
./scripts/install-deps.sh --list-deps
# Verify package list (compare with documentation)
grep -n "aduc_packages=" scripts/install-deps.sh
grep -n "static_analysis_packages=" scripts/install-deps.sh
# Test environment caching functionality
./scripts/install-deps.sh --install-packages-only --work-folder /tmp/test
cat .adu-dev/build.env # Verify cache contents
rm -f .adu-dev/build.env # Reset for testingKey locations in install-deps.sh that should match documentation:
| Documentation Section | Script Location | Line(s) |
|---|---|---|
| System Package Dependencies | aduc_packages= |
~91 |
| Static Analysis Tools | static_analysis_packages= |
~92 |
| Script Options | print_help() function |
~102-145 |
| Default Versions | Variable definitions | ~50-90 |
| Environment Caching | cache_build_environment() function |
~1217-1265 |
| Environment Loading | Cache loading at startup | ~53-58 |
- Install the clang-format package (required for running
scripts/clang-format.sh):
sudo apt install clang-format- Install pip3 and cmake-format (required for running
scripts/cmake-format.sh):
sudo apt install python3-pip
sudo --set-home pip3 install cmake-formatThe Device Update for IoT Hub reference agent code utilizes CMake for building. An example build script is provided at scripts/build.sh.
The build script automatically integrates with the environment caching system. If you've run install-deps.sh, the build script will automatically use the cached environment settings.
To build the reference agent with the default parameters:
./scripts/build.sh -cEnvironment Integration: The script automatically loads cached variables from .adu-dev/build.env if available, ensuring consistent compiler paths, work directories, and CMake settings from your dependency installation.
To see additional build options with build.sh:
build.sh -hThe build script provides several environment-aware features:
- Automatic Cache Loading: Loads build environment from
.adu-dev/build.envif available - Cached CMake Path: Uses CMake installed by
install-deps.shautomatically - Compiler Consistency: Uses the same compiler settings as dependency installation
- Work Folder Integration: Aligns with dependency installation work folder settings
# Standard build (uses cached environment if available)
./scripts/build.sh -c
# Build with unit tests (leverages cached Catch2 installation)
./scripts/build.sh -c -u
# Clean build with documentation
./scripts/build.sh -c -d
# Override cached output directory
./scripts/build.sh -c -o ~/custom-outputTo build and run the unit tests:
./scripts/build.sh -c -u
pushd out
ctest # or ninja testFor more test run options:
ctest -hThe current supported valgrind versions are 3.15+ (Ubuntu 20.04), 3.18+ (Ubuntu 22.04), or 3.23.0 from source.
Installing Valgrind:
# Automatic installation (recommended) - uses apt on Ubuntu 20.04+
./scripts/install-deps.sh --install-valgrind auto
# Install from apt package manager
./scripts/install-deps.sh --install-valgrind apt
# Build from source (version 3.23.0)
./scripts/install-deps.sh --install-valgrind source
# Skip installation
./scripts/install-deps.sh --install-valgrind skipRunning Tests:
There is a top-level DartConfiguration.tcl in the source tree that contains valgrind path and arguments.
Running memcheck with following command will result in CTest generating a DartConfiguration.tcl under the out dir and running all the tests using valgrind:
cd out
ctest -T memcheckNo suppression file is currently used, so the goal is for all the unit tests to run valgrind-clean and to fix even the false-positives.
Run specific tests:
cd out
# Run a single test
ctest -R device_properties_ut -T memcheck
# Run tests matching a pattern with verbose output
ctest -R ".*config_utils.*" -T memcheck -V
# Run tests and continue on failure
ctest -T memcheck --output-on-failureView detailed results:
# View the most recent memcheck log
cat out/Testing/Temporary/MemoryChecker.*.log
# List all memcheck logs
ls -lt out/Testing/Temporary/MemoryChecker.*.log
# Search for leaks in logs
grep -i "definitely lost\|indirectly lost" out/Testing/Temporary/MemoryChecker.*.logRun test binaries directly with Valgrind:
# Full leak check with origins
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes \
--verbose --log-file=valgrind-mytest.log \
./src/agent/adu_core_interface/tests/device_properties_ut
# With child process tracking (useful for tests that spawn processes)
valgrind --leak-check=full --trace-children=yes \
--child-silent-after-fork=yes \
./path/to/test_binary
# Generate detailed XML reports
valgrind --leak-check=full --xml=yes --xml-file=valgrind-report.xml \
./path/to/test_binaryCommon Valgrind options:
--leak-check=full- Show detailed information about each leak--show-leak-kinds=all- Show all types of leaks (definite, indirect, possible, reachable)--track-origins=yes- Track origins of uninitialized values--verbose- More detailed output--log-file=<file>- Save output to file--suppressions=<file>- Use suppression file for known false positives--gen-suppressions=all- Generate suppression entries for reported errors
Tips for memory leak testing:
- Always run tests in a clean build to ensure accurate results
- Use
--track-origins=yesto find where uninitialized values come from - Save logs to files for easier analysis:
--log-file=valgrind-%p.log(where %p is process ID) - For CI/CD integration, use
--error-exitcode=1to fail on errors - Create suppression files for external library false positives
To build the debian package (will be output to the out directory):
./scripts/build.sh --build-packagesThe ADU SDK provides a C API for external applications to query the Azure Device Update agent service status. This is particularly useful for IoT devices that need to:
- Determine if the agent is actively processing updates
- Safely power down during idle periods to conserve battery
- Monitor update deployment workflow status
The SDK is built as part of the main build process and produces:
- Library:
libaducsdk.a(static library) - Header:
aducsdk.h(C/C++ header file) - pkg-config:
aducsdk.pc(package configuration for discovery)
To build just the SDK:
./scripts/build.sh -c
# or build only the SDK target
cmake --build out --target aducsdkTo install the SDK for system-wide use:
sudo cmake --build out --target installThis installs:
- Library:
/usr/local/lib/libaducsdk.a - Header:
/usr/local/include/aduc/aducsdk.h - pkg-config:
/usr/local/lib/pkgconfig/aducsdk.pc
Configure the default FIFO path for communication with the agent:
# Custom FIFO path
cmake -DADUC_API_DEFAULT_FIFO_PATH="/custom/path/to/api/apireq.fifo" ..
./scripts/build.sh -c# Check if SDK is installed
pkg-config --exists aducsdk && echo "SDK found!"
# Get compilation flags
gcc myapp.c $(pkg-config --cflags --libs aducsdk) -o myapp
# Check version
pkg-config --modversion aducsdk#include <stdio.h>
#include <aduc/aducsdk.h>
int main() {
printf("Checking ADU Agent status...\n");
ADUC_ServiceStatus status = GetAduServiceStatus();
const char* statusStr = ADUC_ServiceStatusToString(status);
printf("Status: %s (%d)\n", statusStr, status);
// Power management logic for IoT device
if (status == ADUC_ServiceStatus_Idle || status == ADUC_ServiceStatus_Paused) {
printf("Agent is idle/paused - safe to power down to conserve battery\n");
// system("poweroff"); // Uncomment for actual power management
} else if (status >= ADUC_ServiceStatus_ERROR_UnsupportedApiVersion) {
printf("Error communicating with agent: %s\n", statusStr);
return 1;
} else {
printf("Agent is active - staying online\n");
}
return 0;
}In your CMakeLists.txt:
find_package(PkgConfig REQUIRED)
pkg_check_modules(ADUCSDK REQUIRED aducsdk)
target_include_directories(myapp PRIVATE ${ADUCSDK_INCLUDE_DIRS})
target_link_libraries(myapp ${ADUCSDK_LIBRARIES})
target_compile_options(myapp PRIVATE ${ADUCSDK_CFLAGS_OTHER})DESCRIPTION = "IoT Power Management Application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=..."
# Add dependency on the ADU SDK
DEPENDS += "aducsdk"
# Use pkg-config to get compilation flags
inherit pkgconfig
do_compile() {
# pkg-config automatically provides the right flags
${CC} ${CFLAGS} $(pkg-config --cflags aducsdk) -o myapp main.c $(pkg-config --libs aducsdk)
}
do_install() {
install -d ${D}${bindir}
install -m 0755 myapp ${D}${bindir}/
}To customize SDK build parameters in Yocto, add to your recipe or local.conf:
# Set custom FIFO path
EXTRA_OECMAKE_append = " -DADUC_API_DEFAULT_FIFO_PATH='/custom/adu/api/apireq.fifo'"
# Both together
EXTRA_OECMAKE_append = " -DADUC_API_DEFAULT_FIFO_PATH='/opt/adu/api/request.fifo'"Or in your device-specific configuration:
# In your machine configuration (.conf file)
ADUC_API_DEFAULT_FIFO_PATH = "/custom/path/apireq.fifo"Alternatively, you can build using CMake directly. Set the required product values for ADUC_DEVICEINFO_MANUFACTURER and ADUC_DEVICEINFO_MODEL in the top-level CMakeLists.txt before building. Optional CMake values can be found there as well.
mkdir -p build && pushd build
cmake ..
cmake --build .
popd > /dev/nullor using Ninja
mkdir -p build && pushd build
cmake -G Ninja ..
ninja
popd > /dev/nullYou can do incremental builds with Ninja:
pushd out && ninja
popdOne can already configure the agent to use curl to download update payload content (by registering curl content downloader extension, /var/lib/adu/extensions/sources/libcurl_content_downloader.so).
However, to switch to curl for downloading the RootKey Package (infrastructure file for update signature verification), one must currently rebuild the agent from sources by providing the --rootkeypkg-curl parameter to build.sh.
For example:
./scripts/build.sh -c --rootkeypkg-curlNotes:
-
Just as with update payload downloads, using curl for downloads will require
/usr/bin/curlto be available on the device that has the AducIotAgent binary. -
curl will be invoked with the following cmd-line:
/usr/bin/curl -L -C - -o /path/to/output/file
By Default, both mqtt and mqtt/WebSockets will be linked into the AducIotAgent binary agent and driven by the "iotHubProtocol" config property with valid values of "mqtt" or "mqtt/ws", for MQTT and MQTT over WebSockets, respectively.
Here is the default in top-level CMakeLists.txt:
set (
ADUC_IOT_HUB_PROTOCOL
"IotHub_Protocol_from_Config"
CACHE
STRING
"The protocol for Azure IotHub SDK communication. Options are MQTT, MQTT_over_WebSockets, and IotHub_Protocol_from_Config")Sample /etc/adu/du-config.json that selects MQTT value for the iotHubProtocol property:
{
...
"iotHubProtocol": "mqtt"
}Sample /etc/adu/du-config.json that selects MQTT over WebSockets value for the iotHubProtocol property:
{
...
"iotHubProtocol": "mqtt/ws"
}If using only MQTT, then choosing MQTT for ADUC_IOT_HUB_PROTOCOL in the top-level CMakeLists.txt will reduce the size of Type=SizeMinRel AducIotAgent binary by about 60 KB, which is relatively small compared to the overall footprint that is on the order of 2-3 megabytes.
To link in only MQTT transport provider, set this in the top-level CMakeLists.txt:
set (
ADUC_IOT_HUB_PROTOCOL
"MQTT"
CACHE
STRING
"The protocol for Azure IotHub SDK communication. Options are MQTT, MQTT_over_WebSockets, and IotHub_Protocol_from_Config")After enabling WebSockets above using install-deps.sh so that it builds libiothub_client_mqtt_ws_transport.a static library, modify the top-level CMakeLists.txt to use MQTT_over_WebSockets:
set (
ADUC_IOT_HUB_PROTOCOL
"MQTT_over_WebSockets"
CACHE
STRING
"The protocol for Azure IotHub SDK communication. Options are MQTT, MQTT_over_WebSockets, and IotHub_Protocol_from_Config")Doing ./build.sh after setting this to "MQTT_over_WebSockets" will have the MQTT traffic go over a websocket on port 443.
Using "MQTT" will use SecureMQTT over port 8883.
Please note that, by default, both MQTT (libiothub_client_mqtt_transport.a) and MQTT over WebSockets(libiothub_client_mqtt_ws_transport.a) static libraries are built by install-deps.sh via the use_mqtt and use_wsio -D configs.
# Verify mqtt transport static lib exists
$ locate libiothub_client_mqtt_transport.a | \
grep '/usr/local/lib/'
/usr/local/lib/libiothub_client_mqtt_transport.a# Verify mqtt over websockets static lib exists
$ locate libiothub_client_mqtt_ws_transport.a | \
grep '/usr/local/lib/'
/usr/local/lib/libiothub_client_mqtt_ws_transport.asudo cmake --build out --target installor using Ninja
pushd out > /dev/null
sudo ninja install
popd > /dev/nullNote If the Device Update Agent was built as a daemon, the install targets will install and register the Device Update Agent as a daemon.
After building the Debian package using build.sh --build-packages, do:
sudo apt install ./out/{PKG_NAME}.debThis section explains the rationale behind the ADU project's approach to dependency management, particularly for CMake and other build-from-source dependencies.
The ADU project requires CMake 3.23.2 rather than relying on system package managers for several important reasons:
- Project minimum: Most ADU components require CMake 3.5+
- Tool requirements: Some tools require CMake 3.22+ (
tools/download_file) - Azure SDK compatibility: Azure Storage SDK requires CMake 3.13+
- Target version: 3.23.2 ensures compatibility with all components and modern features
The install script uses a smart strategy for CMake installation:
# Supported architectures: Use pre-built installers (fast)
if [[ $is_amd64 == "true" || $is_arm64 == "true" ]]; then
# Download official CMake installer from GitHub releases
# Faster installation, pre-tested binaries
download_cmake_installer_3.23.2
else
# Unsupported architectures: Build from source
# Ensures compatibility with RISC-V, ARM32, etc.
build_cmake_from_source
fiArchitecture Coverage:
- ✅ x86_64 (amd64): Pre-built installer (fastest)
- ✅ aarch64 (ARM64): Pre-built installer (Raspberry Pi 4, AWS Graviton)
- ✅ ARM32, RISC-V, others: Built from source (IoT/embedded targets)
Building CMake from source enables:
- Consistent Toolchain: Same CMake version across all target platforms
- Embedded Device Support: IoT devices with custom architectures
- Container Reproducibility: Identical builds in Docker, CI/CD
- Distro Independence: Works across Ubuntu, Debian, Alpine, Yocto
Problem with system packages:
# Inconsistent versions across distributions
Ubuntu 20.04: cmake 3.16.3 # Too old for some tools
Ubuntu 22.04: cmake 3.22.1 # Close but not identical
Debian 11: cmake 3.18.4 # Different feature setSolution with controlled installation:
# Identical version everywhere
All platforms: cmake 3.23.2 # Guaranteed compatibilityThe ADU project builds several key dependencies from source for similar cross-platform reliability:
- Reason: Specific LTS branch with known stability
- Benefit: Consistent Azure connectivity across all platforms
- Alternative: System packages often have different versions/patches
- Reason: Specific version ensures test compatibility
- Benefit: Identical test behavior in CI and local development
- Alternative: System packages may not have the exact version needed
- Reason: Latest features for Microsoft's DO client
- Benefit: Cutting-edge download optimization
- Alternative: Not available in most system package repositories
- Reason: Specific tag for blob storage features
- Benefit: Known-good version for ADU's storage requirements
- Alternative: System packages significantly behind latest releases
- Primary target: Development machines, cloud VMs
- Strategy: Pre-built binaries when available, source builds for consistency
- Performance: Optimized for rapid development cycles
- Primary target: Raspberry Pi 4+, AWS Graviton, Apple Silicon
- Strategy: Pre-built ARM64 binaries, native compilation
- Performance: Excellent native performance on modern ARM
- Primary target: Raspberry Pi 3, older embedded systems
- Strategy: Cross-compilation or native source builds
- Considerations: Memory constraints, longer build times
- Primary target: Future IoT devices, research platforms
- Strategy: Source builds ensure forward compatibility
- Benefit: ADU ready for next-generation hardware
# Fast setup for development
./scripts/install-deps.sh --install-all-deps
# Uses pre-built binaries where possible# Explicit control for production
./scripts/install-deps.sh --install-all-deps --cmake-force-source
# Ensures exact same build environment as CI# Target-specific build
./scripts/install-deps.sh --install-all-deps \
--work-folder ./target-deps \
--keep-source-code yes
# Preserves source for cross-compilation investigation# Container-optimized
./scripts/install-deps.sh --install-packages-only
# Use pre-installed CMake in container| Approach | Build Time | Disk Usage | Reproducibility | Cross-Platform |
|---|---|---|---|---|
| System packages | Fastest (minutes) | Minimal | Poor | Limited |
| Pre-built binaries | Fast (minutes) | Moderate | Good | Good |
| Source builds | Slow (30+ min) | High | Excellent | Excellent |
| ADU hybrid | Balanced | Reasonable | Excellent | Excellent |
As the ADU project evolves, the dependency strategy may be updated to:
- CMake 3.25+: For improved C++20 support and performance
- Conan integration: For more sophisticated dependency management
- Multi-stage containers: For optimized production deployments
- vcpkg improvements: Leveraging Microsoft's package manager enhancements
This approach ensures ADU remains buildable and reliable across the diverse landscape of IoT devices and development environments.
If you encounter errors related to uint8_t or missing <cstdint> includes when building the Azure Storage SDK, ensure you're using the latest version of install-deps.sh which includes automatic GCC version detection and patch application for GCC 12+. This affects:
- Ubuntu 24.04 (GCC 13)
- Debian 12 (GCC 12)
- Any custom environment using GCC 12 or later
The patch is only applied when the build system detects GCC version 12 or higher.
If you encounter permission errors with files in .workspace/, the build scripts will automatically fix ownership. If issues persist, manually correct ownership:
sudo chown -R $(id -un):$(id -gn) .workspace/If cmake or shellcheck symlinks are broken, re-run the install script:
./scripts/install-deps.sh --install-packagesTo run tests under valgrind memcheck, ensure valgrind 3.19.0 or later is installed:
# Verify valgrind is accessible
which valgrind
# If not found, create symlink to your valgrind installation
sudo ln -s /opt/valgrind.3.19.0/bin/valgrind /usr/bin/valgrindThen run memcheck:
cd out
ctest -T memcheckResults will be in out/Testing/Temporary/MemoryChecker.*.log
Run Device Update Agent by following these instructions
If using MQTT, run the following netstat command to verify that it is connecting to the remote MQTT port of 8883:
$ sudo ./out/bin/AducIotClient -l0 -e > /dev/null 2>&1 &
$ sudo netstat -pantu | grep Adu
tcp 0 0 <LOCAL IP ADDR>:<LOCAL PORT> <REMOTE IP ADDR>:8883 ESTABLISHED <PID>/./out/bin/Aduc
$ fg
<ctrl-c>If using MQTT over WebSockets, run the following netstat command to verify that it is connecting to the remote WebSockets port of 443:
$ sudo ./out/bin/AducIotClient -l0 -e > /dev/null 2>&1 &
$ sudo netstat -pantu | grep Adu
tcp 0 0 <LOCAL IP ADDR>:<LOCAL PORT> <REMOTE IP ADDR>:443 ESTABLISHED <PID>/./out/bin/Aduc
$ fg
<ctrl-c>