Adopt BSD make ports framework

This commit is contained in:
Michal S.
2026-07-30 22:39:06 +01:00
parent 6dfbfa1938
commit 89399dc6b4
13 changed files with 433 additions and 36 deletions
+7
View File
@@ -1,4 +1,11 @@
BMAKE ?= bmake
.PHONY: check
check:
./tests/check-layout.sh
@if command -v "$(BMAKE)" >/dev/null 2>&1; then \
"$(BMAKE)" -f tests/framework.mk check; \
else \
printf '%s\n' "bmake parse check skipped: bmake is unavailable"; \
fi

Before

Width:  |  Height:  |  Size: 47 B

After

Width:  |  Height:  |  Size: 239 B

+20 -7
View File
@@ -1,14 +1,14 @@
# MOUSE ports
This repository is the independently versioned source-port collection for
MOUSE. It contains third-party recipes and patches only; the `mouse-port`
implementation, ports contract, base-system manifest, and release integration
belong to `mouse-src`.
MOUSE. Each port is a BSD make project, while shared phase and quality-control
machinery lives under `ports/Mk`.
Each port lives below `ports/NAME` and follows the recipe contract shipped by
the compatible MOUSE base release. A MOUSE release pins one tested commit from
this repository, while installed systems may move to later compatible ports
revisions without replacing the base system.
Each port lives below `ports/category/name` and contains a `Makefile`,
`distinfo`, and `pkg-plist`, plus optional patches and service definitions
under `files`. A MOUSE release pins one tested commit from this repository,
while installed systems may move to later compatible ports revisions without
replacing the base system.
The adversarial and demonstration recipes used by the base-system acceptance
suite are deliberately not part of this collection. They live in
@@ -19,3 +19,16 @@ Run the collection checks with:
```sh
make check
```
On MOUSE, a port's public interface is:
```sh
make fetch
make checksum
make build
make stage
make package
make install
make deinstall
make clean
```
+6 -2
View File
@@ -1,4 +1,8 @@
# Infrastructure
Collection-wide generation, lint, and publication tooling belongs here. Base
image assembly and the `mouse-port` implementation remain in `mouse-src`.
Collection-wide generation, lint, and publication tooling belongs here. The
live framework is `ports/Mk/mouse.port.mk`, with narrow shell helpers in
`ports/Mk/Scripts`.
Base-manifest enforcement and transactional package registration remain a
private base-system responsibility in `mouse-src`.
+9
View File
@@ -0,0 +1,9 @@
.PHONY: identity list
identity:
@. /etc/mouse-release; \
revision=$$(cat /usr/share/mouse/ports.revision); \
printf '%s\n' "$$PRETTY_NAME" "ports $$revision"
list:
@/usr/libexec/mouse-package list
+56
View File
@@ -0,0 +1,56 @@
#!/bin/sh
set -eu
if [ "$#" -ne 3 ]; then
printf '%s\n' "usage: check-plist.sh STAGEDIR PREFIX PLIST" >&2
exit 2
fi
stage=$1
prefix=$2
plist=$3
[ -f "$plist" ] || {
printf '%s\n' "missing packing list: $plist" >&2
exit 1
}
normalize() {
case "$1" in
/*) printf '%s\n' "$1" ;;
*) printf '%s/%s\n' "$prefix" "$1" ;;
esac
}
while IFS= read -r entry || [ -n "$entry" ]; do
case "$entry" in
""|\#*) continue ;;
esac
path=$(normalize "$entry")
if [ ! -f "$stage$path" ] && [ ! -L "$stage$path" ]; then
printf '%s\n' "packing-list path was not staged: $path" >&2
exit 1
fi
done <"$plist"
(
cd "$stage"
find . -mindepth 1 \( -type f -o -type l \) -print
) |
while IFS= read -r relative; do
path=${relative#.}
found=0
while IFS= read -r entry || [ -n "$entry" ]; do
case "$entry" in
""|\#*) continue ;;
esac
normalized=$(normalize "$entry")
if [ "$normalized" = "$path" ]; then
found=1
break
fi
done <"$plist"
[ "$found" -eq 1 ] || {
printf '%s\n' "staged path is absent from packing list: $path" >&2
exit 1
}
done
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
set -eu
if [ "$#" -lt 4 ]; then
printf '%s\n' \
"usage: checksum.sh DISTDIR DISTINFO SHA256_COMMAND DISTFILE..." >&2
exit 2
fi
distdir=$1
distinfo=$2
sha256_command=$3
shift 3
[ -f "$distinfo" ] || {
printf '%s\n' "missing distinfo: $distinfo" >&2
exit 1
}
for distfile in "$@"; do
expected=$(sed -n "s/^SHA256 ($distfile) = //p" "$distinfo")
case "$expected" in
????????????????????????????????????????????????????????????????) ;;
*) printf '%s\n' "invalid or missing checksum for $distfile" >&2; exit 1 ;;
esac
case "$expected" in
*[!0-9a-f]*) printf '%s\n' "invalid checksum for $distfile" >&2; exit 1 ;;
esac
actual=$("$sha256_command" "$distdir/$distfile")
[ "$actual" = "$expected" ] || {
printf '%s\n' \
"$distfile checksum mismatch: expected $expected, got $actual" >&2
exit 1
}
done
+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh
set -eu
if [ "$#" -lt 4 ]; then
printf '%s\n' \
"usage: fetch.sh DISTDIR MASTER_SITES FETCH_COMMAND DISTFILE..." >&2
exit 2
fi
distdir=$1
master_sites=$2
fetch_command=$3
shift 3
mkdir -p "$distdir"
for distfile in "$@"; do
case "$distfile" in
""|*/*) printf '%s\n' "unsafe distfile name: $distfile" >&2; exit 1 ;;
esac
if [ ! -f "$distdir/$distfile" ]; then
"$fetch_command" "$master_sites$distfile" \
"$distdir/$distfile"
fi
done
+37
View File
@@ -0,0 +1,37 @@
#!/bin/sh
set -eu
if [ "$#" -lt 2 ]; then
printf '%s\n' \
"usage: install-depends.sh PORTSDIR PACKAGE_COMMAND DEPENDENCY..." >&2
exit 2
fi
portsdir=$1
package_command=$2
shift 2
for dependency in "$@"; do
case "$dependency" in
*:*/*) ;;
*) printf '%s\n' "invalid dependency: $dependency" >&2; exit 1 ;;
esac
name=${dependency%%:*}
origin=${dependency#*:}
category=${origin%%/*}
port=${origin#*/}
case "$origin" in
*/*/*) printf '%s\n' "invalid dependency origin: $origin" >&2; exit 1 ;;
esac
for component in "$name" "$category" "$port"; do
case "$component" in
""|[!a-z]*|*[!a-z0-9-]*)
printf '%s\n' "invalid dependency: $dependency" >&2
exit 1
;;
esac
done
if ! "$package_command" status "$name" >/dev/null 2>&1; then
make -C "$portsdir/$origin" install
fi
done
+68
View File
@@ -0,0 +1,68 @@
#!/bin/sh
set -eu
if [ "$#" -lt 9 ]; then
printf '%s\n' \
"usage: package.sh PKGDIR STAGEDIR NAME VERSION ORIGIN PLIST DISTINFO MAKEFILE PORTS_REVISION [DEPENDENCY...]" >&2
exit 2
fi
package=$1
stage=$2
name=$3
version=$4
origin=$5
plist=$6
distinfo=$7
makefile=$8
ports_revision=$9
shift 9
case "$name" in
""|[!a-z]*|*[!a-z0-9-]*) printf '%s\n' "invalid package name: $name" >&2; exit 1 ;;
esac
case "$version" in
""|*[!A-Za-z0-9._+-]*) printf '%s\n' "invalid package version: $version" >&2; exit 1 ;;
esac
rm -rf "$package"
mkdir -p "$package/root"
cp -R "$stage/." "$package/root/"
{
printf 'name=%s\n' "$name"
printf 'version=%s\n' "$version"
printf 'origin=%s\n' "$origin"
printf 'dependencies='
separator=
for dependency in "$@"; do
case "$dependency" in
*:*/*) ;;
*) printf '%s\n' "invalid dependency: $dependency" >&2; exit 1 ;;
esac
dependency_name=${dependency%%:*}
dependency_origin=${dependency#*:}
category=${dependency_origin%%/*}
dependency_port=${dependency_origin#*/}
case "$dependency_origin" in
*/*/*)
printf '%s\n' "invalid dependency origin: $dependency_origin" >&2
exit 1
;;
esac
for component in "$dependency_name" "$category" "$dependency_port"; do
case "$component" in
""|[!a-z]*|*[!a-z0-9-]*)
printf '%s\n' "invalid dependency: $dependency" >&2
exit 1
;;
esac
done
printf '%s%s' "$separator" "$dependency_name"
separator=" "
done
printf '\n'
} >"$package/+MANIFEST"
cp "$plist" "$package/+PLIST"
cp "$distinfo" "$package/+DISTINFO"
cp "$makefile" "$package/+BUILD"
cp "$ports_revision" "$package/+PORTS_REVISION"
+134
View File
@@ -0,0 +1,134 @@
# Common MOUSE ports framework. Individual ports define metadata and any
# port-specific do-build or do-install targets before including this file.
.if !defined(PORTNAME) || empty(PORTNAME)
.error PORTNAME is required
.endif
.if !defined(PORTVERSION) || empty(PORTVERSION)
.error PORTVERSION is required
.endif
.if !defined(CATEGORIES) || empty(CATEGORIES)
.error CATEGORIES is required
.endif
.if !defined(MASTER_SITES) || empty(MASTER_SITES)
.error MASTER_SITES is required
.endif
.if !defined(DISTFILES) || empty(DISTFILES)
.error DISTFILES is required
.endif
.if !defined(PORTSDIR)
PORTSDIR:= ${.PARSEDIR:H:tA}
.endif
PORTDIR?= ${.CURDIR}
PORTORIGIN?= ${CATEGORIES}/${PORTNAME}
PREFIX?= /usr/local
DISTDIR?= /var/cache/mouse/ports/distfiles
WRKDIR?= /var/tmp/mouse-ports/${PORTNAME}-${PORTVERSION}
WRKSRC?= ${WRKDIR}/source
STAGEDIR?= ${WRKDIR}/stage
PACKAGES?= /var/cache/mouse/packages
PKGNAME?= ${PORTNAME}-${PORTVERSION}
PKGDIR?= ${PACKAGES}/${PKGNAME}
DISTINFO_FILE?= ${PORTDIR}/distinfo
PLIST?= ${PORTDIR}/pkg-plist
PORTS_REVISION_FILE?=/usr/share/mouse/ports.revision
RUN_DEPENDS?=
NO_EXTRACT?= no
EXTRACT_CMD?= /usr/bin/tar
MOUSE_PACKAGE?= /usr/libexec/mouse-package
MOUSE_FETCH?= /usr/libexec/mouse-fetch
MOUSE_SHA256?= /usr/bin/mouse-sha256
MOUSE_SH?= /bin/sh
FETCH_SCRIPT= ${PORTSDIR}/Mk/Scripts/fetch.sh
CHECKSUM_SCRIPT=${PORTSDIR}/Mk/Scripts/checksum.sh
PLIST_SCRIPT= ${PORTSDIR}/Mk/Scripts/check-plist.sh
DEPENDS_SCRIPT= ${PORTSDIR}/Mk/Scripts/install-depends.sh
PACKAGE_SCRIPT= ${PORTSDIR}/Mk/Scripts/package.sh
.PHONY: all fetch checksum extract configure build stage stage-qa package \
install deinstall status clean do-extract do-configure do-build do-install
.MAIN: all
all: build
fetch:
${MOUSE_SH} ${FETCH_SCRIPT} "${DISTDIR}" "${MASTER_SITES}" \
"${MOUSE_FETCH}" ${DISTFILES}
checksum: fetch
${MOUSE_SH} ${CHECKSUM_SCRIPT} "${DISTDIR}" "${DISTINFO_FILE}" \
"${MOUSE_SHA256}" ${DISTFILES}
extract: checksum
rm -rf "${WRKDIR}"
mkdir -p "${WRKSRC}"
.if ${NO_EXTRACT:tl} == "yes"
:
.else
@test -x "${EXTRACT_CMD}" || { \
echo "mouse.port.mk: archive extraction requires ${EXTRACT_CMD}" >&2; \
false; \
}
cd "${WRKSRC}" && "${EXTRACT_CMD}" -xf "${DISTDIR}/${DISTFILES}"
.endif
${MAKE} do-extract
configure: extract
${MAKE} do-configure
build: configure
${MAKE} do-build
stage: build
rm -rf "${STAGEDIR}"
mkdir -p "${STAGEDIR}"
${MAKE} do-install
${MAKE} stage-qa
stage-qa:
${MOUSE_SH} ${PLIST_SCRIPT} "${STAGEDIR}" "${PREFIX}" "${PLIST}"
package: stage
${MOUSE_SH} ${PACKAGE_SCRIPT} "${PKGDIR}" "${STAGEDIR}" \
"${PORTNAME}" "${PORTVERSION}" "${PORTORIGIN}" "${PLIST}" \
"${DISTINFO_FILE}" "${PORTDIR}/Makefile" \
"${PORTS_REVISION_FILE}" ${RUN_DEPENDS}
install:
${MOUSE_SH} ${DEPENDS_SCRIPT} "${PORTSDIR}" "${MOUSE_PACKAGE}" \
${RUN_DEPENDS}
${MAKE} package
${MOUSE_PACKAGE} add "${PKGDIR}"
deinstall:
${MOUSE_PACKAGE} delete "${PORTNAME}"
status:
${MOUSE_PACKAGE} status "${PORTNAME}"
clean:
rm -rf "${WRKDIR}" "${PKGDIR}"
.if !target(do-extract)
do-extract:
:
.endif
.if !target(do-configure)
do-configure:
:
.endif
.if !target(do-build)
do-build:
:
.endif
.if !target(do-install)
do-install:
@echo "mouse.port.mk: ${PORTNAME} must define do-install" >&2
@false
.endif
+9 -2
View File
@@ -1,7 +1,14 @@
# Ports collection
Each immediate child directory is one port named `[a-z][a-z0-9-]*` and contains
one `port.conf` plus its trusted `build.sh` and any port-owned patches or files.
Ports use `category/name` directories. Every ordinary port contains a
`Makefile`, `distinfo`, and `pkg-plist`; optional patches and service files live
under `files`.
Every port ends its Makefile with:
```make
.include "${.PARSEDIR}/../../Mk/mouse.port.mk"
```
The collection is intentionally empty until the first real third-party port is
accepted. Test-only recipes do not belong here.
+16 -25
View File
@@ -2,32 +2,23 @@
set -eu
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
found=0
for directory in "$root"/ports/*; do
[ -d "$directory" ] || continue
[ "${directory##*/}" != README.md ] || continue
found=1
name=${directory##*/}
case "$name" in
""|[!a-z]*|*[!a-z0-9-]*)
printf '%s\n' "invalid port directory name: $name" >&2
[ -f "$root/ports/Mk/mouse.port.mk" ]
[ -x "$root/ports/Mk/Scripts/check-plist.sh" ]
[ -x "$root/ports/Mk/Scripts/checksum.sh" ]
[ -x "$root/ports/Mk/Scripts/fetch.sh" ]
[ -x "$root/ports/Mk/Scripts/install-depends.sh" ]
[ -x "$root/ports/Mk/Scripts/package.sh" ]
find "$root/ports" -mindepth 2 -maxdepth 2 -type d \
! -path "$root/ports/Mk/*" -print |
while IFS= read -r port; do
for file in Makefile distinfo pkg-plist; do
[ -f "$port/$file" ] || {
printf '%s\n' "missing $file: $port" >&2
exit 1
;;
esac
[ -f "$directory/port.conf" ] || {
printf '%s\n' "missing recipe: ports/$name/port.conf" >&2
exit 1
}
[ -x "$directory/build.sh" ] || {
printf '%s\n' "missing executable build script: ports/$name/build.sh" >&2
exit 1
}
sh -n "$directory/build.sh"
}
done
done
if [ "$found" -eq 0 ]; then
printf '%s\n' "ports collection is empty"
else
printf '%s\n' "ports layout check passed"
fi
printf '%s\n' "ports layout checks passed"
+13
View File
@@ -0,0 +1,13 @@
PORTNAME= framework-test
PORTVERSION= 1.0.0
CATEGORIES= tests
MASTER_SITES= http://example.invalid/
DISTFILES= framework-test.c
NO_EXTRACT= yes
.include "${.PARSEDIR}/../ports/Mk/mouse.port.mk"
check:
@test "${PORTNAME}" = framework-test
@test "${PORTSDIR}" = "${.CURDIR}/ports"
@test "${PORTORIGIN}" = tests/framework-test