# Debian Packaging for quadriga-lib

## Quick Start

```bash
# 1. Copy debian/ and Dockerfile into your repo root (next to CMakeLists.txt)
cp -r debian/ /path/to/quadriga-lib/
cp Dockerfile.ubuntu2404 /path/to/quadriga-lib/

# 2. Build with Docker
cd /path/to/quadriga-lib
docker build -f Dockerfile.ubuntu2404 -t quadriga-deb-noble .
docker run --rm -v $(pwd)/out:/out quadriga-deb-noble

# 3. Inspect + install
dpkg-deb -I out/quadriga-lib_0.10.9-1_amd64.deb   # show metadata + deps
sudo dpkg -i out/quadriga-lib_0.10.9-1_amd64.deb
sudo apt-get -f install                             # pull in any missing deps
```

## File Layout

```
your-repo/
├── CMakeLists.txt
├── include/quadriga_lib.hpp          ← single source of truth for version
├── Dockerfile.ubuntu2404
├── debian/
│   ├── changelog                     ← generated by stamp-changelog.sh
│   ├── stamp-changelog.sh           ← extracts version from .hpp header
│   ├── control                       ← build-deps, runtime-deps, description
│   ├── copyright                     ← Apache-2.0 (DEP-5 format)
│   ├── rules                         ← build recipe (wraps cmake + doc build)
│   ├── source/
│   │   └── format                    ← 3.0 (native)
│   └── quadriga-lib.lintian-overrides
└── gitlab-ci-deb.yml                 ← CI snippet (merge into .gitlab-ci.yml)
```

## Version Propagation

The version is defined in exactly one place:

```cpp
// include/quadriga_lib.hpp
#define QUADRIGA_LIB_VERSION v0_10_9
```

Before `dpkg-buildpackage` runs, `debian/stamp-changelog.sh` parses this
header, converts `v0_10_9` → `0.10.9`, and rewrites `debian/changelog`:

```
quadriga-lib (0.10.9-1) noble; urgency=medium
  ...
```

This happens automatically in both the Dockerfile and the GitLab CI jobs.
You never need to manually edit `debian/changelog` — just bump the version
in `quadriga_lib.hpp` and the packaging follows.

The script also auto-detects the Ubuntu codename (`noble`, etc.) via
`lsb_release -cs`, or you can force one: `debian/stamp-changelog.sh plucky`

## Installed Package Contents

```
/usr/include/                                        # C++ public headers
/usr/lib/x86_64-linux-gnu/libquadriga.a              # static library
/usr/lib/python3/dist-packages/quadriga_lib.*.so     # Python module
/usr/share/quadriga-lib/+quadriga_lib/*.mexa64       # MATLAB MEX files
/usr/share/quadriga-lib/+quadriga_lib/*.mex          # Octave MEX files
/usr/share/quadriga-lib/+quadriga_lib/*.m            # MEX documentation
/usr/share/doc/quadriga-lib/html/                    # HTML API documentation
```

Users add the MEX path to MATLAB/Octave:
```matlab
addpath('/usr/share/quadriga-lib')
```

## Documentation Build

The `debian/rules` `override_dh_auto_build` target mirrors the GNUmakefile
`documentation` target. After compiling the library and the `version`
binary (`ENABLE_BIN=ON`), it runs:

1. `tools/extract_version.py` — stamps the version into HTML part files
2. `tools/extract_html.py` — assembles final HTML pages from source
   comments and part templates

The generated pages (index, mex_api, cpp_api, python_api, formats, faq,
download) are installed to `/usr/share/doc/quadriga-lib/html/`.

The `version` binary itself is **not** installed — it is removed in
`override_dh_auto_install`.

## How Runtime Dependency Detection Works

The `debian/rules` file uses the standard debhelper toolchain, which
includes **`dh_shlibdeps`**. Here's the chain:

```
ELF binaries in package
        │
        ▼
   dh_shlibdeps          ← called automatically by "dh" sequencer
        │
        │  scans each ELF binary (Python .so, Octave .mex)
        │  with dpkg-shlibdeps, which:
        │
        │  1. Runs objdump/readelf to find NEEDED entries
        │     e.g.: NEEDED libhdf5_serial.so.103, NEEDED libgomp.so.1
        │
        │  2. Looks up each .so in /var/lib/dpkg/info/*.shlibs
        │     to find which package provides it
        │     e.g.: libhdf5_serial.so.103 → libhdf5-103-1
        │           libgomp.so.1          → libgomp1
        │
        │  3. Writes to debian/quadriga-lib.substvars:
        │     shlibs:Depends=libhdf5-103-1, libgomp1, libstdc++6, libc6, ...
        │
        ▼
   dh_gencontrol
        │  Substitutes ${shlibs:Depends} in debian/control
        ▼
   Final .deb DEBIAN/control:
     Depends: libhdf5-103-1, libgomp1, libstdc++6 (>= 12), libc6 (>= 2.38), ...
```

### What Gets Scanned

| Artifact       | Scanned?     | Notes                                           |
| -------------- | ------------ | ----------------------------------------------- |
| Python .so     | Yes          | links libhdf5, libgomp, libstdc++, libpython3   |
| Octave .mex    | Yes          | similar shared-lib deps                         |
| Static .a      | No           | no NEEDED entries in archives                   |
| MATLAB .mexa64 | **Excluded** | compiled with `--unresolved-symbols=ignore-all` |

The MATLAB exclusion is handled by `dh_shlibdeps -Xmexa64` in
`debian/rules`. The .mexa64 runtime deps (libgomp, libstdc++, libhdf5)
are already pulled in by the Python and Octave binaries.

### Manual Verification

```bash
# After building, inspect detected deps:
dpkg-deb -I quadriga-lib_*.deb | grep Depends

# Check what a specific binary needs:
objdump -p /usr/lib/python3/dist-packages/quadriga_lib.*.so | grep NEEDED

# Manual shlibdeps on a single file:
dpkg-shlibdeps -O /usr/share/quadriga-lib/+quadriga_lib/some_file.mex
```

## Adapting for New Ubuntu Releases

1. `debian/stamp-changelog.sh` auto-detects the codename — no manual edits
2. Check if `-dev` package names changed (e.g. libhdf5 SO version bump)
3. Build in the target release's Docker image — `dh_shlibdeps` detects
   the correct versioned deps automatically

The GitLab CI snippet already has both 24.04 and 26.04 jobs templated.

## Version Bumps

Just edit `include/quadriga_lib.hpp`:
```cpp
#define QUADRIGA_LIB_VERSION v0_11_0
```

Everything else propagates automatically. No need to touch `debian/changelog`.
