Morning fellas! It’s sunny with a blue blue sky outside today!

My last blog was about RPMBuild Failure. This wasn’t the only thing that I was working on in the last week.

I have also been working on completing the Debian Packaging support. My blog on 3rd of March concluded some of my initial work which was buggy.

Writing templates

The first obvious step was to write templates which would serve as the base for packaging different boards.

After carefully reviewing how the existing rpm.spec.in was laid out, I started working on debian template files.

Directory structure

Unlike rpmbuild, which takes a single file as input, dpkg-buildpackage expects files present in a specific directory with an expected structure. Here is the required directory structure:

.
└── debian
    ├── changelog
    ├── compat
    ├── control
    └── rules

There are 4 files required, each serving different purpose.

  • changelog : Records package versions, release notes, and target distributions.
  • compat : Sets the debhelper compatibility version used during packaging.
  • control : Defines package metadata, dependencies, maintainer info, and build requirements.
  • rules : Makefile-like script that controls how the package is built and installed.

The following template files are consumed by waf, which substitutes the required variables based on the selected deployment configuration. File : debian.changelog.in

rtems-@RSB_PKG_NAME@ (@RSB_VERSION@-@RSB_REVISION@) unstable; urgency=medium

  * Automated package build via RTEMS Source Builder.

 -- RTEMS Builder <builder@rtems.org>  @DEB_DATE@

File : debian.compat.in

12

File : debian.control.in

Source: rtems-@RSB_PKG_NAME@
Section: devel
Priority: optional
Maintainer: RTEMS Builder <builder@rtems.org>
Build-Depends: debhelper (>= 12)
Standards-Version: 4.5.0

Package: rtems-@RSB_PKG_NAME@
Architecture: @RSB_HOST_ARCH@
Depends: ${misc:Depends}
Description: RTEMS tools and board support package
 This package provides development tools and libraries for RTEMS.
 It was automatically generated by the RTEMS Source Builder (RSB).

@USER_DEB_CONFIG@

File : debian.rules.in

#!/usr/bin/make -f

SHELL := /bin/bash

FLAGS_TO_CLEAR = CC CXX CFLAGS CXXFLAGS CPPFLAGS LDFLAGS ASFLAGS TRFLAGS
$(foreach flag,$(FLAGS_TO_CLEAR),$(eval export $(flag)=))

export DEB_CFLAGS_MAINT_STRIP = -Werror=format-security
export DEB_CXXFLAGS_MAINT_STRIP = -Werror=format-security

export DEB_BUILD_OPTIONS=nostrip nocompress

%:
	dh $@

override_dh_auto_build:
	cd @RSB_WORK_PATH@ && @RSB_SET_BUILDER@ @RSB_SET_BUILDER_ARGS@

override_dh_auto_install:
	mkdir -p debian/rtems-@RSB_PKG_NAME@/@PREFIX@
	tar jxf @TARFILE@ -C debian/rtems-@RSB_PKG_NAME@

override_dh_strip:
	# Equivalent to %global _enable_debug_package 0

override_dh_compress:
	# Equivalent to %global __os_install_post /usr/lib/rpm/brp-compress %{nil}

override_dh_makeshlibs:
	# Equivalent to %global __os_install_post /usr/lib/rpm/brp-ldconfig %{nil}

override_dh_shlibdeps:
	# Disables automatic shared library dependency resolution.
	# Crucial for cross-compiled embedded binaries so dpkg doesn't crash.

override_dh_auto_clean:
	# Equivalent to %clean
	rm -rf debian/rtems-@RSB_PKG_NAME@

After writing all the template files, adding a target a deb target to waf was the next step.

To bring Debian packaging into the fold, I had to dig into pkg/linux.py and implement the mechanics necessary to generate full, valid Debian source trees on the fly.

Handling the Platform Architecture Mismatch

The first hurdle was mapping platform architecture strings cleanly.

I introduced a static arch_map dictionary right at the top of the file to manage these system translations:

arch_map = {
    'rpmspec': {
        'x86_64': 'x86_64',
        'aarch64': 'aarch64',
    },
    'deb': {
        'x86_64': 'amd64',
        'aarch64': 'arm64',
    }
}

Now, instead of blindly passing platform.machine() downstream, the build targets dynamically resolve their architecture identity based on the active backend. For instance, an x86_64 host compiling a Debian target safely injects amd64 right where it belongs.

