qt6-migrated-legatus — build and run, step by step
Build guide for the Qt6-migrated legatus tree on the release validation-scope branch. Everything runs inside a prebuilt Docker image; the host only needs Docker.
Scope
What new_docker_build.sh builds and packages:
| Area | Contents |
|---|---|
TetraIF/ |
CoPrtCnv, LogView, PrtCnv (+ unit-test/ — see Step 3) |
Z/src/ |
NGL application/core library stack (NGLLogger, nglcoreapplication, …) |
Lts/Common/ |
LTS common libraries (LtsCoordTrans, LtsLicence, LtsUtils, …) |
Lts/Proxies/TrainingAreaBase |
tabTelegrams library, built as a TetraIF dependency |
Entries at the tree root:
new_docker_build.sh— build driver (image build, compile, ctest, DEB packaging)Dockerfile— recipe of the prebuilt build image (Ubuntu 26.04 + Qt 6.10.2 + deps)rpmbuild/make_rpm_ubuntu26.py— in-tree DEB package producer (self-contained)jenkins-ci-rme/— in-tree copy of the Jenkins CI flow (see its ownREADME.md)build4/— build tree, created by the driver (git-ignored scratch area)
Toolchain inside the image: Ubuntu 26.04, GCC 15.2.0, Qt 6.10.2
(/opt/Qt/6.10.2/gcc_64), C++17, Release build. No license file is
required.
Prerequisites
- Docker available on the host.
- The tree must be reachable inside the container at
/workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus, i.e. mount the pipeline root on/workspace— this is hownew_docker_build.shinvokes Docker, so running the driver as-is satisfies this.
The driver resolves the pipeline root from its own location (override
with the exported MIGRATE_PIPELINE_ROOT or the .envrc in this
directory).
All commands below are run from this directory
(qt6-migrated-legatus/).
Step 1 — Build the prebuilt Docker image (once)
./new_docker_build.sh --build-prebuilt-image --docker-image legatus-qt6:1.0
This builds the image from the in-tree Dockerfile (Ubuntu 26.04 base,
apt deps, Qt 6.10.2 via aqtinstall, KDReports from source). Re-run it
only after changing the Dockerfile or the APT_PACKAGES list in
new_docker_build.sh.
To remove it later:
./new_docker_build.sh --delete-prebuilt-image --docker-image legatus-qt6:1.0
Step 2 — Compile the release scope
./new_docker_build.sh --docker-image legatus-qt6:1.0
What the driver does inside the container:
-
rm -rf build4 && mkdir build4 && cd build4(fresh build tree) -
Configure with CMake:
cmake .. \ -DBUILD_TESTING=ON \ -DRELEASE_BUILD=ON \ -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/opt/Qt/6.10.2/gcc_64:/usr/local(
RELEASE_BUILD=ONselects the release scope; everything else is at its CMake default.) -
make -j8the release-scope targets (TetraIF modules, Z stack, Lts/Common libraries, all test executables — the list isRELEASE_BUILD_TARGETSinnew_docker_build.sh). -
Run ctest (see Step 3).
Output to inspect after the run:
build4/release_build.log— full make output (the build must end with 0 warnings)build4/bin/— executables (apps + test binaries)build4/lib/— shared libraries
To rebuild incrementally without a fresh tree, run make directly in an
interactive container against the existing build4/:
docker run --rm -it -v /path/to/pipeline-root:/workspace legatus-qt6:1.0 \
bash -c 'cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus/build4 \
&& make -j8'
Run an executable directly
To run a built executable interactively — e.g. the GUI apps under
build4/bin/ (LogView, PrtCnv) against your local display — start a
shell inside the container with X11 forwarding:
docker run --rm -it \
-e DISPLAY \
-e LC_ALL=C.UTF-8 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v /path/to/pipeline-root:/workspace \
legatus-qt6:1.0 \
bash -c 'cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus/build4 && exec bash'
Step 3 — Run the tests
3.1 ctest (automatic)
new_docker_build.sh already runs ctest --test-dir build4 --output-on-failure after the compile. Expected baseline (7 tests, all
passed):
| # | Test | Result |
|---|---|---|
| 1 | coprtcnv_unit_test |
Passed |
| 2 | logview_unit_test |
Passed |
| 3 | prtcnv_unit_test |
Passed |
| 4 | NGLToolsQtSmoke |
Passed |
| 5 | test_nglpdfwidget |
Passed |
| 6 | lts_coord_trans_unit_test |
Passed |
| 7 | aes_crypt_test |
Passed |
3.2 Build the unit tests only
docker run --rm -v /path/to/pipeline-root:/workspace legatus-qt6:1.0 \
bash -c 'cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus/build4 \
&& make -j8 coprtcnv_unit_test logview_unit_test prtcnv_unit_test'
3.3 Run a test executable directly
GUI-free tests need the offscreen Qt platform in a headless container:
docker run --rm -it -v /path/to/pipeline-root:/workspace legatus-qt6:1.0 \
bash -c 'export QT_QPA_PLATFORM=offscreen
cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus/build4
./bin/coprtcnv_unit_test # CoPrtCnv: telegrams, fragmentation, helpers
./bin/logview_unit_test # LogView: IntegerCalculator
./bin/prtcnv_unit_test # PrtCnv: PrtCnvMember fragmented-telegram handling
./bin/lts_coord_trans_unit_test
./bin/aes_crypt_test
./bin/test_nglpdfwidget
# or everything registered:
ctest --output-on-failure'
Each QtTest binary accepts a single slot name to run just one test,
e.g. ./bin/coprtcnv_unit_test radioTelegramFragmentationRoundtrip.
3.4 TetraIF unit tests (TetraIF/unit-test/)
| Test | Module covered | Note |
|---|---|---|
coprtcnv_unit_test |
CoPrtCnv — central & radio telegram header fields, payload access, length checks, fragmentation/reassembly roundtrip, id→string helpers |
links CoPrtCnv |
logview_unit_test |
LogView — IntegerCalculator (arithmetic, precedence, error codes, variables) |
compiles LogView/IntegerCalculator.cpp into the test (the class otherwise lives in the LogView executable) |
prtcnv_unit_test |
PrtCnv — PrtCnvMember state + fragmented-telegram add/reassembly |
compiles PrtCnv/PrtCnvMember.cpp into the test because PrtCnvLib is built with hidden visibility (its symbols are not linkable from outside) |
Sources are registered with ctest via TetraIF/unit-test/CMakeLists.txt
and packaged into the ctest DEBs (see Step 4).
Step 4 — Produce the release DEB packages
export WORKSPACE=/path/to/pipeline-root # e.g. ~/release/qt6-migration-pipeline
export PROJECT=guez
export OS_VERSION=26.04
./new_docker_build.sh --docker-image legatus-qt6:1.0 \
--run-jenkins-builder \
--package-output-dir "${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/" \
> /path/to/build.log 2>&1
(export the variables before the command — command-prefix variables are not expanded in the same command's arguments.)
The driver compiles the release scope, runs ctest, then stages the tree
and runs the in-tree rpmbuild/make_rpm_ubuntu26.py, which produces 5
DEBs in ${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/:
| DEB | Contents |
|---|---|
qt6-release-runtime_* |
runtime libraries + the release-scope executables |
qt6-release-sources_* |
release-scope sources |
qt6-release-ctest-runtime_* |
test executables under /usr/lib/ctest/ (incl. the TetraIF unit tests) |
qt6-release-ctest-sources_* |
test sources (files with "test" in the name under the release dirs, incl. TetraIF/unit-test/*.cpp) |
qt6-release-build-pipeline_* |
the in-tree pipeline files (new_docker_build.sh, Dockerfile, CMakeLists.txt, rpmbuild/, jenkins-ci-rme/) under /usr/src/pipeline/ |
Verify a package:
dpkg -c "${WORKSPACE}/rpms/guez/26.04/qt6-release-ctest-runtime_1.0-1_amd64.deb" \
| grep unit_test
dpkg -i "${WORKSPACE}/rpms/guez/26.04/"*.deb # install locally, if wanted
A copy of the DEBs is also placed in the container's
/workspace/packages-out mount (same-file skip guarded by device:inode).
Step 5 — CI / Jenkins flow (optional)
The in-tree jenkins-ci-rme/ directory contains the full Jenkins
pipeline (Jenkinsfile, builder, publisher) adapted to this tree. For a
local dry run of the same flow without Jenkins:
jenkins-ci-rme/release-16-10-2026.sh
See jenkins-ci-rme/README.md for the pipeline details.
Step 6 — Code coverage (lcov)
export WORKSPACE=/path/to/pipeline-root # e.g. ~/release/qt6-migration-pipeline
export PROJECT=guez
export OS_VERSION=26.04
./new_docker_build.sh --docker-image legatus-qt6:1.0 \
--run-coverage \
--coverage-output-dir "${WORKSPACE}/coverage/${PROJECT}/${OS_VERSION}/" \
> /path/to/coverage.log 2>&1
(export the variables before the command — command-prefix variables are not expanded in the same command's arguments.)
The driver builds the release scope with -O0 --coverage, runs the same ctest
suite, then captures the data with lcov: the report covers the release scope
(TetraIF, Z/src, Lts/Common, Lts/Proxies/TrainingAreaBase), and test
sources are filtered out of the report, so the percentages reflect the module
code.
Artifacts in the coverage output directory:
| File | Contents |
|---|---|
html/index.html |
browsable report (line + branch coverage per file) |
coverage.info |
machine-readable lcov tracefile |
coverage_summary.txt |
whole-scope totals |
--run-coverage is mutually exclusive with --run-jenkins-builder (different
instrumentation of the same build tree).
The Jenkins pipeline runs the same step on every build (RunCoverage
parameter, on by default) and archives the report as build artifacts under
coverage/<project>/<os>/; the local dry run
(jenkins-ci-rme/release-16-10-2026.sh) does the same, with
DRYRUN_COVERAGE=OFF to skip coverage there.
Options reference
./new_docker_build.sh --help prints the full option list. The main
ones:
| Option | Default | Purpose |
|---|---|---|
--docker-image IMAGE |
legatus-qt6:1.0 |
image to use/build/delete |
--build-prebuilt-image |
— | build the image from ./Dockerfile and exit |
--delete-prebuilt-image |
— | delete the image and exit |
--run-jenkins-builder |
off | produce the DEB packages (Step 4) |
--package-output-dir DIR |
./packages-out |
where the DEBs are written |
--lts-version / --project / --build-number / --build-tag |
1.0 / guez / 1 / manual-build |
package metadata |
--run-coverage |
off | coverage build + lcov report (Step 6) |
--coverage-output-dir DIR |
./coverage-out |
where the coverage report is written |
Environment overrides: DOCKER_IMAGE, RUN_JENKINS_BUILDER,
PACKAGE_OUTPUT_DIR, RUN_COVERAGE, COVERAGE_OUTPUT_DIR, PROJECT,
LTS_VERSION, GUEZ_MODE, BUILD_NUMBER, BUILD_TAG,
MIGRATE_PIPELINE_ROOT.
Troubleshooting
- "no Qt platform plugin could be initialized" when running a test
binary by hand →
export QT_QPA_PLATFORM=offscreen. - Stale build tree → the driver always recreates
build4/; for manual builds deletebuild4/and rerun Step 2 if CMake behaves oddly. - DEBs not on the host → the output dir must be a path that exists
on the host (it is bind-mounted into the container as
/workspace/packages-out); check thepackage-output-dirargument.