SPS · log · files · refs · tags · releases · people · languages · GitHub · file
Makefile
10230 bytes at 2909919 · GitHub
POSIX_SHELL = /bin/sh
PREFIX = /usr
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib/sps
MANDIR = $(PREFIX)/share/man
SYSCONFDIR = /etc/sps
DESTDIR =
PROGRAMS = src sget mkpkg pkin pkdel pkstat pkcheck pkmark pkconf setup mkiso
AWK_LIBS = common.awk config.awk db.awk deps.awk index.awk package.awk recipe.awk repository.awk relations.awk version.awk
.PHONY: all check lint check-version source-archive release-check \
install install-config clean iso iso-slim iso-plasma \
audit-recipes audit-recipes-net
all:
@printf '%s\n' "SPS is interpreted; run 'make check' or 'make install'."
check: lint
$(POSIX_SHELL) tests/run
lint:
@set -e; for program in $(PROGRAMS); do \
$(POSIX_SHELL) -n bin/$$program; \
done
@set -e; for library in lib/*.sh; do \
$(POSIX_SHELL) -n $$library; \
done
@set -e; for library in lib/setup/*.sh; do \
[ -f $$library ] || continue; \
$(POSIX_SHELL) -n $$library; \
done
@set -e; for script in lib/setup/sv/runit/*/run lib/setup/sv/s6/*/run \
lib/setup/sv/openrc/*; do \
[ -f $$script ] || continue; \
$(POSIX_SHELL) -n $$script; \
done
@python3 -m py_compile tools/upgrade-recipes tools/write-stock-services.py \
tools/audit-recipes
@set -e; for script in lib/mkiso/initrd-init lib/mkiso/live-rc \
lib/mkiso/live-plasma lib/mkiso/live-plasma-session \
lib/mkiso/live-init lib/mkiso/udhcpc-script; do \
[ -f $$script ] || continue; \
$(POSIX_SHELL) -n $$script; \
done
@# Pure library modules can be parsed without an action or fixture. The
@# executable AWK modules validate their input in END rules, so the test
@# suite below is their syntax + behavior check rather than invoking them
@# against an intentionally invalid empty stream.
@awk -f lib/common.awk </dev/null >/dev/null
@awk -f lib/common.awk -f lib/config.awk </dev/null >/dev/null
@awk -f lib/version.awk </dev/null >/dev/null
# Recipe-tree audits. SPS_RECIPE_TREES, or RECIPE_TREES on the command line,
# selects the trees; the default is the usual /usr/src/sps checkouts.
#
# make audit-recipes RECIPE_TREES=/usr/src/sps/core:/usr/src/sps/extra
#
# audit-recipes is offline and fast enough for every commit. audit-recipes-net
# downloads every declared source and verifies its SHA-256, which is the only
# way to catch an upstream tarball that moved or was re-rolled. Run it on a
# schedule, not on every push.
RECIPE_TREES =
audit-recipes:
@python3 tools/audit-recipes $(if $(RECIPE_TREES),--trees $(RECIPE_TREES))
audit-recipes-net:
@python3 tools/audit-recipes --net \
$(if $(RECIPE_TREES),--trees $(RECIPE_TREES))
check-version:
@LC_ALL=C awk '\
NR == 1 && $$0 ~ /^[0-9]+\.[0-9]+\.[0-9]+$$/ { valid = 1 } \
END { exit !(NR == 1 && valid) } \
' VERSION || { \
printf '%s\n' 'VERSION must contain one X.Y.Z release number' >&2; \
exit 1; \
}
# Build from the committed tree so untracked files and local edits can never
# enter a published source archive. gzip -n omits its timestamp and filename.
source-archive: check-version
@set -eu; \
version=$$(sed -n '1p' VERSION); \
for tool in git gzip mktemp; do \
command -v "$$tool" >/dev/null 2>&1 || { \
printf 'source-archive: required tool not found: %s\n' "$$tool" >&2; \
exit 1; \
}; \
done; \
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { \
printf '%s\n' 'source-archive: not inside a Git work tree' >&2; \
exit 1; \
}; \
mkdir -p dist; \
archive="dist/sps-$$version.tar.gz"; \
tmp_tar=$$(mktemp "$${TMPDIR:-/tmp}/sps-source.XXXXXX"); \
tmp_archive="$$archive.tmp"; \
trap 'rm -f "$$tmp_tar" "$$tmp_archive"' 0 HUP INT TERM; \
git archive --format=tar --prefix="sps-$$version/" \
--output="$$tmp_tar" HEAD; \
gzip -n -9 -c "$$tmp_tar" >"$$tmp_archive"; \
mv "$$tmp_archive" "$$archive"; \
printf '%s\n' "$$archive"
# Run this after creating vVERSION and before pushing the tag. It verifies
# that the tag, committed VERSION, work tree, and reproducible archive agree.
release-check: check source-archive
@set -eu; \
version=$$(sed -n '1p' VERSION); \
tag="v$$version"; \
archive="dist/sps-$$version.tar.gz"; \
prefix="sps-$$version/"; \
if [ -n "$$(git status --porcelain --untracked-files=normal)" ]; then \
printf '%s\n' 'release-check: Git work tree is not clean' >&2; \
exit 1; \
fi; \
git diff --check; \
tag_commit=$$(git rev-parse -q --verify "$$tag^{commit}") || { \
printf 'release-check: tag %s does not exist\n' "$$tag" >&2; \
exit 1; \
}; \
[ "$$(git cat-file -t "$$tag")" = tag ] || { \
printf 'release-check: tag %s is not annotated\n' "$$tag" >&2; \
exit 1; \
}; \
head_commit=$$(git rev-parse HEAD); \
[ "$$tag_commit" = "$$head_commit" ] || { \
printf 'release-check: tag %s does not point to HEAD\n' "$$tag" >&2; \
exit 1; \
}; \
gzip -t "$$archive"; \
members=$$(mktemp "$${TMPDIR:-/tmp}/sps-members.XXXXXX"); \
repro_tar=$$(mktemp "$${TMPDIR:-/tmp}/sps-repro.XXXXXX"); \
repro_archive=$$(mktemp "$${TMPDIR:-/tmp}/sps-repro-gz.XXXXXX"); \
trap 'rm -f "$$members" "$$repro_tar" "$$repro_archive"' \
0 HUP INT TERM; \
tar -tzf "$$archive" >"$$members"; \
awk -v prefix="$$prefix" '\
index($$0, prefix) != 1 { bad = 1 } \
END { exit bad || NR == 0 } \
' "$$members" || { \
printf '%s\n' 'release-check: archive has an invalid member path' >&2; \
exit 1; \
}; \
for required in VERSION Makefile bin/src; do \
grep -F -x "$$prefix$$required" "$$members" >/dev/null || { \
printf 'release-check: archive is missing %s\n' "$$required" >&2; \
exit 1; \
}; \
done; \
archive_version=$$(tar -xOzf "$$archive" "$$prefix"VERSION); \
[ "$$archive_version" = "$$version" ] || { \
printf '%s\n' 'release-check: archive VERSION does not match' >&2; \
exit 1; \
}; \
git archive --format=tar --prefix="$$prefix" \
--output="$$repro_tar" HEAD; \
gzip -n -9 -c "$$repro_tar" >"$$repro_archive"; \
cmp "$$archive" "$$repro_archive" >/dev/null || { \
printf '%s\n' 'release-check: source archive is not reproducible' >&2; \
exit 1; \
}; \
printf 'release-check: %s is ready\n' "$$archive"
install:
install -d "$(DESTDIR)$(BINDIR)" "$(DESTDIR)$(LIBDIR)" \
"$(DESTDIR)$(MANDIR)/man1" "$(DESTDIR)$(MANDIR)/man5"
@set -e; for program in $(PROGRAMS); do \
if [ -d "$(DESTDIR)$(BINDIR)/$$program" ]; then \
printf '%s: error: %s is a directory; %s command would be shadowed\n' \
"$0" "$(DESTDIR)$(BINDIR)/$$program" "$$program" >&2; \
exit 1; \
fi; \
install -m 0755 bin/$$program "$(DESTDIR)$(BINDIR)/$$program"; \
done
@set -e; for library in lib/*.awk lib/*.sh; do \
install -m 0644 $$library "$(DESTDIR)$(LIBDIR)/$${library##*/}"; \
done
@set -e; for page in man/*.1; do \
install -m 0644 $$page "$(DESTDIR)$(MANDIR)/man1/$${page##*/}"; \
done
@set -e; for page in man/*.5; do \
install -m 0644 $$page "$(DESTDIR)$(MANDIR)/man5/$${page##*/}"; \
done
install -d "$(DESTDIR)$(LIBDIR)/setup/profiles" \
"$(DESTDIR)$(LIBDIR)/setup/sets"
install -m 0644 lib/setup/keymaps lib/setup/timezones \
lib/setup/locales lib/setup/shells lib/setup/extras \
lib/setup/services \
"$(DESTDIR)$(LIBDIR)/setup/"
@set -e; for f in lib/setup/profiles/*.profile; do \
install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/setup/profiles/"; \
done
@set -e; for f in lib/setup/sets/*.set; do \
install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/setup/sets/"; \
done
install -d "$(DESTDIR)$(LIBDIR)/mkiso"
@set -e; for f in lib/mkiso/*; do \
[ -f "$$f" ] || continue; \
case $$f in \
*/live-init|*/initrd-init|*/live-rc|*/live-plasma|*/live-plasma-session|*/udhcpc-script) \
install -m 0755 $$f "$(DESTDIR)$(LIBDIR)/mkiso/" ;; \
*) \
install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/mkiso/" ;; \
esac; \
done
install -d "$(DESTDIR)$(LIBDIR)/mkiso/systemd"
@set -e; for f in lib/mkiso/systemd/*; do \
[ -f "$$f" ] || continue; \
install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/mkiso/systemd/"; \
done
install -d "$(DESTDIR)$(LIBDIR)/setup"
@set -e; for f in lib/setup/*.sh; do \
[ -f "$$f" ] || continue; \
install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/setup/"; \
done
@set -e; \
if [ -d lib/setup/sv ]; then \
find lib/setup/sv -type d | while read -r d; do \
install -d "$(DESTDIR)$(LIBDIR)/setup/$${d#lib/setup/}"; \
done; \
find lib/setup/sv -type f | while read -r f; do \
mode=0644; \
case $$f in \
*/sv/openrc/*|*/sv/runit/*/run|*/sv/runit/*/finish|*/sv/s6/*/run|*/sv/s6/*/finish) \
mode=0755 ;; \
esac; \
install -m $$mode $$f "$(DESTDIR)$(LIBDIR)/setup/$${f#lib/setup/}"; \
done; \
fi
# Configuration is separate so installation never overwrites administrator
# policy. Existing files are deliberately left untouched.
install-config:
install -d "$(DESTDIR)$(SYSCONFDIR)"
@test -e "$(DESTDIR)$(SYSCONFDIR)/sps.conf" || \
install -m 0644 examples/sps.conf "$(DESTDIR)$(SYSCONFDIR)/sps.conf"
@test -e "$(DESTDIR)$(SYSCONFDIR)/repos.conf" || \
install -m 0644 examples/repos.conf "$(DESTDIR)$(SYSCONFDIR)/repos.conf"
# SPS Linux (Splux) live images. busybox must be a static binary; the host
# copy is usually dynamically linked and will not survive in an initramfs.
ISO_BUSYBOX ?= /tmp/sps-wave/busybox.static
ISO_CORE ?= /usr/src/sps/core
ISO_EXTRA ?= /usr/src/sps/extra
ISO_DIR ?= dist
iso:
@test -x "$(ISO_BUSYBOX)" || { \
printf '%s\n' "iso: static busybox not found: $(ISO_BUSYBOX)" >&2; \
exit 1; \
}
mkdir -p "$(ISO_DIR)"
bin/mkiso --output "$(ISO_DIR)/sps-live.iso" --init systemd \
--busybox "$(ISO_BUSYBOX)" --core "$(ISO_CORE)" --extra "$(ISO_EXTRA)"
iso-slim:
@test -x "$(ISO_BUSYBOX)" || { \
printf '%s\n' "iso-slim: static busybox not found: $(ISO_BUSYBOX)" >&2; \
exit 1; \
}
mkdir -p "$(ISO_DIR)"
bin/mkiso --output "$(ISO_DIR)/sps-live-slim.iso" --init systemd \
--no-seed --no-modules --busybox "$(ISO_BUSYBOX)"
iso-plasma:
@test -x "$(ISO_BUSYBOX)" || { \
printf '%s\n' "iso-plasma: static busybox not found: $(ISO_BUSYBOX)" >&2; \
exit 1; \
}
mkdir -p "$(ISO_DIR)"
bin/mkiso --output "$(ISO_DIR)/sps-live-plasma.iso" \
--session plasma --init systemd \
--busybox "$(ISO_BUSYBOX)" --core "$(ISO_CORE)" --extra "$(ISO_EXTRA)"
clean:
@find . -type f \( -name '*.pkg.tar' -o -name '*.pkg.tar.gz' -o \
-name '*.pkg.tar.xz' -o -name '*.pkg.tar.zst' \) -exec rm -f {} \;