Reproducible Builds & Deb Date Parsing

Debian packaging is incredibly strict regarding timestamps—especially when ensuring reproducible build environments. To address this, I updated the configuration phase (deb_configure) to evaluate the environment for a SOURCE_DATE_EPOCH.

If it exists (such as inside automated CI/CD runners), we parse it directly; otherwise, we gracefully fallback to the native system time. We then cleanly format it into a RFC 5322 timestamp matching Debian’s rigid changelog specifications:

if 'SOURCE_DATE_EPOCH' in os.environ:
    now = datetime.datetime.fromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']), datetime.timezone.utc)
else:
    now = datetime.datetime.now(datetime.timezone.utc)
conf.env.DEB_DATE = format_datetime(now)

Choreographing the Task Generators

Unlike RPM, which primarily relies on a single consolidated .spec template file, building a proper Debian template tree requires spitting out multiple foundational files under the debian/ directory: control, rules, changelog, and compat.

Instead of cluttering the script by duplicating arguments across four separate Waf block executions, I consolidated our substitution parameters into a unified, reusable subst_vars dictionary.

Using Python’s dictionary unpacking operator (**), I assigned separate Waf context task generators to render the entire template suite directly into the target build layout:

# Consolidate all common substitution variables
subst_vars = {
    'RSB_BUILDROOT': buildroot.abspath(),
    'RSB_PKG_NAME': bset['name'],
    'RSB_HOST_ARCH': arch_map["deb"][platform.machine()],
    'PREFIX': bld.env.PREFIX,
    'RSB_VERSION': bld.env.RSB_VERSION,
    'RSB_REVISION': _esc_label(bld.env.RSB_REVISION),
    'RSB_RELEASED': rel,
    'TARFILE': bset['tar'],
    'RSB_SET_BUILDER': bset['cmd'],
    'RSB_SET_BUILDER_ARGS': ' '.join(bset['pkg-opts']),
    'RSB_WORK_PATH': bld.path.abspath(),
    'USER_DEB_CONFIG': user_deb_config,
    'DEB_DATE': bld.env.DEB_DATE
}

bld(name='deb_control_' + bset['name'],
    features='subst',
    description='Generate Debian control file',
    target=bld.path.get_bld().find_or_declare(deb_dir + '/control'),
    source='pkg/debian.control.in',
    **subst_vars)

bld(name='deb_rules_' + bset['name'],
    features='subst',
    description='Generate Debian rules file',
    target=bld.path.get_bld().find_or_declare(deb_dir + '/rules'),
    source='pkg/debian.rules.in',
    chmod=0o755, # Ensures the compiled build script remains executable
    **subst_vars)

Notice the chmod=0o755 flag assigned exclusively to the rules generation block. Because the Debian build engine invokes debian/rules as an executable script file, dropping that execution bit explicitly during template compilation saves developers from nasty permission issues later on.

Tying it all back together

Finally, I mapped the initialization hooks so users can trigger this work seamlessly straight from the root terminal context:

def init(ctx):
    pkg.configs.add_wscript_fun(ctx, 'rpmspec', rpmspec)
    pkg.configs.add_wscript_fun(ctx, 'deb', deb)

Now, executing ./waf deb parses the internal configurations, runs our architecture mapping, formats the timestamps, and outputs a complete, ready to build Debian metadata suite.

This generates directories like amd-kria-k26.debian/, with all the required files and directory structure I talked about at the start of the blog, in out/<target>/ directory. Example:

out/amd/
.
├── amd-kria-k26-next.debian
├── amd-kria-k26.debian
├── amd-zcu102-lwip.debian
├── avnet-microzed-next.debian
└── avnet-microzed.debian

Each of these directories contain files I mentioned earlier.

Now generating a debian package was as simple as running dpkg-buildpackage in the target directory.

cd out/amd/amd-kria-k26.debian
dpkg-buildpackage -b -uc -us

I tested the whole pipeline and was able to generate a .deb package successfully.

Next step

With debian packaging up and working, my next step would be developing a common backend for packaging with simplified command line. As number supported packagers grows in future it would be hard for user to remember different commands and workflow for each packager. Introducing a common backend would make things easier and also robust with some checks in place.

It was fun working on this Debian packaging support. I enjoyed it a lot.

See you in the next blog!