ASK research docs
This commit is contained in:
536
docs/ask-deepresearch.md
Normal file
536
docs/ask-deepresearch.md
Normal file
@@ -0,0 +1,536 @@
|
|||||||
|
# Reproducible Container Build System for ASK Tarball Sources
|
||||||
|
|
||||||
|
## Executive summary
|
||||||
|
|
||||||
|
The supplied ASK archive is buildable in containers, but in its current form it is not hermetic or reproducible enough for serious CI use. Inspection of the archive shows a Debian-oriented arm64 build that still fetches sources during the build, assumes a GNU/glibc cross toolchain, and expects a separate kernel tree for module builds. That combination is fine for local hacking and bad for supply-chain control.
|
||||||
|
|
||||||
|
The clean fix is straightforward: close the build over a `packages/` directory that contains **every** source archive the build needs, pass only archive **paths** as build arguments, verify checksums, replace every `git clone` and `wget` step with tarball extraction plus patch application, and use a multi-stage Dockerfile that exports only artifacts. Docker’s own docs explicitly support build arguments for parameterizing instructions, recommend multi-stage builds to keep final outputs small, and provide local exporters so you can pull files out of a build without shipping the entire toolchain image. citeturn9view0turn9view3turn9view5turn9view6turn23view0
|
||||||
|
|
||||||
|
The bad news is that a pure Alpine conversion is not free. Alpine uses musl, not glibc, and that changes ABI and runtime behavior. If the final ASK userspace binaries must run unchanged on a glibc-based target, Alpine-only is the wrong default unless you deliberately introduce a glibc sysroot, static-link what you can, or use compatibility mechanisms such as `gcompat` for simple cases. Alpine’s own docs and the musl docs both make that point indirectly but unmistakably. citeturn13view2turn13view3turn14view0turn13view1turn13view0
|
||||||
|
|
||||||
|
## What the supplied ASK archive implies
|
||||||
|
|
||||||
|
Inspection of the supplied ASK tarball shows a C and kernel-module build for entity["company","NXP","semiconductor company"] Layerscape hardware. The root Makefile already pins `fmlib` and `fmc` to `lf-6.12.49-2.2.0`, downloads `libnfnetlink-1.0.2` and `libnetfilter_conntrack-1.1.0` during the build, and assumes a separate kernel source tree through `KDIR`. The included `build/setup.sh` is Debian host bootstrap logic; inside a container, that script should be retired, not executed.
|
||||||
|
|
||||||
|
For Alpine, the dependency picture is mixed but manageable. Official Alpine packages exist for `libmnl-dev`, `libpcap-dev`, `libxml2-dev`, and `tclap-dev`, with `tclap-dev` living in `community`. The nuisance dependency is `libcli`: Alpine’s package index only shows `libcli` in edge/testing, not as a stable v3.22 package, while upstream libcli’s own README documents a plain `make && make install` flow into `/usr/local/lib`. For this project, vendoring libcli as a tarball and building it in a helper stage is the sane choice; enabling edge/testing for one leaf dependency is not. citeturn4view0turn4view1turn14view2turn25search0turn19view0turn24view0turn26search10
|
||||||
|
|
||||||
|
There is one more important project-specific constraint: the archive does **not** include the kernel tree that ASK’s out-of-tree modules need. So a fully reproducible module build requires one more input archive, such as `packages/linux.tar.xz`, or a deliberate decision to build only `userspace`.
|
||||||
|
|
||||||
|
## Recommended build architecture
|
||||||
|
|
||||||
|
The right shape is a **closed build context**. Put the ASK tarball, every required dependency tarball, and optionally the matching kernel source tarball in `packages/`. Keep the Docker context small with `.dockerignore`. Verify those archives with `SHA256SUMS`. Set `SOURCE_DATE_EPOCH` so timestamps stop drifting. In CI, pin the base image by digest instead of trusting a moving tag. Docker’s best-practices guide is blunt about why: tags move, digests do not, multi-stage builds slim outputs, and `.dockerignore` matters. The `SOURCE_DATE_EPOCH` spec exists precisely to keep timestamps from poisoning reproducibility. citeturn23view0turn22search2turn22search4turn22search1
|
||||||
|
|
||||||
|
For ASK specifically, the least painful Alpine route is to build natively for `linux/arm64` with Buildx rather than trying to recreate Debian’s `crossbuild-essential-arm64` model inside Alpine. Debian has a purpose-built GNU/glibc cross meta-package; Alpine’s stable equivalent is not that. Alpine’s native toolchain story is `build-base` on musl, and Docker’s multi-platform build support is exactly what makes `--platform=linux/arm64` practical here. citeturn8view7turn13view4turn14view5turn9view4
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[packages/*.tar.* in build context] --> B[buildx builder stage]
|
||||||
|
B --> C[extract ASK tarball]
|
||||||
|
B --> D[extract vendored dependency tarballs]
|
||||||
|
D --> E[patch and build fmlib, fmc, libnfnetlink, libnetfilter_conntrack, libcli]
|
||||||
|
C --> F[build ASK userspace and optionally kernel modules]
|
||||||
|
E --> F
|
||||||
|
F --> G[dist or artifact directory]
|
||||||
|
G --> H[scratch artifacts stage]
|
||||||
|
H --> I[buildx local exporter to out/ask]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Concrete implementation
|
||||||
|
|
||||||
|
The wrapper below fixes the syntax problem in the prompt. `docker buildx build` needs an explicit build context, repeated `--build-arg KEY=VALUE` flags, and a `--file` argument. The local exporter is the right default here because ASK’s real deliverable is a directory of build artifacts, not a fat runtime image. citeturn9view5turn9view6
|
||||||
|
|
||||||
|
**Recommended `.dockerignore`**
|
||||||
|
|
||||||
|
```dockerignore
|
||||||
|
**
|
||||||
|
!Makefile
|
||||||
|
!docker/**
|
||||||
|
!packages/**
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker recommends using `.dockerignore` to exclude irrelevant files from the build context, and that matters here because the whole point is to build from a tightly controlled set of source tarballs. citeturn23view0
|
||||||
|
|
||||||
|
**Host-side vendoring script**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
PACKAGES_DIR="${1:-packages}"
|
||||||
|
ASK_SRC="${ASK_SRC:-/absolute/path/to/ASK.tar.gz}"
|
||||||
|
NXP_TAG="lf-6.12.49-2.2.0"
|
||||||
|
|
||||||
|
mkdir -p "${PACKAGES_DIR}"
|
||||||
|
|
||||||
|
install -m 0644 "${ASK_SRC}" "${PACKAGES_DIR}/ASK.tar.gz"
|
||||||
|
|
||||||
|
curl -L --fail -o "${PACKAGES_DIR}/fmlib-${NXP_TAG}.tar.gz" \
|
||||||
|
"https://github.com/nxp-qoriq/fmlib/archive/refs/tags/${NXP_TAG}.tar.gz"
|
||||||
|
|
||||||
|
curl -L --fail -o "${PACKAGES_DIR}/fmc-${NXP_TAG}.tar.gz" \
|
||||||
|
"https://github.com/nxp-qoriq/fmc/archive/refs/tags/${NXP_TAG}.tar.gz"
|
||||||
|
|
||||||
|
curl -L --fail -o "${PACKAGES_DIR}/libnfnetlink-1.0.2.tar.bz2" \
|
||||||
|
"https://www.netfilter.org/projects/libnfnetlink/files/libnfnetlink-1.0.2.tar.bz2"
|
||||||
|
|
||||||
|
curl -L --fail -o "${PACKAGES_DIR}/libnetfilter_conntrack-1.1.0.tar.xz" \
|
||||||
|
"https://www.netfilter.org/projects/libnetfilter_conntrack/files/libnetfilter_conntrack-1.1.0.tar.xz"
|
||||||
|
|
||||||
|
curl -L --fail -o "${PACKAGES_DIR}/libcli-1.10.7.tar.gz" \
|
||||||
|
"https://github.com/dparrish/libcli/archive/refs/tags/V1.10.7.tar.gz"
|
||||||
|
|
||||||
|
# Optional: add a matching kernel source archive for full module builds.
|
||||||
|
# install -m 0644 /path/to/linux.tar.xz "${PACKAGES_DIR}/linux.tar.xz"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "${PACKAGES_DIR}"
|
||||||
|
find . -maxdepth 1 -type f \
|
||||||
|
\( -name '*.tar.gz' -o -name '*.tar.xz' -o -name '*.tar.bz2' \) \
|
||||||
|
-print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
If local repacking is part of your process, normalize ownership, ordering, and mtimes and set `SOURCE_DATE_EPOCH`; otherwise you are leaking host-local timestamps into your supposedly reproducible input archive. citeturn22search1turn22search4
|
||||||
|
|
||||||
|
**Root wrapper `Makefile`**
|
||||||
|
|
||||||
|
```make
|
||||||
|
.RECIPEPREFIX := >
|
||||||
|
|
||||||
|
PLATFORM ?= linux/arm64
|
||||||
|
ALPINE_VERSION ?= 3.22
|
||||||
|
BUILD_TARGET ?= dist
|
||||||
|
OUT_DIR ?= out/ask
|
||||||
|
IMAGE ?= ask-build:local
|
||||||
|
|
||||||
|
ASK_TAR ?= packages/ASK.tar.gz
|
||||||
|
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
|
||||||
|
KERNEL_TAR ?= packages/linux.tar.xz
|
||||||
|
|
||||||
|
SOURCE_DATE_EPOCH ?= 1704067200
|
||||||
|
JOBS ?= 0
|
||||||
|
USERSPACE_CFLAGS ?=
|
||||||
|
USERSPACE_LDFLAGS ?=
|
||||||
|
|
||||||
|
.PHONY: ASK ASK_IMAGE
|
||||||
|
|
||||||
|
ASK:
|
||||||
|
> docker buildx build \
|
||||||
|
> --platform="$(PLATFORM)" \
|
||||||
|
> --file docker/ask.Dockerfile \
|
||||||
|
> --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 "BUILD_TARGET=$(BUILD_TARGET)" \
|
||||||
|
> --build-arg "SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)" \
|
||||||
|
> --build-arg "JOBS=$(JOBS)" \
|
||||||
|
> --build-arg "USERSPACE_CFLAGS=$(USERSPACE_CFLAGS)" \
|
||||||
|
> --build-arg "USERSPACE_LDFLAGS=$(USERSPACE_LDFLAGS)" \
|
||||||
|
> --target artifacts \
|
||||||
|
> --output "type=local,dest=$(OUT_DIR)" \
|
||||||
|
> .
|
||||||
|
|
||||||
|
ASK_IMAGE:
|
||||||
|
> docker buildx build \
|
||||||
|
> --platform="$(PLATFORM)" \
|
||||||
|
> --file docker/ask.Dockerfile \
|
||||||
|
> --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 "BUILD_TARGET=$(BUILD_TARGET)" \
|
||||||
|
> --build-arg "SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)" \
|
||||||
|
> --build-arg "JOBS=$(JOBS)" \
|
||||||
|
> --build-arg "USERSPACE_CFLAGS=$(USERSPACE_CFLAGS)" \
|
||||||
|
> --build-arg "USERSPACE_LDFLAGS=$(USERSPACE_LDFLAGS)" \
|
||||||
|
> --load \
|
||||||
|
> --tag "$(IMAGE)" \
|
||||||
|
> .
|
||||||
|
```
|
||||||
|
|
||||||
|
**`docker/overrides/toolchain.mk`**
|
||||||
|
|
||||||
|
```make
|
||||||
|
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)
|
||||||
|
|
||||||
|
HOST ?= $(shell $(CC) -dumpmachine 2>/dev/null || echo aarch64-alpine-linux-musl)
|
||||||
|
KDIR ?= /opt/kernel
|
||||||
|
```
|
||||||
|
|
||||||
|
**`docker/overrides/Makefile`**
|
||||||
|
|
||||||
|
This override intentionally removes the upstream host-setup/network-fetch behavior and keeps only the build targets that matter for containerized CI.
|
||||||
|
|
||||||
|
```make
|
||||||
|
.RECIPEPREFIX := >
|
||||||
|
|
||||||
|
include build/toolchain.mk
|
||||||
|
include build/sources.mk
|
||||||
|
|
||||||
|
DIST := $(CURDIR)/dist
|
||||||
|
SRCDIR := $(CURDIR)/sources
|
||||||
|
PATCHES := $(CURDIR)/patches
|
||||||
|
VENDOR_DIR ?= /vendor/packages
|
||||||
|
HOST ?= $(shell $(CC) -dumpmachine 2>/dev/null || echo aarch64-alpine-linux-musl)
|
||||||
|
JOBS ?= $(shell getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||||
|
|
||||||
|
USERSPACE_CFLAGS ?=
|
||||||
|
USERSPACE_LDFLAGS ?=
|
||||||
|
|
||||||
|
FMLIB_DIR := $(SRCDIR)/fmlib
|
||||||
|
FMC_DIR := $(SRCDIR)/fmc/source
|
||||||
|
LIBFCI_DIR := $(CURDIR)/fci/lib
|
||||||
|
SYSROOT := $(SRCDIR)/sysroot
|
||||||
|
ABM_DIR := $(CURDIR)/auto_bridge
|
||||||
|
|
||||||
|
FMLIB_TAR ?= $(VENDOR_DIR)/fmlib-$(NXP_TAG).tar.gz
|
||||||
|
FMC_TAR ?= $(VENDOR_DIR)/fmc-$(NXP_TAG).tar.gz
|
||||||
|
LIBNFNETLINK_TAR ?= $(VENDOR_DIR)/libnfnetlink-$(LIBNFNETLINK_VER).tar.bz2
|
||||||
|
LIBNFCT_TAR ?= $(VENDOR_DIR)/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)
|
||||||
|
|
||||||
|
S := $(SRCDIR)/.stamps
|
||||||
|
$(shell mkdir -p $(S))
|
||||||
|
|
||||||
|
define extract_strip1
|
||||||
|
rm -rf $(2) && mkdir -p $(2) && tar -xf $(1) --strip-components=1 -C $(2)
|
||||||
|
endef
|
||||||
|
|
||||||
|
.PHONY: all sources modules userspace cdx fci auto_bridge fmc cmm dpa_app dist clean clean-all
|
||||||
|
|
||||||
|
all: modules userspace
|
||||||
|
sources: $(S)/fmlib $(S)/fmc $(S)/libfci $(S)/libnfnetlink $(S)/libnfct
|
||||||
|
|
||||||
|
$(S)/fmlib:
|
||||||
|
> @echo "==> fmlib: extract, patch, build"
|
||||||
|
> test -f $(FMLIB_TAR)
|
||||||
|
> $(call extract_strip1,$(FMLIB_TAR),$(FMLIB_DIR))
|
||||||
|
> cd $(FMLIB_DIR) && patch -p1 -i $(PATCHES)/fmlib/01-mono-ask-extensions.patch
|
||||||
|
> $(MAKE) -j$(JOBS) -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:
|
||||||
|
> @echo "==> fmc: extract, patch, build"
|
||||||
|
> test -f $(FMC_TAR)
|
||||||
|
> rm -rf $(SRCDIR)/fmc
|
||||||
|
> mkdir -p $(SRCDIR)/fmc
|
||||||
|
> tar -xf $(FMC_TAR) -C $(SRCDIR)/fmc --strip-components=1
|
||||||
|
> cd $(FMC_DIR) && patch -p1 -i $(PATCHES)/fmc/01-mono-ask-extensions.patch
|
||||||
|
> $(MAKE) -j$(JOBS) -C $(FMC_DIR) \
|
||||||
|
> CC=$(CC) \
|
||||||
|
> CFLAGS="$(USERSPACE_CFLAGS)" \
|
||||||
|
> LDFLAGS="$(USERSPACE_LDFLAGS)" \
|
||||||
|
> TCLAP_HEADER_PATH=/usr/include \
|
||||||
|
> LIBXML2_HEADER_PATH=/usr/include/libxml2 \
|
||||||
|
> SYSROOT=$(SYSROOT)
|
||||||
|
> touch $@
|
||||||
|
|
||||||
|
$(S)/libnfnetlink:
|
||||||
|
> @echo "==> libnfnetlink: extract, patch, install into sysroot"
|
||||||
|
> test -f $(LIBNFNETLINK_TAR)
|
||||||
|
> rm -rf $(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER)
|
||||||
|
> mkdir -p $(SRCDIR) $(SYSROOT)
|
||||||
|
> tar -xf $(LIBNFNETLINK_TAR) -C $(SRCDIR)
|
||||||
|
> cd $(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER) && \
|
||||||
|
> patch -p1 -i $(PATCHES)/libnfnetlink/0001-libnfnetlink-fix-for-ARM64-and-clang.patch && \
|
||||||
|
> ./configure --host=$(HOST) --prefix=$(SYSROOT) --disable-shared --enable-static && \
|
||||||
|
> $(MAKE) -j$(JOBS) && $(MAKE) install
|
||||||
|
> touch $@
|
||||||
|
|
||||||
|
$(S)/libnfct: $(S)/libnfnetlink
|
||||||
|
> @echo "==> libnetfilter_conntrack: extract, patch, install into sysroot"
|
||||||
|
> test -f $(LIBNFCT_TAR)
|
||||||
|
> rm -rf $(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER)
|
||||||
|
> mkdir -p $(SRCDIR) $(SYSROOT)
|
||||||
|
> tar -xf $(LIBNFCT_TAR) -C $(SRCDIR)
|
||||||
|
> cd $(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER) && \
|
||||||
|
> patch -p1 -i $(PATCHES)/libnetfilter_conntrack/0001-libnetfilter_conntrack-fix-for-ARM64-and-clang.patch && \
|
||||||
|
> ./configure --host=$(HOST) --prefix=$(SYSROOT) --with-libnfnetlink=$(SYSROOT) --disable-shared --enable-static && \
|
||||||
|
> $(MAKE) -j$(JOBS) && $(MAKE) install
|
||||||
|
> touch $@
|
||||||
|
|
||||||
|
$(S)/libfci: $(S)/fmlib $(S)/libnfnetlink $(S)/libnfct
|
||||||
|
> @echo "==> libfci"
|
||||||
|
> $(MAKE) -C fci/lib CC=$(CC) AR=$(AR)
|
||||||
|
> touch $@
|
||||||
|
|
||||||
|
modules: cdx fci auto_bridge
|
||||||
|
userspace: fmc cmm dpa_app
|
||||||
|
|
||||||
|
cdx:
|
||||||
|
> $(MAKE) -C cdx $(CDX_ARGS)
|
||||||
|
|
||||||
|
fci: $(S)/libfci
|
||||||
|
> $(MAKE) -C fci $(FCI_ARGS)
|
||||||
|
|
||||||
|
auto_bridge:
|
||||||
|
> $(MAKE) -C auto_bridge $(ABM_ARGS)
|
||||||
|
|
||||||
|
fmc: $(S)/fmc
|
||||||
|
|
||||||
|
cmm: $(S)/libfci $(S)/libnfnetlink $(S)/libnfct
|
||||||
|
> $(MAKE) -C cmm \
|
||||||
|
> CC=$(CC) \
|
||||||
|
> LIBFCI_DIR=$(LIBFCI_DIR) \
|
||||||
|
> ABM_DIR=$(ABM_DIR) \
|
||||||
|
> SYSROOT=$(SYSROOT) \
|
||||||
|
> CFLAGS="$(USERSPACE_CFLAGS)" \
|
||||||
|
> LDFLAGS="$(USERSPACE_LDFLAGS)"
|
||||||
|
|
||||||
|
dpa_app: $(S)/fmc
|
||||||
|
> $(MAKE) -C dpa_app \
|
||||||
|
> CC=$(CC) \
|
||||||
|
> CFLAGS="-DDPAA_DEBUG_ENABLE -DNCSW_LINUX $(USERSPACE_CFLAGS) -I$(FMC_DIR) -I$(FMC_DIR)/inc/integrations/drivers/netcfg" \
|
||||||
|
> LDFLAGS="-lpthread -lcli -lxml2 -lstdc++ $(USERSPACE_LDFLAGS)"
|
||||||
|
|
||||||
|
dist: all
|
||||||
|
> rm -rf $(DIST)
|
||||||
|
> mkdir -p $(DIST)
|
||||||
|
> cp -a $(CURDIR)/cmm/src/cmm $(DIST)/
|
||||||
|
> cp -a $(CURDIR)/dpa_app/dpa_app $(DIST)/
|
||||||
|
> cp -a $(CURDIR)/cdx/cdx.ko $(DIST)/
|
||||||
|
> cp -a $(CURDIR)/fci/fci.ko $(DIST)/
|
||||||
|
> cp -a $(CURDIR)/auto_bridge/auto_bridge.ko $(DIST)/
|
||||||
|
> cp -a $(SRCDIR)/fmc/source/fmc $(DIST)/
|
||||||
|
> cp -a scripts/init/* $(DIST)/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
> $(MAKE) -C auto_bridge clean || true
|
||||||
|
> $(MAKE) -C cdx clean || true
|
||||||
|
> $(MAKE) -C cmm clean || true
|
||||||
|
> $(MAKE) -C dpa_app clean || true
|
||||||
|
> $(MAKE) -C fci clean || true
|
||||||
|
> $(MAKE) -C fci/lib clean || true
|
||||||
|
> rm -rf $(DIST)
|
||||||
|
|
||||||
|
clean-all: clean
|
||||||
|
> rm -rf $(SRCDIR)
|
||||||
|
```
|
||||||
|
|
||||||
|
**`docker/ask.Dockerfile`**
|
||||||
|
|
||||||
|
This Dockerfile deliberately enables Alpine `community`, because Alpine documents that `community` is not enabled by default in many configurations, and ASK needs `tclap-dev`, which is in that repository. citeturn26search10turn25search0
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
ARG ALPINE_VERSION=3.22
|
||||||
|
|
||||||
|
FROM alpine:${ALPINE_VERSION} AS builder
|
||||||
|
ARG ALPINE_VERSION
|
||||||
|
|
||||||
|
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=packages/linux.tar.xz
|
||||||
|
ARG BUILD_TARGET=dist
|
||||||
|
ARG SOURCE_DATE_EPOCH=1704067200
|
||||||
|
ARG JOBS=0
|
||||||
|
ARG USERSPACE_CFLAGS=
|
||||||
|
ARG USERSPACE_LDFLAGS=
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
printf '%s\n' \
|
||||||
|
"https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/main" \
|
||||||
|
"https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/community" \
|
||||||
|
> /etc/apk/repositories; \
|
||||||
|
apk add --no-cache \
|
||||||
|
bash \
|
||||||
|
bc \
|
||||||
|
bison \
|
||||||
|
build-base \
|
||||||
|
bzip2 \
|
||||||
|
coreutils \
|
||||||
|
file \
|
||||||
|
findutils \
|
||||||
|
flex \
|
||||||
|
gawk \
|
||||||
|
libmnl-dev \
|
||||||
|
libpcap-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
linux-headers \
|
||||||
|
openssl \
|
||||||
|
openssl-dev \
|
||||||
|
patch \
|
||||||
|
perl \
|
||||||
|
pkgconf \
|
||||||
|
python3 \
|
||||||
|
tar \
|
||||||
|
tclap-dev \
|
||||||
|
xz \
|
||||||
|
zlib-dev
|
||||||
|
|
||||||
|
COPY packages/ /vendor/packages/
|
||||||
|
COPY docker/overrides/ /docker-overrides/
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
if [ -f /vendor/packages/SHA256SUMS ]; then \
|
||||||
|
cd /vendor/packages && sha256sum -c SHA256SUMS; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
test -f "/vendor/${ASK_TAR}"; \
|
||||||
|
mkdir -p /work/src; \
|
||||||
|
tar -xf "/vendor/${ASK_TAR}" -C /work/src; \
|
||||||
|
test -d /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; \
|
||||||
|
if [ ! -f /work/src/ASK/cmm/src/version.h ]; then \
|
||||||
|
printf '/* Auto-generated */\n#ifndef VERSION_H\n#define VERSION_H\n#define CMM_VERSION "%s"\n#endif\n' "tarball" > /work/src/ASK/cmm/src/version.h; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
test -f "/vendor/${LIBCLI_TAR}"; \
|
||||||
|
mkdir -p /tmp/libcli; \
|
||||||
|
tar -xf "/vendor/${LIBCLI_TAR}" --strip-components=1 -C /tmp/libcli; \
|
||||||
|
make -C /tmp/libcli; \
|
||||||
|
make -C /tmp/libcli install; \
|
||||||
|
rm -rf /tmp/libcli
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
case "${BUILD_TARGET}" in \
|
||||||
|
all|modules|kernel|dist) \
|
||||||
|
test -f "/vendor/${KERNEL_TAR}" || { echo "KERNEL_TAR is required for BUILD_TARGET=${BUILD_TARGET}"; exit 2; }; \
|
||||||
|
mkdir -p /opt/kernel; \
|
||||||
|
tar -xf "/vendor/${KERNEL_TAR}" --strip-components=1 -C /opt/kernel; \
|
||||||
|
;; \
|
||||||
|
*) : ;; \
|
||||||
|
esac
|
||||||
|
|
||||||
|
ENV LC_ALL=C
|
||||||
|
ENV TZ=UTC
|
||||||
|
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
if [ "${JOBS}" = "0" ]; then JOBS="$(getconf _NPROCESSORS_ONLN)"; fi; \
|
||||||
|
make -C /work/src/ASK \
|
||||||
|
VENDOR_DIR=/vendor/packages \
|
||||||
|
FMLIB_TAR="/vendor/${FMLIB_TAR}" \
|
||||||
|
FMC_TAR="/vendor/${FMC_TAR}" \
|
||||||
|
LIBNFNETLINK_TAR="/vendor/${LIBNFNETLINK_TAR}" \
|
||||||
|
LIBNFCT_TAR="/vendor/${LIBNFCT_TAR}" \
|
||||||
|
KDIR=/opt/kernel \
|
||||||
|
JOBS="${JOBS}" \
|
||||||
|
USERSPACE_CFLAGS="${USERSPACE_CFLAGS}" \
|
||||||
|
USERSPACE_LDFLAGS="${USERSPACE_LDFLAGS}" \
|
||||||
|
"${BUILD_TARGET}"
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
mkdir -p /out; \
|
||||||
|
if [ -d /work/src/ASK/dist ]; then \
|
||||||
|
cp -a /work/src/ASK/dist/. /out/; \
|
||||||
|
else \
|
||||||
|
[ -f /work/src/ASK/cmm/src/cmm ] && cp /work/src/ASK/cmm/src/cmm /out/ || true; \
|
||||||
|
[ -f /work/src/ASK/dpa_app/dpa_app ] && cp /work/src/ASK/dpa_app/dpa_app /out/ || true; \
|
||||||
|
[ -f /work/src/ASK/sources/fmc/source/fmc ] && cp /work/src/ASK/sources/fmc/source/fmc /out/ || true; \
|
||||||
|
[ -f /work/src/ASK/cdx/cdx.ko ] && cp /work/src/ASK/cdx/cdx.ko /out/ || true; \
|
||||||
|
[ -f /work/src/ASK/fci/fci.ko ] && cp /work/src/ASK/fci/fci.ko /out/ || true; \
|
||||||
|
[ -f /work/src/ASK/auto_bridge/auto_bridge.ko ] && cp /work/src/ASK/auto_bridge/auto_bridge.ko /out/ || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
FROM scratch AS artifacts
|
||||||
|
COPY --from=builder /out/ /
|
||||||
|
```
|
||||||
|
|
||||||
|
The Dockerfile above uses `COPY packages/` plus explicit `tar -xf` because that is the less magical pattern once you have **multiple optional archives** such as `KERNEL_TAR`, checksum verification, and tarballs whose extracted top-level directory names you do not want to trust blindly. Docker’s docs are clear that `ADD` can auto-unpack local tar archives, but they are equally clear that `COPY` is the basic, explicit copy primitive. In practice, explicit wins here. citeturn23view0turn10view0turn10view1turn10view4
|
||||||
|
|
||||||
|
The normal invocation is `make ASK BUILD_TARGET=dist KERNEL_TAR=packages/linux.tar.xz` for a full module build, or `make ASK BUILD_TARGET=userspace` when only the source/dependency tarballs are available and the kernel tree is not. The artifact-export path is the better default; the image-loading target is only useful if you actually want a local OCI image as an intermediate. citeturn9view5turn9view6
|
||||||
|
|
||||||
|
**Short `ARG + ADD` pattern and a secret-safe variant**
|
||||||
|
|
||||||
|
If all you need is the shortest path-based pattern, Docker supports it:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
ARG ASK_TAR=packages/ASK.tar.gz
|
||||||
|
ADD ${ASK_TAR} /work/src/
|
||||||
|
```
|
||||||
|
|
||||||
|
That works because local `ADD` sources are relative to the build context and local tar archives are unpacked automatically. But Docker also warns that build args are not secret channels. So if the archive is confidential, use a BuildKit secret mount instead:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# build command:
|
||||||
|
# docker buildx build --secret id=ask,src=packages/ASK.tar.gz -f docker/ask.Dockerfile .
|
||||||
|
|
||||||
|
RUN --mount=type=secret,id=ask,target=/tmp/ASK.tar.gz \
|
||||||
|
mkdir -p /work/src && tar -xf /tmp/ASK.tar.gz -C /work/src
|
||||||
|
```
|
||||||
|
|
||||||
|
That is the secure answer. Build args should carry a **path or selector**, not secret bytes. citeturn10view0turn10view2turn9view0turn9view1
|
||||||
|
|
||||||
|
## Debian and Alpine package mapping
|
||||||
|
|
||||||
|
The table below is the practical package/command map for the common build tools requested, plus the one ASK-specific exception that matters in real life.
|
||||||
|
|
||||||
|
| Need | Debian Trixie | Alpine 3.22 | Practical note |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Meta toolchain bundle | `apt-get install -y build-essential` | `apk add --no-cache build-base` | Rough equivalents for native builds |
|
||||||
|
| arm64 cross bundle | `apt-get install -y crossbuild-essential-arm64` | no single stable equivalent | On Alpine, use `buildx --platform=linux/arm64` or bring your own GNU sysroot |
|
||||||
|
| C compiler | `apt-get install -y gcc` | `apk add --no-cache gcc` | `build-base` already includes it |
|
||||||
|
| C++ compiler | `apt-get install -y g++` | `apk add --no-cache g++` | `build-base` already includes it |
|
||||||
|
| `make` | `apt-get install -y make` | `apk add --no-cache make` | `build-base` already includes it |
|
||||||
|
| CMake | `apt-get install -y cmake` | `apk add --no-cache cmake` | Direct name match |
|
||||||
|
| pkg-config tooling | `apt-get install -y pkgconf` | `apk add --no-cache pkgconf` | On Debian, `pkg-config` is now effectively the pkgconf world too |
|
||||||
|
| OpenSSL headers/libs | `apt-get install -y libssl-dev` | `apk add --no-cache openssl-dev` | Direct development-package equivalents |
|
||||||
|
| zlib headers/libs | `apt-get install -y zlib1g-dev` | `apk add --no-cache zlib-dev` | Direct development-package equivalents |
|
||||||
|
| C++ runtime | runtime usually via `libstdc++6`; headers via `g++`/`libstdc++-14-dev` | `apk add --no-cache libstdc++` | Alpine splits runtime from the compiler meta-package |
|
||||||
|
| libc headers | `apt-get install -y libc6-dev` | `apk add --no-cache musl-dev` | Alpine exposes `libc-dev` through musl |
|
||||||
|
| ASK-specific TCLAP | `apt-get install -y libtclap-dev` | `apk add --no-cache tclap-dev` | Needs Alpine `community` |
|
||||||
|
| ASK-specific libcli | `apt-get install -y libcli-dev` | vendor tarball | Stable Alpine v3.22 does not have a normal `libcli-dev` package |
|
||||||
|
|
||||||
|
These mappings are grounded in Debian’s package pages and Alpine’s official package index. The important details are: `build-base` bundles `gcc`, `g++`, `make`, `patch`, and `libc-dev`; `musl-dev` provides the `libc-dev` role on Alpine; `pkgconf` is the package you actually install for pkg-config tooling; and Debian’s `crossbuild-essential-arm64` does not have a one-package Alpine twin. citeturn5search0turn8view7turn7view0turn8view0turn8view2turn5search1turn5search9turn8view3turn8view4turn8view5turn8view6turn14view5turn13view4turn4view2turn4view3turn14view1turn14view3turn14view4
|
||||||
|
|
||||||
|
For ASK specifically, the two Alpine deltas worth remembering are `tclap-dev` in `community` and the absence of stable `libcli`, which is why the helper-stage libcli build is worth doing. citeturn25search0turn19view0turn24view0turn26search10
|
||||||
|
|
||||||
|
## Musl compatibility, troubleshooting, and assumptions
|
||||||
|
|
||||||
|
This is where most Alpine ports actually fail.
|
||||||
|
|
||||||
|
- **Do not trust glibc-specific `#ifdef` logic.** The musl FAQ explicitly calls out hardcoded glibc assumptions and wrong `__GLIBC__` checks as common failure causes. Fix the preprocessor logic first, not last. The same FAQ also calls out GNU `getopt` expectations, iconv/UCS2 assumptions, `off_t` width assumptions, and too-small default thread stacks as recurring causes of runtime failures. citeturn13view1
|
||||||
|
|
||||||
|
- **Musl locale behavior is not glibc locale behavior.** Alpine’s own musl page says musl does not implement most of the locale features that glibc implements. If your code relies on glibc locale internals, stop pretending Alpine is a drop-in replacement. citeturn13view2
|
||||||
|
|
||||||
|
- **Plugin unload/reload semantics differ.** musl’s dynamic loader keeps libraries loaded for the life of the process and treats `dlclose` as a no-op. If your software depends on glibc-style unload/reload behavior, that is a real portability bug, not a packaging bug. citeturn13view0
|
||||||
|
|
||||||
|
- **Static versus dynamic needs a hard-headed choice.** `-static-libgcc` and `-static-libstdc++` are good pragmatic flags when the problem is just the GCC/C++ runtime. They are not magic. Full static linking only works cleanly when every dependency is available as a static archive and your deployment model actually benefits from it.
|
||||||
|
|
||||||
|
- **`gcompat` is for simple runtime cases, not for wishful thinking.** Alpine documents `gcompat` as a glibc compatibility layer for musl systems, and the package provides the relevant `libc6-compat`/loader compatibility pieces. For more complex glibc applications, Alpine’s own docs point you toward a glibc chroot/container instead. citeturn13view3turn14view0
|
||||||
|
|
||||||
|
- **ASK-specific practical fix:** because ASK’s source already contains at least one musl-friendly guard around `execinfo`/`backtrace`, a native Alpine build is plausible for the build itself. The unresolved question is not “can it compile?” It is “what libc must the resulting userspace binaries target on the final device?”
|
||||||
|
|
||||||
|
Where the upstream project is genuinely unspecified, the pattern stays the same and only the builder image family changes: C/C++/Make/CMake/autotools usually start from `alpine`, Go from `golang:*-alpine`, Python from `python:*-alpine`, and Node from `node:*-alpine`, all with the same multi-stage split. The exception rule is blunt: the moment the project depends on glibc-only binaries, ABI-sensitive vendor libraries, or packaging ecosystems that assume glibc, switch back to a glibc builder instead of burning time fighting musl. Docker’s own guidance is to use trusted minimal images and multi-stage builds; Alpine’s own docs make clear that musl/glibc compatibility is extra engineering work, not something the base image solves for you. citeturn23view0turn9view3turn13view2turn13view3
|
||||||
|
|
||||||
|
**Open questions / limitations**
|
||||||
|
|
||||||
|
The supplied ASK tarball did not include the matching kernel source tree, so a fully reproducible module build still needs one more input archive. The deploy target’s libc requirement was also unspecified; that matters a lot, because a musl-linked `dpa_app` or `cmm` is not automatically a drop-in replacement for a glibc target userland. Finally, no existing Debian Dockerfiles were present in the supplied archive, so the Alpine conversion above is a concrete replacement design derived from the archive’s real build logic, not a literal line-by-line rewrite of preexisting Dockerfiles.
|
||||||
Reference in New Issue
Block a user