Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/README.md
T
2026-10-15 12:21:52 +02:00

11 KiB

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 own README.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

  1. Docker available on the host.
  2. 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 how new_docker_build.sh invokes 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:

  1. rm -rf build4 && mkdir build4 && cd build4 (fresh build tree)

  2. 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=ON selects the release scope; everything else is at its CMake default.)

  3. make -j8 the release-scope targets (TetraIF modules, Z stack, Lts/Common libraries, all test executables — the list is RELEASE_BUILD_TARGETS in new_docker_build.sh).

  4. 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 delete build4/ 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 the package-output-dir argument.