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 .PHONY: check
check: check:
./tests/check-layout.sh ./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 # MOUSE ports
This repository is the independently versioned source-port collection for This repository is the independently versioned source-port collection for
MOUSE. It contains third-party recipes and patches only; the `mouse-port` MOUSE. Each port is a BSD make project, while shared phase and quality-control
implementation, ports contract, base-system manifest, and release integration machinery lives under `ports/Mk`.
belong to `mouse-src`.
Each port lives below `ports/NAME` and follows the recipe contract shipped by Each port lives below `ports/category/name` and contains a `Makefile`,
the compatible MOUSE base release. A MOUSE release pins one tested commit from `distinfo`, and `pkg-plist`, plus optional patches and service definitions
this repository, while installed systems may move to later compatible ports under `files`. A MOUSE release pins one tested commit from this repository,
revisions without replacing the base system. 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 The adversarial and demonstration recipes used by the base-system acceptance
suite are deliberately not part of this collection. They live in suite are deliberately not part of this collection. They live in
@@ -19,3 +19,16 @@ Run the collection checks with:
```sh ```sh
make check 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 # Infrastructure
Collection-wide generation, lint, and publication tooling belongs here. Base Collection-wide generation, lint, and publication tooling belongs here. The
image assembly and the `mouse-port` implementation remain in `mouse-src`. 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 # Ports collection
Each immediate child directory is one port named `[a-z][a-z0-9-]*` and contains Ports use `category/name` directories. Every ordinary port contains a
one `port.conf` plus its trusted `build.sh` and any port-owned patches or files. `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 The collection is intentionally empty until the first real third-party port is
accepted. Test-only recipes do not belong here. accepted. Test-only recipes do not belong here.
+16 -25
View File
@@ -2,32 +2,23 @@
set -eu set -eu
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
found=0
for directory in "$root"/ports/*; do [ -f "$root/ports/Mk/mouse.port.mk" ]
[ -d "$directory" ] || continue [ -x "$root/ports/Mk/Scripts/check-plist.sh" ]
[ "${directory##*/}" != README.md ] || continue [ -x "$root/ports/Mk/Scripts/checksum.sh" ]
found=1 [ -x "$root/ports/Mk/Scripts/fetch.sh" ]
name=${directory##*/} [ -x "$root/ports/Mk/Scripts/install-depends.sh" ]
case "$name" in [ -x "$root/ports/Mk/Scripts/package.sh" ]
""|[!a-z]*|*[!a-z0-9-]*)
printf '%s\n' "invalid port directory name: $name" >&2 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 exit 1
;; }
esac done
[ -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 layout checks passed"
printf '%s\n' "ports collection is empty"
else
printf '%s\n' "ports layout check passed"
fi
+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