ASK research docs 3
This commit is contained in:
747
docs/ask-deepresearch-3.md
Normal file
747
docs/ask-deepresearch-3.md
Normal file
@@ -0,0 +1,747 @@
|
|||||||
|
# Reproducible Docker Build System for a Tarball-Supplied Upstream Project
|
||||||
|
|
||||||
|
## Executive summary
|
||||||
|
|
||||||
|
The right design is to treat the upstream project tarball as an input artifact, not something the build fetches for itself. That means: vendor every required source tarball into the build context, pin the container base image by digest, verify checksums before extraction, remove `.git` metadata after unpacking, set `SOURCE_DATE_EPOCH`, and export build artifacts with `docker buildx build --output type=local` rather than baking them into an opaque image layer. For container construction, the correct pattern is **“copy a vendored packages directory, select the tarball path with `ARG`, extract in `RUN`, build in a throwaway stage, and export only the outputs.”** Docker explicitly documents that multi-stage builds reduce final image size, that build arguments parameterize the Dockerfile but are not appropriate for secrets, that `.dockerignore` should keep the context small, and that `buildx` supports local artifact export and build secrets. Docker also recommends digest pinning for deterministic base-image selection. citeturn12view3turn12view0turn12view1turn16view0turn16view1turn13view0turn16view3
|
||||||
|
|
||||||
|
For Alpine conversion, the key issue is not package-manager syntax; it is the libc boundary. Alpine uses musl, not glibc, and that changes package names, runtime behavior, and sometimes build logic. Alpine’s own documentation is blunt that compiling codebases may be harder on Alpine because of musl, that `build-base` is the standard compiler meta-package, that stable reproducible builds should use stable repositories rather than edge/testing, and that `gcompat` is only a compatibility layer for some glibc-built binaries. musl’s own documentation highlights the load-bearing differences: smaller default thread stacks, limited symbol-versioning support, `dlclose()` as a no-op, and common bugs caused by glibc-specific `#ifdef` logic. citeturn17view3turn17view0turn17view1turn17view2turn17view5turn17view6turn17view7turn17view8
|
||||||
|
|
||||||
|
Inspection of the supplied files changes the recommendation in one important way. The uploaded ASK source tarball is not self-contained: its upstream `Makefile` still clones `fmlib` and `fmc`, and downloads `libnfnetlink` and `libnetfilter_conntrack`. So a **reproducible/offline** ASK build requires vendoring **all** of those dependency tarballs as inputs alongside `ASK.tar.gz`, and overriding the upstream fetch logic to extract vendored tarballs instead of using `git clone` or `wget`. The supplied board build files also show a current kernel build flow that already merges `kernel-extra.config` into an NXP 6.12 kernel tree and checks the resolved `.config`. That is the right shape; one fragment line is wrong for this kernel. The reported error occurs because `CONFIG_NETFILTER_XTABLES_LEGACY=y` is required by the uploaded fragment, but that symbol does **not** exist in the uploaded `lf-6.12.49-2.2.0` kernel tree. In that tree, the relevant legacy iptables symbols are `CONFIG_IP_NF_IPTABLES_LEGACY` and `CONFIG_IP6_NF_IPTABLES_LEGACY`, and both are already present in the supplied fragment. Comparing the supplied fragment against the uploaded kernel tree shows that `CONFIG_NETFILTER_XTABLES_LEGACY` is the only missing symbol. The fix is to drop or version-gate that single line, not to weaken the checker globally.
|
||||||
|
|
||||||
|
## Findings from the supplied files
|
||||||
|
|
||||||
|
The supplied board-specific build files target an entity["company","NXP","semiconductor company"] Layerscape-oriented arm64 build with `NXP_VERSION=lf-6.12.49-2.2.0`, `ARCH=arm64`, `CROSS_COMPILE=aarch64-linux-gnu-`, and `DEVICE_TREE_TARGET=mono-gateway-dk-sdk`. The current root build uses a Debian Trixie-based builder image, downloads many artifacts into `packages/`, builds the kernel in a container, and exports artifacts locally with `--output type=local`. That overall shape is sound; the main weaknesses are network fetches during build and glibc-specific base images.
|
||||||
|
|
||||||
|
The supplied ASK tarball is a C/C++/Make-based project. Its upstream `Makefile` builds kernel modules and userspace components, but its “sources” phase still clones `fmlib` and `fmc` and downloads `libnfnetlink` and `libnetfilter_conntrack`. That means `ASK.tar.gz` alone is **not** enough for a fully reproducible build. You must vendor those dependency tarballs too, or the build will still depend on the network.
|
||||||
|
|
||||||
|
The uploaded ASK tree also contains a `.git/` directory inside the tarball. That is unusual for release tarballs and bad for reproducibility unless you intentionally need VCS-derived version stamping. In this tree, `cmm` already falls back to `"unknown"` when `git describe` is unavailable, but `cdx` only generates `version.h` if `.git` exists. That means a tarball-only build should remove `.git` for determinism **and** generate a stable placeholder `cdx/version.h` before building.
|
||||||
|
|
||||||
|
The supplied `cmm/src/cmm.c` already guards `execinfo.h` / `backtrace()` under `#if defined(__GLIBC__)`, which is exactly the right direction for musl portability: glibc-only code stays behind glibc checks, and non-glibc systems take the portable path. That is a good sign for Alpine migration.
|
||||||
|
|
||||||
|
### Kernel mismatch diagnosis
|
||||||
|
|
||||||
|
The specific kernel error you reported:
|
||||||
|
|
||||||
|
```text
|
||||||
|
kconfig mismatch: CONFIG_NETFILTER_XTABLES_LEGACY
|
||||||
|
expected: CONFIG_NETFILTER_XTABLES_LEGACY=y
|
||||||
|
actual: <missing>
|
||||||
|
error: resolved kernel config does not satisfy /tmp/kernel-extra.config
|
||||||
|
```
|
||||||
|
|
||||||
|
is not a generic dependency failure. It is a **fragment/tree mismatch**.
|
||||||
|
|
||||||
|
The supplied `kernel-extra.config` contains all three of these settings:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CONFIG_NETFILTER_XTABLES_LEGACY=y
|
||||||
|
CONFIG_IP_NF_IPTABLES_LEGACY=y
|
||||||
|
CONFIG_IP6_NF_IPTABLES_LEGACY=y
|
||||||
|
```
|
||||||
|
|
||||||
|
In the uploaded NXP 6.12 kernel tree, `CONFIG_IP_NF_IPTABLES_LEGACY` and `CONFIG_IP6_NF_IPTABLES_LEGACY` exist, but `CONFIG_NETFILTER_XTABLES_LEGACY` does not. That newer top-level gate belongs to later kernels; it is not valid for this tree. So the direct fix is:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
-CONFIG_NETFILTER_XTABLES_LEGACY=y
|
||||||
|
```
|
||||||
|
|
||||||
|
and nothing more. If you want one fragment to span multiple kernel lines, you must filter unsupported symbols before `olddefconfig`, or enable Kconfig’s unknown-symbol warnings and treat them as fatal. The kernel docs explicitly provide `KCONFIG_WARN_UNKNOWN_SYMBOLS` and `KCONFIG_WERROR` for this purpose, and also recommend `make listnewconfig` / `scripts/diffconfig` to inspect config drift. citeturn21view0turn8search1
|
||||||
|
|
||||||
|
## Reproducible build design
|
||||||
|
|
||||||
|
The build should be split into four distinct responsibilities.
|
||||||
|
|
||||||
|
First, a **host vendoring step** collects all source archives into `packages/` and writes `SHA256SUMS`. That step is allowed to touch the network if your policy permits; the container build should not. If the tarballs are private or sensitive, pass them as BuildKit secrets instead of regular build args. Docker’s docs are explicit: build args are not for secrets, because they may appear in image history and provenance metadata, while `RUN --mount=type=secret` makes the file available only for that instruction. citeturn12view0turn12view1turn12view2
|
||||||
|
|
||||||
|
Second, the container build should copy the vendored package directory into the builder stage, verify checksums, and extract only what the build needs. This is why the build arg should select a **path inside the already-copied vendor directory**, not try to trigger host-side fetching logic. In practice, that means `ARG ASK_TAR=packages/ASK.tar.gz`, then `tar -xf "/vendor/${ASK_TAR}" ...` inside `RUN`.
|
||||||
|
|
||||||
|
Third, for module builds, a matching `KERNEL_TAR` must be supplied and configured inside the build. The Linux kernel documentation is clear that external modules need a kernel tree with the right configuration and headers, and that `modules_prepare` is not enough when `CONFIG_MODVERSIONS` matters because it does not produce `Module.symvers`. If you are building out-of-tree modules against a board kernel, the safe rule is: **if the project builds kernel modules, require a matching kernel source tarball and either run a full kernel build or at least a preparation step appropriate to your versioning model.** citeturn22search0turn22search2
|
||||||
|
|
||||||
|
Fourth, export artifacts directly to the host filesystem using `--output type=local,dest=...`. Docker documents this as a first-class `buildx` output mode, and it is the cleanest way to make build results explicit and inspectable. citeturn16view0turn16view1
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Vendor tarballs on host\nASK + dependency tarballs + optional KERNEL_TAR] --> B[Generate SHA256SUMS]
|
||||||
|
B --> C[docker buildx build]
|
||||||
|
C --> D[base-build stage\napk add toolchain]
|
||||||
|
D --> E[Verify SHA256SUMS]
|
||||||
|
E --> F[Extract ASK tarball]
|
||||||
|
F --> G[Replace fetch logic with vendored tarball extraction]
|
||||||
|
C --> H{KERNEL_TAR supplied?}
|
||||||
|
H -- yes --> I[Extract kernel tree]
|
||||||
|
I --> J[Filter kernel fragment\nmerge_config.sh\nolddefconfig]
|
||||||
|
J --> K[Build kernel or modules_prepare]
|
||||||
|
K --> L[Build ASK kernel modules]
|
||||||
|
H -- no --> M[Build userspace only]
|
||||||
|
G --> M
|
||||||
|
M --> N[Collect dist/]
|
||||||
|
L --> N
|
||||||
|
N --> O[artifacts stage]
|
||||||
|
O --> P[--output type=local]
|
||||||
|
O --> Q[ASK_IMAGE minimal artifact image]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Concrete implementation
|
||||||
|
|
||||||
|
### Assumptions
|
||||||
|
|
||||||
|
Where the prompt does not specify details, these are the assumptions I am making rather than inventing facts:
|
||||||
|
|
||||||
|
- The supplied ASK project is a C/C++/Make project because that is what the uploaded tarball contains.
|
||||||
|
- The generic design below is still valid for CMake, Autotools, Go, Python, and Node projects, but the builder stage package set and build commands should be adjusted accordingly.
|
||||||
|
- The examples default to `linux/arm64` because your uploaded board build files target arm64.
|
||||||
|
- Tarballs are assumed to have a single top-level directory, which is normal for release archives and `git archive` outputs.
|
||||||
|
- GNU tar semantics are assumed for the normalization snippet.
|
||||||
|
- `KERNEL_TAR` is optional for userspace-only builds and mandatory for kernel-module builds.
|
||||||
|
- Base-image digests are shown as placeholders because you asked for the pattern, not a locked digest for one exact tag.
|
||||||
|
|
||||||
|
### Makefile
|
||||||
|
|
||||||
|
The example in your prompt needs one correction: the valid flag is `--build-arg`, not `--args`, and the Dockerfile is specified with `-f/--file`. This Makefile gives you a tarball-driven `ASK` target and an `ASK_IMAGE` target that loads a minimal artifact image locally.
|
||||||
|
|
||||||
|
```make
|
||||||
|
.RECIPEPREFIX := >
|
||||||
|
|
||||||
|
DOCKER_BUILDKIT ?= 1
|
||||||
|
export DOCKER_BUILDKIT
|
||||||
|
|
||||||
|
PLATFORM ?= linux/arm64
|
||||||
|
ALPINE_VERSION ?= 3.22
|
||||||
|
ASK_NAME ?= ask
|
||||||
|
ASK_OUT ?= out/ask
|
||||||
|
ASK_IMAGE_NAME ?= local/$(ASK_NAME):dev
|
||||||
|
|
||||||
|
# Required vendored inputs
|
||||||
|
ASK_TAR ?= packages/ASK.tar.gz
|
||||||
|
|
||||||
|
# Optional vendored inputs for offline/reproducible dependency builds
|
||||||
|
FMLIB_TAR ?= packages/fmlib-lf-6.12.49-2.2.0.tar.gz
|
||||||
|
FMC_TAR ?= packages/fmc-lf-6.12.49-2.2.0.tar.gz
|
||||||
|
LIBNFNETLINK_TAR?= packages/libnfnetlink-1.0.2.tar.bz2
|
||||||
|
LIBNFCT_TAR ?= packages/libnetfilter_conntrack-1.1.0.tar.xz
|
||||||
|
LIBCLI_TAR ?= packages/libcli-1.10.7.tar.gz
|
||||||
|
|
||||||
|
# Required only if you build kernel modules
|
||||||
|
KERNEL_TAR ?=
|
||||||
|
KERNEL_EXTRA ?= docker/kernel-extra.config
|
||||||
|
|
||||||
|
ARCH ?= arm64
|
||||||
|
CROSS_COMPILE ?=
|
||||||
|
BUILD_TARGET ?= dist
|
||||||
|
SOURCE_DATE_EPOCH ?= 1715731200
|
||||||
|
PROGRESS ?= plain
|
||||||
|
|
||||||
|
COMMON_BUILD_ARGS = \
|
||||||
|
--build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
|
||||||
|
--build-arg ASK_TAR=$(ASK_TAR) \
|
||||||
|
--build-arg FMLIB_TAR=$(FMLIB_TAR) \
|
||||||
|
--build-arg FMC_TAR=$(FMC_TAR) \
|
||||||
|
--build-arg LIBNFNETLINK_TAR=$(LIBNFNETLINK_TAR) \
|
||||||
|
--build-arg LIBNFCT_TAR=$(LIBNFCT_TAR) \
|
||||||
|
--build-arg LIBCLI_TAR=$(LIBCLI_TAR) \
|
||||||
|
--build-arg KERNEL_TAR=$(KERNEL_TAR) \
|
||||||
|
--build-arg ARCH=$(ARCH) \
|
||||||
|
--build-arg CROSS_COMPILE=$(CROSS_COMPILE) \
|
||||||
|
--build-arg BUILD_TARGET=$(BUILD_TARGET) \
|
||||||
|
--build-arg SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)
|
||||||
|
|
||||||
|
.PHONY: ASK ASK_IMAGE clean
|
||||||
|
|
||||||
|
ASK:
|
||||||
|
> mkdir -p $(ASK_OUT)
|
||||||
|
> docker buildx build \
|
||||||
|
> --platform $(PLATFORM) \
|
||||||
|
> --progress=$(PROGRESS) \
|
||||||
|
> -f docker/ask.Dockerfile \
|
||||||
|
> $(COMMON_BUILD_ARGS) \
|
||||||
|
> --target artifacts \
|
||||||
|
> --output type=local,dest=$(ASK_OUT) \
|
||||||
|
> .
|
||||||
|
|
||||||
|
ASK_IMAGE:
|
||||||
|
> docker buildx build \
|
||||||
|
> --platform $(PLATFORM) \
|
||||||
|
> --progress=$(PROGRESS) \
|
||||||
|
> -f docker/ask.Dockerfile \
|
||||||
|
> $(COMMON_BUILD_ARGS) \
|
||||||
|
> --target runtime \
|
||||||
|
> --load \
|
||||||
|
> -t $(ASK_IMAGE_NAME) \
|
||||||
|
> .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
> rm -rf $(ASK_OUT)
|
||||||
|
```
|
||||||
|
|
||||||
|
Example invocations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Userspace-only artifact export
|
||||||
|
make ASK ASK_TAR=packages/ASK.tar.gz BUILD_TARGET=userspace
|
||||||
|
|
||||||
|
# Full artifact export, including module build against a matching kernel tree
|
||||||
|
make ASK \
|
||||||
|
ASK_TAR=packages/ASK.tar.gz \
|
||||||
|
KERNEL_TAR=packages/lf-6.12.49-2.2.0.tar.gz \
|
||||||
|
BUILD_TARGET=dist \
|
||||||
|
ARCH=arm64 \
|
||||||
|
CROSS_COMPILE=
|
||||||
|
|
||||||
|
# Load a minimal artifact image into the local Docker image store
|
||||||
|
make ASK_IMAGE ASK_TAR=packages/ASK.tar.gz
|
||||||
|
|
||||||
|
# Override SOURCE_DATE_EPOCH for a release
|
||||||
|
make ASK SOURCE_DATE_EPOCH=1735689600
|
||||||
|
```
|
||||||
|
|
||||||
|
### `docker/ask.Dockerfile`
|
||||||
|
|
||||||
|
This Dockerfile does four things that matter: it verifies vendored source archives, extracts the selected tarball based on a build arg, replaces fetch logic with vendored extraction through an override Makefile, and supports optional kernel-tree injection for module builds.
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
ARG ALPINE_VERSION=3.22
|
||||||
|
|
||||||
|
FROM alpine:${ALPINE_VERSION} AS base-build
|
||||||
|
ARG SOURCE_DATE_EPOCH
|
||||||
|
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV TZ=UTC
|
||||||
|
|
||||||
|
# Replace the tag with a digest in CI/release builds:
|
||||||
|
# FROM alpine:${ALPINE_VERSION}@sha256:<digest> AS base-build
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
bash bc bison build-base cpio coreutils diffutils file findutils flex \
|
||||||
|
gawk git jq kmod libelf-dev libmnl-dev libpcap-dev libxml2-dev \
|
||||||
|
linux-headers openssl-dev patch perl pkgconf python3 rsync tar tclap-dev \
|
||||||
|
xz zlib-dev
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
|
||||||
|
# Copy vendored packages once; select the specific tarball path with ARG later.
|
||||||
|
COPY packages/ /vendor/packages/
|
||||||
|
COPY docker/overrides/ /docker-overrides/
|
||||||
|
COPY scripts/filter-kconfig-fragment.sh /usr/local/bin/filter-kconfig-fragment.sh
|
||||||
|
COPY docker/kernel-extra.config /tmp/kernel-extra.config
|
||||||
|
|
||||||
|
RUN chmod +x /usr/local/bin/filter-kconfig-fragment.sh && \
|
||||||
|
if [ -f /vendor/packages/SHA256SUMS ]; then \
|
||||||
|
cd /vendor/packages && sha256sum -c SHA256SUMS; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
FROM base-build AS builder
|
||||||
|
ARG ASK_TAR=packages/ASK.tar.gz
|
||||||
|
ARG FMLIB_TAR=packages/fmlib-lf-6.12.49-2.2.0.tar.gz
|
||||||
|
ARG FMC_TAR=packages/fmc-lf-6.12.49-2.2.0.tar.gz
|
||||||
|
ARG LIBNFNETLINK_TAR=packages/libnfnetlink-1.0.2.tar.bz2
|
||||||
|
ARG LIBNFCT_TAR=packages/libnetfilter_conntrack-1.1.0.tar.xz
|
||||||
|
ARG LIBCLI_TAR=packages/libcli-1.10.7.tar.gz
|
||||||
|
ARG KERNEL_TAR=
|
||||||
|
ARG ARCH=arm64
|
||||||
|
ARG CROSS_COMPILE=
|
||||||
|
ARG BUILD_TARGET=dist
|
||||||
|
ARG SOURCE_DATE_EPOCH
|
||||||
|
|
||||||
|
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
|
||||||
|
ENV KBUILD_BUILD_TIMESTAMP=@${SOURCE_DATE_EPOCH}
|
||||||
|
ENV KBUILD_BUILD_USER=repro
|
||||||
|
ENV KBUILD_BUILD_HOST=repro-host
|
||||||
|
|
||||||
|
# Optional vendored libcli because Alpine stable does not have a dependable stable libcli-dev path.
|
||||||
|
RUN if [ -n "${LIBCLI_TAR}" ] && [ -f "/vendor/${LIBCLI_TAR}" ]; then \
|
||||||
|
mkdir -p /tmp/libcli-src /usr/local && \
|
||||||
|
tar -xf "/vendor/${LIBCLI_TAR}" --strip-components=1 -C /tmp/libcli-src && \
|
||||||
|
make -C /tmp/libcli-src && \
|
||||||
|
make -C /tmp/libcli-src PREFIX=/usr/local install; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract ASK from the provided tarball and replace upstream fetch logic.
|
||||||
|
RUN mkdir -p /work/src/ASK && \
|
||||||
|
tar -xf "/vendor/${ASK_TAR}" --strip-components=1 -C /work/src/ASK && \
|
||||||
|
rm -rf /work/src/ASK/.git && \
|
||||||
|
install -m 0644 /docker-overrides/Makefile /work/src/ASK/Makefile && \
|
||||||
|
install -m 0644 /docker-overrides/toolchain.mk /work/src/ASK/build/toolchain.mk && \
|
||||||
|
mkdir -p /work/src/ASK/cdx && \
|
||||||
|
printf '%s\n' \
|
||||||
|
'/* Auto-generated for tarball builds */' \
|
||||||
|
'#ifndef CDX_VERSION_H' \
|
||||||
|
'#define CDX_VERSION_H' \
|
||||||
|
'#define CDX_GIT_VERSION "tarball"' \
|
||||||
|
'#endif' \
|
||||||
|
> /work/src/ASK/cdx/version.h
|
||||||
|
|
||||||
|
# Optional matching kernel tree, required for module builds.
|
||||||
|
RUN if [ -n "${KERNEL_TAR}" ]; then \
|
||||||
|
mkdir -p /opt/kernel && \
|
||||||
|
tar -xf "/vendor/${KERNEL_TAR}" --strip-components=1 -C /opt/kernel && \
|
||||||
|
/usr/local/bin/filter-kconfig-fragment.sh /opt/kernel /tmp/kernel-extra.config > /tmp/kernel-extra.effective.config && \
|
||||||
|
make -C /opt/kernel ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" defconfig && \
|
||||||
|
/opt/kernel/scripts/kconfig/merge_config.sh -m /opt/kernel/.config /tmp/kernel-extra.effective.config && \
|
||||||
|
KCONFIG_WARN_UNKNOWN_SYMBOLS=1 KCONFIG_WERROR=1 \
|
||||||
|
make -C /opt/kernel ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" olddefconfig && \
|
||||||
|
make -C /opt/kernel ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" -j"$(nproc)" modules_prepare; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORKDIR /work/src/ASK
|
||||||
|
ENV KDIR=/opt/kernel
|
||||||
|
ENV TAR_DIR=/vendor/packages
|
||||||
|
ENV FMLIB_TAR=/vendor/${FMLIB_TAR}
|
||||||
|
ENV FMC_TAR=/vendor/${FMC_TAR}
|
||||||
|
ENV LIBNFNETLINK_TAR=/vendor/${LIBNFNETLINK_TAR}
|
||||||
|
ENV LIBNFCT_TAR=/vendor/${LIBNFCT_TAR}
|
||||||
|
|
||||||
|
RUN case "${BUILD_TARGET}" in \
|
||||||
|
userspace) make userspace ;; \
|
||||||
|
modules) test -n "${KERNEL_TAR}" && make modules ;; \
|
||||||
|
dist) test -n "${KERNEL_TAR}" && make dist ;; \
|
||||||
|
*) echo "unsupported BUILD_TARGET=${BUILD_TARGET}" >&2; exit 2 ;; \
|
||||||
|
esac && \
|
||||||
|
mkdir -p /out && \
|
||||||
|
cp -a dist/. /out/
|
||||||
|
|
||||||
|
FROM scratch AS artifacts
|
||||||
|
COPY --from=builder /out/ /
|
||||||
|
|
||||||
|
# Minimal artifact image, not a runtime service image.
|
||||||
|
FROM scratch AS runtime
|
||||||
|
COPY --from=builder /out/ /opt/ask/
|
||||||
|
```
|
||||||
|
|
||||||
|
### `docker/overrides/toolchain.mk`
|
||||||
|
|
||||||
|
This keeps the upstream layout but makes the toolchain configurable and points the kernel directory at the injected kernel tree.
|
||||||
|
|
||||||
|
```make
|
||||||
|
# Toolchain override for reproducible container builds
|
||||||
|
CROSS_COMPILE ?=
|
||||||
|
ARCH ?= arm64
|
||||||
|
PLATFORM ?= LS1043A
|
||||||
|
|
||||||
|
CC := $(if $(CROSS_COMPILE),$(CROSS_COMPILE)gcc,gcc)
|
||||||
|
CXX := $(if $(CROSS_COMPILE),$(CROSS_COMPILE)g++,g++)
|
||||||
|
AR := $(if $(CROSS_COMPILE),$(CROSS_COMPILE)ar,ar)
|
||||||
|
STRIP := $(if $(CROSS_COMPILE),$(CROSS_COMPILE)strip,strip)
|
||||||
|
|
||||||
|
# Injected by Dockerfile when building modules
|
||||||
|
KDIR ?= /opt/kernel
|
||||||
|
```
|
||||||
|
|
||||||
|
### `docker/overrides/Makefile`
|
||||||
|
|
||||||
|
This is the critical part. It preserves the upstream build shape but replaces network fetches with vendored tarball extraction. The intent is simple: **no `git clone`, no `wget`, no `curl` inside the build.**
|
||||||
|
|
||||||
|
```make
|
||||||
|
include build/toolchain.mk
|
||||||
|
include build/sources.mk
|
||||||
|
|
||||||
|
DEFCONFIG := $(KDIR)/.config
|
||||||
|
DIST := $(CURDIR)/dist
|
||||||
|
SRCDIR := $(CURDIR)/sources
|
||||||
|
PATCHES := $(CURDIR)/patches
|
||||||
|
HOST := $(if $(CROSS_COMPILE),$(patsubst %-,-%,$(CROSS_COMPILE:%-=%)),)
|
||||||
|
SYSROOT := $(SRCDIR)/sysroot
|
||||||
|
FMLIB_DIR := $(SRCDIR)/fmlib
|
||||||
|
FMC_DIR := $(SRCDIR)/fmc/source
|
||||||
|
LIBFCI_DIR := $(CURDIR)/fci/lib
|
||||||
|
ABM_DIR := $(CURDIR)/auto_bridge
|
||||||
|
|
||||||
|
S := $(SRCDIR)/.stamps
|
||||||
|
$(shell mkdir -p $(S))
|
||||||
|
|
||||||
|
FMLIB_TAR ?= /vendor/packages/fmlib-$(NXP_TAG).tar.gz
|
||||||
|
FMC_TAR ?= /vendor/packages/fmc-$(NXP_TAG).tar.gz
|
||||||
|
LIBNFNETLINK_TAR ?= /vendor/packages/libnfnetlink-$(LIBNFNETLINK_VER).tar.bz2
|
||||||
|
LIBNFCT_TAR ?= /vendor/packages/libnetfilter_conntrack-$(LIBNFCT_VER).tar.xz
|
||||||
|
|
||||||
|
KBUILD_ARGS := CROSS_COMPILE=$(CROSS_COMPILE) ARCH=$(ARCH)
|
||||||
|
CDX_ARGS := $(KBUILD_ARGS) KERNELDIR=$(KDIR) PLATFORM=$(PLATFORM)
|
||||||
|
FCI_ARGS := $(KBUILD_ARGS) KERNEL_SOURCE=$(KDIR) BOARD_ARCH=$(ARCH) \
|
||||||
|
KBUILD_EXTRA_SYMBOLS=$(CURDIR)/cdx/Module.symvers
|
||||||
|
ABM_ARGS := $(KBUILD_ARGS) KERNEL_SOURCE=$(KDIR) PLATFORM=$(PLATFORM)
|
||||||
|
|
||||||
|
.PHONY: all sources modules userspace dist clean clean-all
|
||||||
|
all: modules userspace
|
||||||
|
|
||||||
|
sources: $(S)/fmlib $(S)/fmc $(S)/libfci $(S)/libnfnetlink $(S)/libnfct
|
||||||
|
|
||||||
|
$(S)/fmlib:
|
||||||
|
test -f "$(FMLIB_TAR)"
|
||||||
|
rm -rf "$(FMLIB_DIR)"
|
||||||
|
mkdir -p "$(FMLIB_DIR)"
|
||||||
|
tar -xf "$(FMLIB_TAR)" --strip-components=1 -C "$(FMLIB_DIR)"
|
||||||
|
cd "$(FMLIB_DIR)" && patch -p1 < "$(PATCHES)/fmlib/01-mono-ask-extensions.patch"
|
||||||
|
$(MAKE) -C "$(FMLIB_DIR)" CROSS_COMPILE="$(CROSS_COMPILE)" KERNEL_SRC="$(KDIR)" libfm-arm.a
|
||||||
|
ln -sf libfm-arm.a "$(FMLIB_DIR)/libfm.a"
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(S)/fmc: $(S)/fmlib
|
||||||
|
test -f "$(FMC_TAR)"
|
||||||
|
rm -rf "$(SRCDIR)/fmc"
|
||||||
|
mkdir -p "$(SRCDIR)/fmc"
|
||||||
|
tar -xf "$(FMC_TAR)" --strip-components=1 -C "$(SRCDIR)/fmc"
|
||||||
|
cd "$(SRCDIR)/fmc" && patch -p1 < "$(PATCHES)/fmc/01-mono-ask-extensions.patch"
|
||||||
|
$(MAKE) -C "$(FMC_DIR)" \
|
||||||
|
CC="$(CC)" CXX="$(CXX)" AR="$(AR)" \
|
||||||
|
MACHINE=ls1046 \
|
||||||
|
FMD_USPACE_HEADER_PATH="$(FMLIB_DIR)/include/fmd" \
|
||||||
|
FMD_USPACE_LIB_PATH="$(FMLIB_DIR)" \
|
||||||
|
LIBXML2_HEADER_PATH=/usr/include/libxml2 \
|
||||||
|
TCLAP_HEADER_PATH=/usr/include
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(S)/libfci:
|
||||||
|
$(MAKE) -C "$(LIBFCI_DIR)" CC="$(CC)" AR="$(AR)"
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(S)/libnfnetlink:
|
||||||
|
test -f "$(LIBNFNETLINK_TAR)"
|
||||||
|
rm -rf "$(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER)"
|
||||||
|
mkdir -p "$(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER)"
|
||||||
|
tar -xf "$(LIBNFNETLINK_TAR)" --strip-components=1 -C "$(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER)"
|
||||||
|
cd "$(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER)" && \
|
||||||
|
patch -p1 < "$(PATCHES)/libnfnetlink/01-nxp-ask-nonblocking-heap-buffer.patch" && \
|
||||||
|
./configure --host="$(HOST)" --prefix="$(SYSROOT)" --enable-static --disable-shared -q && \
|
||||||
|
$(MAKE) -j$$(nproc) -s && $(MAKE) install -s
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(S)/libnfct: $(S)/libnfnetlink
|
||||||
|
test -f "$(LIBNFCT_TAR)"
|
||||||
|
rm -rf "$(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER)"
|
||||||
|
mkdir -p "$(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER)"
|
||||||
|
tar -xf "$(LIBNFCT_TAR)" --strip-components=1 -C "$(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER)"
|
||||||
|
cd "$(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER)" && \
|
||||||
|
patch -p1 < "$(PATCHES)/libnetfilter-conntrack/01-nxp-ask-comcerto-fp-extensions.patch" && \
|
||||||
|
PKG_CONFIG_PATH="$(SYSROOT)/lib/pkgconfig" \
|
||||||
|
./configure --host="$(HOST)" --prefix="$(SYSROOT)" --enable-static --disable-shared -q \
|
||||||
|
CFLAGS="-I$(SYSROOT)/include" LDFLAGS="-L$(SYSROOT)/lib" && \
|
||||||
|
$(MAKE) -j$$(nproc) -s && $(MAKE) install -s
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
modules: sources
|
||||||
|
test -d "$(KDIR)"
|
||||||
|
$(MAKE) -C cdx $(CDX_ARGS)
|
||||||
|
$(MAKE) -C fci $(FCI_ARGS)
|
||||||
|
$(MAKE) -C auto_bridge $(ABM_ARGS)
|
||||||
|
|
||||||
|
userspace: sources
|
||||||
|
$(MAKE) -C "$(FMC_DIR)" \
|
||||||
|
CC="$(CC)" CXX="$(CXX)" AR="$(AR)" \
|
||||||
|
MACHINE=ls1046 \
|
||||||
|
FMD_USPACE_HEADER_PATH="$(FMLIB_DIR)/include/fmd" \
|
||||||
|
FMD_USPACE_LIB_PATH="$(FMLIB_DIR)" \
|
||||||
|
LIBXML2_HEADER_PATH=/usr/include/libxml2 \
|
||||||
|
TCLAP_HEADER_PATH=/usr/include
|
||||||
|
$(MAKE) -C cmm \
|
||||||
|
CC="$(CC)" \
|
||||||
|
PKG_CONFIG=pkg-config \
|
||||||
|
PKG_CONFIG_PATH="$(SYSROOT)/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig" \
|
||||||
|
LDFLAGS="-L$(SYSROOT)/lib -L/usr/local/lib" \
|
||||||
|
CFLAGS="-I$(SYSROOT)/include -I/usr/local/include"
|
||||||
|
$(MAKE) -C dpa_app \
|
||||||
|
CC="$(CC)" \
|
||||||
|
CFLAGS="-I$(FMLIB_DIR)/include -I$(FMC_DIR)/libfci_cli/src" \
|
||||||
|
LDFLAGS="-lpthread -lcli -L/usr/local/lib -L$(FMC_DIR) -lfmc -L$(FMLIB_DIR) -lfm -lstdc++ -lxml2 -lm"
|
||||||
|
|
||||||
|
dist: modules userspace
|
||||||
|
mkdir -p "$(DIST)"
|
||||||
|
cp cdx/cdx.ko "$(DIST)/"
|
||||||
|
cp fci/fci.ko "$(DIST)/"
|
||||||
|
cp auto_bridge/auto_bridge.ko "$(DIST)/"
|
||||||
|
cp "$(FMC_DIR)/fmc" "$(DIST)/"
|
||||||
|
cp cmm/src/cmm "$(DIST)/"
|
||||||
|
cp dpa_app/dpa_app "$(DIST)/"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-$(MAKE) -C cdx $(CDX_ARGS) clean
|
||||||
|
-$(MAKE) -C fci $(FCI_ARGS) clean
|
||||||
|
-$(MAKE) -C auto_bridge $(ABM_ARGS) clean
|
||||||
|
-$(MAKE) -C "$(LIBFCI_DIR)" clean
|
||||||
|
-$(MAKE) -C cmm clean
|
||||||
|
-$(MAKE) -C dpa_app clean
|
||||||
|
rm -rf "$(DIST)"
|
||||||
|
rm -f "$(S)"/*
|
||||||
|
|
||||||
|
clean-all: clean
|
||||||
|
rm -rf "$(SRCDIR)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `.dockerignore`
|
||||||
|
|
||||||
|
Keep the context brutally small. That improves cache behavior and reduces the chance that unrelated files perturb reproducibility.
|
||||||
|
|
||||||
|
```dockerignore
|
||||||
|
**
|
||||||
|
!docker/**
|
||||||
|
!scripts/**
|
||||||
|
!packages/**
|
||||||
|
!Makefile
|
||||||
|
```
|
||||||
|
|
||||||
|
### Host vendoring script
|
||||||
|
|
||||||
|
This is the clean place to fetch and lock dependency tarballs. It can pull from upstream or from your own artifact mirror; the container build stays offline either way.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
mkdir -p packages
|
||||||
|
|
||||||
|
# Required
|
||||||
|
cp "${ASK_TAR_SRC:?set ASK_TAR_SRC}" packages/ASK.tar.gz
|
||||||
|
|
||||||
|
# Optional but required for full offline ASK builds
|
||||||
|
[ -n "${FMLIB_TAR_SRC:-}" ] && cp "${FMLIB_TAR_SRC}" packages/fmlib-lf-6.12.49-2.2.0.tar.gz
|
||||||
|
[ -n "${FMC_TAR_SRC:-}" ] && cp "${FMC_TAR_SRC}" packages/fmc-lf-6.12.49-2.2.0.tar.gz
|
||||||
|
[ -n "${LIBNFNETLINK_TAR_SRC:-}" ] && cp "${LIBNFNETLINK_TAR_SRC}" packages/libnfnetlink-1.0.2.tar.bz2
|
||||||
|
[ -n "${LIBNFCT_TAR_SRC:-}" ] && cp "${LIBNFCT_TAR_SRC}" packages/libnetfilter_conntrack-1.1.0.tar.xz
|
||||||
|
[ -n "${LIBCLI_TAR_SRC:-}" ] && cp "${LIBCLI_TAR_SRC}" packages/libcli-1.10.7.tar.gz
|
||||||
|
[ -n "${KERNEL_TAR_SRC:-}" ] && cp "${KERNEL_TAR_SRC}" packages/lf-6.12.49-2.2.0.tar.gz
|
||||||
|
|
||||||
|
(
|
||||||
|
cd packages
|
||||||
|
rm -f SHA256SUMS
|
||||||
|
sha256sum *.tar.gz *.tar.xz *.tar.bz2 2>/dev/null | sort -k2 > SHA256SUMS
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tarball normalization snippet
|
||||||
|
|
||||||
|
Use this on the host if you need to strip VCS noise and normalize timestamps before vendoring.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
: "${SOURCE_DATE_EPOCH:?set SOURCE_DATE_EPOCH}"
|
||||||
|
: "${IN_TAR:?set IN_TAR}"
|
||||||
|
: "${OUT_TAR:?set OUT_TAR}"
|
||||||
|
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmp"' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$tmp/src"
|
||||||
|
tar -xf "$IN_TAR" -C "$tmp/src"
|
||||||
|
root="$(find "$tmp/src" -mindepth 1 -maxdepth 1 -type d | head -n1)"
|
||||||
|
|
||||||
|
rm -rf "$root/.git"
|
||||||
|
find "$root" -exec touch -h -d "@${SOURCE_DATE_EPOCH}" {} +
|
||||||
|
|
||||||
|
tar --sort=name \
|
||||||
|
--mtime="@${SOURCE_DATE_EPOCH}" \
|
||||||
|
--owner=0 --group=0 --numeric-owner \
|
||||||
|
-czf "$OUT_TAR" -C "$tmp/src" "$(basename "$root")"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kernel fragment filtering and concrete fix for `CONFIG_NETFILTER_XTABLES_LEGACY`
|
||||||
|
|
||||||
|
This script solves the exact mismatch you reported without weakening verification for valid symbols.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
KERNEL_DIR="${1:?kernel dir required}"
|
||||||
|
FRAGMENT="${2:?fragment required}"
|
||||||
|
|
||||||
|
symbol_exists() {
|
||||||
|
local sym="${1#CONFIG_}"
|
||||||
|
grep -RqsE "^[[:space:]]*(menu)?config[[:space:]]+${sym}\b" \
|
||||||
|
"$KERNEL_DIR"/Kconfig \
|
||||||
|
"$KERNEL_DIR"/arch \
|
||||||
|
"$KERNEL_DIR"/drivers \
|
||||||
|
"$KERNEL_DIR"/fs \
|
||||||
|
"$KERNEL_DIR"/init \
|
||||||
|
"$KERNEL_DIR"/kernel \
|
||||||
|
"$KERNEL_DIR"/lib \
|
||||||
|
"$KERNEL_DIR"/mm \
|
||||||
|
"$KERNEL_DIR"/net \
|
||||||
|
"$KERNEL_DIR"/security \
|
||||||
|
"$KERNEL_DIR"/sound \
|
||||||
|
"$KERNEL_DIR"/virt
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
stripped="$(printf '%s\n' "$line" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
|
||||||
|
[ -z "$stripped" ] && { printf '\n'; continue; }
|
||||||
|
|
||||||
|
case "$stripped" in
|
||||||
|
\#\ CONFIG_*" is not set")
|
||||||
|
sym="$(printf '%s' "$stripped" | sed 's/^# \(CONFIG_[^ ]*\) is not set$/\1/')"
|
||||||
|
symbol_exists "$sym" && printf '%s\n' "$stripped"
|
||||||
|
;;
|
||||||
|
CONFIG_*=*)
|
||||||
|
sym="${stripped%%=*}"
|
||||||
|
symbol_exists "$sym" && printf '%s\n' "$stripped"
|
||||||
|
;;
|
||||||
|
\#*)
|
||||||
|
printf '%s\n' "$stripped"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf '%s\n' "$stripped"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < "$FRAGMENT"
|
||||||
|
```
|
||||||
|
|
||||||
|
For your exact fragment, the direct patch is just this:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
--- a/docker/kernel-extra.config
|
||||||
|
+++ b/docker/kernel-extra.config
|
||||||
|
@@
|
||||||
|
-CONFIG_NETFILTER_XTABLES_LEGACY=y
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to inject and verify a corrected kernel config inside the Docker build before module compilation, use this sequence:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
RUN mkdir -p /opt/kernel && \
|
||||||
|
tar -xf "/vendor/${KERNEL_TAR}" --strip-components=1 -C /opt/kernel && \
|
||||||
|
/usr/local/bin/filter-kconfig-fragment.sh /opt/kernel /tmp/kernel-extra.config > /tmp/kernel-extra.effective.config && \
|
||||||
|
make -C /opt/kernel ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" defconfig && \
|
||||||
|
/opt/kernel/scripts/kconfig/merge_config.sh -m /opt/kernel/.config /tmp/kernel-extra.effective.config && \
|
||||||
|
KCONFIG_WARN_UNKNOWN_SYMBOLS=1 KCONFIG_WERROR=1 \
|
||||||
|
make -C /opt/kernel ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" olddefconfig && \
|
||||||
|
grep -E '^CONFIG_(NETFILTER_XTABLES|IP_NF_IPTABLES|IP_NF_IPTABLES_LEGACY|IP6_NF_IPTABLES|IP6_NF_IPTABLES_LEGACY)=' /opt/kernel/.config
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer imperative fixes over fragment files, `scripts/config` is fine too:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/kernel
|
||||||
|
scripts/config --file .config \
|
||||||
|
-e NETFILTER \
|
||||||
|
-e NETFILTER_ADVANCED \
|
||||||
|
-e NETFILTER_XTABLES \
|
||||||
|
-e IP_NF_IPTABLES \
|
||||||
|
-e IP_NF_IPTABLES_LEGACY \
|
||||||
|
-e IP_NF_FILTER \
|
||||||
|
-e IP_NF_MANGLE \
|
||||||
|
-e IP_NF_NAT \
|
||||||
|
-e IP6_NF_IPTABLES \
|
||||||
|
-e IP6_NF_IPTABLES_LEGACY \
|
||||||
|
-e IP6_NF_FILTER \
|
||||||
|
-e IP6_NF_MANGLE \
|
||||||
|
-e IP6_NF_NAT
|
||||||
|
|
||||||
|
# Only enable if the symbol actually exists in this tree.
|
||||||
|
grep -RqsE '^[[:space:]]*(menu)?config[[:space:]]+NETFILTER_XTABLES_LEGACY\b' . && \
|
||||||
|
scripts/config --file .config -e NETFILTER_XTABLES_LEGACY || true
|
||||||
|
|
||||||
|
KCONFIG_WARN_UNKNOWN_SYMBOLS=1 KCONFIG_WERROR=1 \
|
||||||
|
make ARCH=arm64 CROSS_COMPILE="${CROSS_COMPILE}" olddefconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build-arg versus BuildKit secret mount
|
||||||
|
|
||||||
|
For ordinary source tarballs, build args are fine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker buildx build \
|
||||||
|
-f docker/ask.Dockerfile \
|
||||||
|
--build-arg ASK_TAR=packages/ASK.tar.gz \
|
||||||
|
--target artifacts \
|
||||||
|
--output type=local,dest=out/ask \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
For **private** source archives or credentials, do not use build args. Use a secret mount:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker buildx build \
|
||||||
|
-f docker/ask.Dockerfile \
|
||||||
|
--secret id=ask_tar,src=packages/ASK.tar.gz \
|
||||||
|
--target artifacts \
|
||||||
|
--output type=local,dest=out/ask \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
RUN --mount=type=secret,id=ask_tar,target=/run/secrets/ASK.tar.gz \
|
||||||
|
mkdir -p /work/src/ASK && \
|
||||||
|
tar -xf /run/secrets/ASK.tar.gz --strip-components=1 -C /work/src/ASK
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes on the code above: Docker’s documented behavior is that build args are for Dockerfile parameterization, may persist in image metadata/history, and are therefore not appropriate for secrets; build secrets are mounted temporarily for the duration of a single `RUN`; multi-stage builds are the recommended way to shrink outputs; `.dockerignore` is the standard way to keep context small; and `buildx` supports `type=local` output directly to the client filesystem. Docker also recommends digest pinning for deterministic base-image selection. citeturn12view0turn12view1turn12view2turn12view3turn13view0turn16view0turn16view1turn16view3
|
||||||
|
|
||||||
|
Kernel-side notes on the config flow above: the kernel docs provide `KCONFIG_WARN_UNKNOWN_SYMBOLS`, `KCONFIG_WERROR`, `KCONFIG_ALLCONFIG`, `listnewconfig`, and `scripts/diffconfig` for config hygiene; they also show `merge_config.sh` in use; and they document that external modules need a prepared kernel tree, with `modules_prepare` not being sufficient for `Module.symvers` when module versioning is involved. Reproducible kernel builds should also set `KBUILD_BUILD_TIMESTAMP`, `KBUILD_BUILD_USER`, `KBUILD_BUILD_HOST`, and `SOURCE_DATE_EPOCH` where external code embeds timestamps. citeturn21view0turn8search1turn22search0turn22search4turn9view1turn11search0
|
||||||
|
|
||||||
|
## Debian Trixie to Alpine conversion
|
||||||
|
|
||||||
|
The conversion rule is simple: translate package names, then re-evaluate libc assumptions. Do **not** assume a one-line mechanical translation is enough. Alpine stable gives you `build-base`, `gcc`, `g++`, `musl-dev`, `pkgconf`, `openssl-dev`, `zlib-dev`, `cmake`, and `tclap-dev` through its normal repositories, but repository choice matters: Alpine explicitly warns that `testing` is edge-only and should not be used for deterministic production-like container builds. That matters for `libcli`, where the official package index shows a `libcli` package in edge/testing, but not a stable `libcli-dev` path for the v3.22 line. In other words: for `libcli`, vendoring the source tarball is the safe answer. citeturn17view1turn17view3turn18view8
|
||||||
|
|
||||||
|
| Tool / library | Debian Trixie package(s) | Debian install command | Alpine package(s) | Alpine install command | Practical note |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| gcc | `gcc` | `apt-get update && apt-get install -y gcc` | `gcc` | `apk add --no-cache gcc` | Same compiler family, different libc target by default |
|
||||||
|
| g++ | `g++` | `apt-get update && apt-get install -y g++` | `g++` | `apk add --no-cache g++` | Same caveat as gcc |
|
||||||
|
| make | `make` | `apt-get update && apt-get install -y make` | `make` | `apk add --no-cache make` | Straight mapping |
|
||||||
|
| cmake | `cmake` | `apt-get update && apt-get install -y cmake` | `cmake` | `apk add --no-cache cmake` | Straight mapping |
|
||||||
|
| pkg-config / pkgconf | `pkgconf` or `pkg-config` | `apt-get update && apt-get install -y pkgconf` | `pkgconf` | `apk add --no-cache pkgconf` | Prefer `pkgconf` on both |
|
||||||
|
| OpenSSL development | `libssl-dev` | `apt-get update && apt-get install -y libssl-dev` | `openssl-dev` | `apk add --no-cache openssl-dev` | Use `openssl` separately if you need the CLI |
|
||||||
|
| zlib development | `zlib1g-dev` | `apt-get update && apt-get install -y zlib1g-dev` | `zlib-dev` | `apk add --no-cache zlib-dev` | Straight mapping |
|
||||||
|
| libstdc++ runtime | `libstdc++6` | `apt-get update && apt-get install -y libstdc++6` | `libstdc++` | `apk add --no-cache libstdc++` | Headers/dev pieces still come from `g++` / toolchain |
|
||||||
|
| libc development files | `libc6-dev` | `apt-get update && apt-get install -y libc6-dev` | `musl-dev` | `apk add --no-cache musl-dev` | This is the real ABI divide |
|
||||||
|
| build meta-package | `build-essential` | `apt-get update && apt-get install -y build-essential` | `build-base` | `apk add --no-cache build-base` | Debian and Alpine standard meta-packages |
|
||||||
|
| arm64 cross meta-package | `crossbuild-essential-arm64` | `apt-get update && apt-get install -y crossbuild-essential-arm64` | **no direct equivalent** | `apk add --no-cache build-base` plus Buildx/QEMU, or install a dedicated cross toolchain | Prefer Buildx `--platform` unless you truly need a host cross toolchain |
|
||||||
|
| TCLAP development | `libtclap-dev` | `apt-get update && apt-get install -y libtclap-dev` | `tclap-dev` | `apk add --no-cache tclap-dev` | Alpine package is in `community` |
|
||||||
|
| libcli development | `libcli-dev` | `apt-get update && apt-get install -y libcli-dev` | **no stable equivalent surfaced for v3.22** | **vendor and build from tarball** | edge/testing shows `libcli`, not a dependable stable `libcli-dev` path |
|
||||||
|
|
||||||
|
Sources and notes for the table: Debian’s official package pages document `build-essential`, `crossbuild-essential-arm64`, and the default `g++` metapackage; the Debian package index and source pages also show `libtclap-dev` and `libcli-dev`. Alpine’s official package pages and wiki document `build-base`, `gcc`, `g++`, `cmake`, `musl-dev`, `libstdc++`, `openssl-dev`, `zlib-dev`, `tclap-dev`, and repository support rules. Alpine’s repository documentation explicitly says `testing` is edge-only and not appropriate for deterministic production-like use, which is why vendoring `libcli` is the better answer than leaning on edge/testing in a stable builder. citeturn18view10turn18view9turn18view11turn7search0turn18view12turn18view0turn19search2turn18view3turn18view4turn18view1turn18view2turn19search5turn19search9turn18view7turn17view1turn17view3turn18view8
|
||||||
|
|
||||||
|
## Musl compatibility and troubleshooting
|
||||||
|
|
||||||
|
The musl migration problem is mostly about identifying which failures are **toolchain/configuration** issues and which are **libc/ABI** issues.
|
||||||
|
|
||||||
|
At the toolchain level, Alpine’s own documentation says `build-base` is the standard compiler meta-package and warns that ordinary Alpine systems use BusyBox, which means some GNU utility behavior is absent or reduced; if your build scripts assume full GNU userland, add `bash`, `coreutils`, `findutils`, `diffutils`, `gawk`, and similar packages explicitly instead of assuming Debian defaults. citeturn17view3turn17view4
|
||||||
|
|
||||||
|
At the libc level, musl’s documentation highlights the differences that actually break software. The big four are: smaller default thread stacks, limited symbol-versioning support compared with glibc’s versioned symbol model, `dlclose()` being a no-op, and code taking the wrong path because the project wrote glibc-specific preprocessor logic backwards. The musl FAQ explicitly calls out `getopt` assumptions, `iconv` assumptions, `off_t` width assumptions, and bad `#ifdef` logic as common failure modes, and recommends making the preprocessor logic check for `__GLIBC__` correctly rather than defaulting to glibc behavior. citeturn17view5turn17view6
|
||||||
|
|
||||||
|
For the supplied ASK sources specifically, the good news is that one of the classic glibc-only pain points is already handled. `cmm/src/cmm.c` wraps `execinfo.h` and `backtrace()` behind `__GLIBC__` guards. Keep that pattern. Do not “fix” it by forcing musl through the glibc path.
|
||||||
|
|
||||||
|
A practical troubleshooting order is:
|
||||||
|
|
||||||
|
- If the failure is missing headers or package names, fix package mappings first.
|
||||||
|
- If the failure is undefined references or runtime loader errors, inspect whether the code or binary assumes glibc versioned symbols.
|
||||||
|
- If the failure is random crashes in worker threads, suspect musl’s smaller default thread stack and either reduce stack usage or set an explicit stack size.
|
||||||
|
- If the failure is plugin-reload behavior, remember that musl treats `dlclose()` differently.
|
||||||
|
- If the failure is from a third-party binary blob, try `gcompat` only for simple runtime compatibility. Do not assume it is a full glibc replacement.
|
||||||
|
|
||||||
|
Recommended fixes, in descending order of cleanliness:
|
||||||
|
|
||||||
|
- **Patch the source** so it is libc-agnostic.
|
||||||
|
- **Adjust link flags** for more self-contained outputs where appropriate, such as `-static-libgcc` and, if acceptable for your deployment model, `-static-libstdc++`.
|
||||||
|
- **Use `gcompat`** only for relatively simple glibc-linked runtime binaries on Alpine, not as a blanket cure-all. Alpine documents it as a compatibility layer, not a full glibc runtime. citeturn17view2turn17view7turn17view8
|
||||||
|
- **Use a glibc builder stage** for the one component that genuinely cannot be ported, while keeping the rest of the system on Alpine.
|
||||||
|
- **Keep a glibc runtime image** for that component if you hit hard symbol-versioning or loader constraints that `gcompat` cannot cover.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Component fails on Alpine/musl] --> B{Package/toolchain issue?}
|
||||||
|
B -- yes --> C[Fix apk package names\nadd GNU userland tools\nrebuild]
|
||||||
|
C --> D{Now links and runs?}
|
||||||
|
D -- yes --> E[Stay on Alpine]
|
||||||
|
D -- no --> F{Source has glibc-only ifdefs or APIs?}
|
||||||
|
B -- no --> F
|
||||||
|
F -- yes --> G[Patch source\nuse __GLIBC__ guards\nfix flags]
|
||||||
|
G --> H{Still failing?}
|
||||||
|
H -- no --> E
|
||||||
|
H -- yes --> I{Simple glibc runtime dependency only?}
|
||||||
|
F -- no --> I
|
||||||
|
I -- yes --> J[Try gcompat]
|
||||||
|
J --> K{Reliable enough?}
|
||||||
|
K -- yes --> E
|
||||||
|
K -- no --> L[Use glibc builder or runtime for that component]
|
||||||
|
I -- no --> L
|
||||||
|
L --> M{Kernel module / ABI-coupled piece?}
|
||||||
|
M -- yes --> N[Require matching KERNEL_TAR\nand matching kernel config]
|
||||||
|
M -- no --> O[Split build:\nAlpine for musl-safe parts,\nglibc stage for offender]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sources and notes
|
||||||
|
|
||||||
|
This report prioritizes official documentation. Docker guidance comes from the official Docker docs on best practices, multi-stage builds, build variables, build secrets, build context, `.dockerignore`, and `buildx` outputs and secret handling. Alpine guidance comes from the Alpine wiki and package index for musl, repositories, software management, BusyBox, `build-base`, `tclap-dev`, `gcompat`, and related packages. musl/glibc compatibility notes come from the musl wiki’s FAQ and functional-differences pages. Kernel configuration and reproducibility guidance comes from the official kernel docs on configuration targets, Kbuild environment variables, external modules, and reproducible builds. citeturn12view3turn12view0turn12view1turn12view4turn12view5turn16view0turn16view1turn16view3turn17view0turn17view1turn17view2turn17view3turn17view4turn17view7turn17view8turn17view5turn17view6turn21view0turn9view1turn22search0turn22search4turn11search0
|
||||||
|
|
||||||
|
Open questions and limitations: I did not pin one exact final base-image digest because you did not specify one exact image tag policy; the code uses placeholders for digest pinning. I also did not assume one exact ASK dependency inventory beyond what is visible in the supplied tarball: the report covers the dependencies the uploaded ASK `Makefile` demonstrably fetches (`fmlib`, `fmc`, `libnfnetlink`, `libnetfilter_conntrack`, and practically `libcli`). If your unrevealed local patches or downstream packaging add more source fetches, those need the same vendoring treatment.
|
||||||
Reference in New Issue
Block a user