Files
mtkports/mk/port.mk
T

295 lines
11 KiB
Makefile

# port.mk - Shared rules for MontaukOS ports.
#
# There are two kinds of port.
#
# NOTE: a port must set its PORT_* variables BEFORE including this file --
# the install rules are expanded at include time.
#
# LIBRARY PORTS set PORT_LIB := 1 and install headers and a static archive
# into the shared ports sysroot at $(PORT_SYSROOT):
#
# sysroot/include/... public headers
# sysroot/lib/lib<x>.a static archive
#
# Dependent ports pick these up automatically: $(PORT_CFLAGS_COMMON) already
# includes -I $(PORT_SYSROOT_INC), so a port that needs zlib just adds
# $(PORT_SYSROOT_LIB)/libz.a to its link line. A library port declares its own
# prerequisites with PORT_DEPENDS (names only, for documentation and for the
# top-level build order).
#
# APPLICATION PORTS produce an app bundle in $(OUTDIR)/apps/<name>/ containing:
#
# <name>.elf the binary
# manifest.toml launcher metadata (name, binary, icon, category)
# <icon>.svg the launcher icon
# .port marker so the OS release step can tell ports from
# in-tree apps (see MontaukOS scripts/install_apps.sh)
#
# The desktop readdirs 0:/apps/ at runtime and parses each manifest.toml, so a
# bundle dropped into the ramdisk needs no changes anywhere in the OS tree.
#
# A port Makefile sets PORT_NAME, PORT_ICON and its own sources/flags, then
# includes this file and uses $(BUNDLE_DIR) as its link target directory.
MAKEFLAGS += -rR
.SUFFIXES:
# This file defines rules before the including Makefile does, so pin the
# default goal rather than letting the first bundle rule claim it.
.DEFAULT_GOAL := all
# ==== Repo root =======================================================
# Derived from this file's own location so a port builds the same whether it
# is invoked from the top-level Makefile or directly with `make -C <cat>/<port>`,
# and regardless of how deeply categories nest it.
PORT_MK_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
MTKPORTS_ROOT := $(abspath $(PORT_MK_DIR)/..)
# ==== SDK location ====================================================
# Ports build against a live MontaukOS checkout rather than a copied
# sysroot, so libc/header changes are picked up without a sync step.
MONTAUKOS ?= $(HOME)/dev/MontaukOS
ifeq ($(wildcard $(MONTAUKOS)/programs/link.ld),)
$(error MontaukOS tree not found at $(MONTAUKOS). Pass MONTAUKOS=/path/to/MontaukOS)
endif
# ==== Toolchain =======================================================
TOOLCHAIN_PREFIX := $(MONTAUKOS)/toolchain/local/bin/x86_64-montauk-
ifeq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
$(error x86_64-montauk toolchain not found. Run $(MONTAUKOS)/toolchain/build-montauk-toolchain.sh first)
endif
CC := $(TOOLCHAIN_PREFIX)gcc
CXX := $(TOOLCHAIN_PREFIX)g++
AR := $(TOOLCHAIN_PREFIX)ar
LD := $(TOOLCHAIN_PREFIX)gcc
# ==== SDK paths =======================================================
SDK_INC := $(MONTAUKOS)/programs/include
SDK_LIBC_INC:= $(MONTAUKOS)/programs/include/libc
SDK_LIB := $(MONTAUKOS)/programs/lib
SDK_LINK_LD := $(MONTAUKOS)/programs/link.ld
LIBC_A := $(SDK_LIB)/libc/liblibc.a
LIBLOADER_A := $(SDK_LIB)/libloader/liblibloader.a
LIBTLS_A := $(SDK_LIB)/tls/libtls.a
LIBBEARSSL_A:= $(SDK_LIB)/bearssl/libbearssl.a
LIBJPEG_A := $(SDK_LIB)/libjpeg/libjpeg.a
# The kernel ABI version this port is being built against. Recorded in the
# bundle so a stale port can be spotted after a syscall/ABI bump rather than
# crashing mysteriously at runtime.
SDK_API_VERSION := $(shell sed -n 's/.*apiVersion = \([0-9]\+\);.*/\1/p' \
$(MONTAUKOS)/kernel/src/Api/Info.hpp 2>/dev/null | head -1)
# ==== Output ==========================================================
OUTDIR ?= $(MTKPORTS_ROOT)/out
OBJDIR ?= obj
# Shared sysroot that library ports install into and every port compiles
# against. Kept separate from the MontaukOS SDK so `make clean` here never
# touches the OS tree.
PORT_SYSROOT ?= $(MTKPORTS_ROOT)/sysroot
PORT_SYSROOT_INC := $(PORT_SYSROOT)/include
PORT_SYSROOT_LIB := $(PORT_SYSROOT)/lib
BUNDLE_DIR := $(OUTDIR)/apps/$(PORT_NAME)
TARGET := $(BUNDLE_DIR)/$(PORT_NAME).elf
# ==== Common flags ====================================================
# Mirrors the in-tree convention: freestanding, no stdlib, linked with the
# shared program link script.
PORT_CFLAGS_COMMON := \
-g -O2 -pipe \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-ffunction-sections \
-fdata-sections \
-msse \
-msse2 \
-isystem $(SDK_LIBC_INC) \
-I $(SDK_INC) \
-I $(PORT_SYSROOT_INC)
PORT_LDFLAGS_COMMON := \
-nostdlib \
-Wl,--gc-sections \
-T $(SDK_LINK_LD)
# ==== NetSurf library ports ===========================================
# NetSurf's libraries share one buildsystem (dev-util/netsurf-buildsystem)
# and all build the same way, so the invocation lives here rather than being
# copied into each port.
#
# It cross-compiles for x86_64-montauk unpatched: HOST is auto-detected from
# `$(CC) -dumpmachine`, and BUILD != HOST puts it in cross mode. CC/AR must be
# passed on the command line (their `origin ... default` checks skip them
# otherwise), while CFLAGS must go through the ENVIRONMENT -- their makefiles
# do `CFLAGS := -I... $(CFLAGS)`, and a command-line CFLAGS would override
# that assignment outright and drop their own include paths.
NS_BUILDSYSTEM := $(MTKPORTS_ROOT)/dev-util/netsurf-buildsystem/upstream
NS_BUILDDIR := build-montauk
NS_CFLAGS := \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-msse \
-msse2 \
-isystem $(SDK_LIBC_INC) \
-I $(SDK_INC) \
-I $(PORT_SYSROOT_INC)
NS_MAKE_ARGS := \
NSSHARED=$(NS_BUILDSYSTEM) \
CC=$(CC) \
AR=$(AR) \
COMPONENT_TYPE=lib-static \
BUILDDIR=$(NS_BUILDDIR) \
WANT_TEST=no
# MAKEFLAGS must be cleared for the sub-make. This file sets `-rR`, which
# disables built-in variables, and their buildsystem probes those with
# `ifeq ($(origin RM),default)` style tests -- under -rR the origin is
# "undefined" instead, so RM/ARFLAGS silently expand to nothing and the
# archive rule tries to *execute* the .a file (exit 127).
NS_MAKE := MAKEFLAGS= $(MAKE)
# Their makefiles resolve dependencies through pkg-config, so point it at the
# ports sysroot (prepended, so host packages still resolve for host tools).
# PYTHON is forced to python3: libcss's codegen invokes $(PYTHON), and this
# host has no bare `python`.
NS_ENV := \
PKG_CONFIG_PATH="$(PORT_SYSROOT_LIB)/pkgconfig:$$PKG_CONFIG_PATH" \
PYTHON=python3
# Build a NetSurf library in $(1) (its checkout dir) and copy the archive to
# $(PORT_ARCHIVE). Usage in a recipe: $(call ns_build,upstream,wapcaplet)
# ==== Patch series ====================================================
# Most ports need no patches -- a stub header or a build variable is usually
# enough, and both survive an upstream bump for free. Where upstream genuinely
# has to change (platform detection inside its own headers, say), put unified
# diffs in the port's patches/ directory and call ns_patch before building.
#
# The checkout is reset to its pinned commit first, so applying is idempotent
# and the series always lands on a known state. NOTE: this discards any manual
# edits inside the submodule -- edit the patch, not the checkout.
ns_patch = \
if ls patches/*.patch >/dev/null 2>&1; then \
git -C $(1) checkout -- . && \
for p in $(CURDIR)/patches/*.patch; do \
echo " PATCH: $$(basename $$p)"; \
git -C $(1) apply "$$p" || exit 1; \
done; \
fi
# A port may add its own flags by setting NS_CFLAGS_EXTRA (expanded at call
# time, so it can be set after this file is included).
ns_build = \
CFLAGS="$(NS_CFLAGS) $(NS_CFLAGS_EXTRA)" $(NS_ENV) \
$(NS_MAKE) -C $(1) $(NS_MAKE_ARGS) $(NS_MAKE_ARGS_EXTRA) && \
cp $(1)/$(NS_BUILDDIR)/lib$(2).a $(PORT_ARCHIVE)
ns_clean = $(NS_ENV) $(NS_MAKE) -C $(1) $(NS_MAKE_ARGS) clean >/dev/null 2>&1 || true
ifeq ($(PORT_LIB),1)
# ==== Library ports ===================================================
# The port builds $(PORT_ARCHIVE) and lists its public headers in
# $(PORT_HEADERS); port.mk installs both into the shared sysroot. Headers
# keep any subdirectory they are named with (e.g. parserutils/parserutils.h
# installs to sysroot/include/parserutils/parserutils.h), so set
# PORT_HEADER_ROOT to the directory those paths are relative to.
PORT_HEADER_ROOT ?= include
INSTALLED_LIB := $(PORT_SYSROOT_LIB)/$(notdir $(PORT_ARCHIVE))
INSTALLED_HEADERS := $(patsubst $(PORT_HEADER_ROOT)/%,$(PORT_SYSROOT_INC)/%,$(PORT_HEADERS))
$(INSTALLED_LIB): $(PORT_ARCHIVE)
@mkdir -p $(dir $@)
cp $< $@
$(PORT_SYSROOT_INC)/%: $(PORT_HEADER_ROOT)/%
@mkdir -p $(dir $@)
cp $< $@
# Optional pkg-config file. NetSurf libraries locate their dependencies with
# pkg-config (REQUIRED_PKGS), so a library that others depend on must publish
# one into the sysroot or those builds fail to find it. Their .pc.in files use
# literal PREFIX/LIBDIR/INCLUDEDIR/VERSION placeholders, substituted here the
# same way their install step would.
ifneq ($(PORT_PC_IN),)
INSTALLED_PC := $(PORT_SYSROOT_LIB)/pkgconfig/$(PORT_NAME).pc
# Several .pc.in files carry a LIBRARIES placeholder that upstream's install
# expands to the link flags (Makefile.top: 's#LIBRARIES#$(__libraries)#'). For
# libnsutils, libnslog and libnspsl that placeholder holds the library's OWN
# -l flag, so leaving it unexpanded produces a .pc that names no library at
# all and puts a bare "LIBRARIES" token on the link line.
#
# Default to the port's own -l flag; a port whose placeholder means "my
# dependencies" (libdom) overrides PORT_PC_LIBS.
PORT_PC_LIBS ?= -l$(patsubst lib%,%,$(PORT_NAME))
$(INSTALLED_PC): $(PORT_PC_IN) Makefile
@mkdir -p $(dir $@)
sed -e 's|PREFIX|$(PORT_SYSROOT)|' \
-e 's|LIBDIR|lib|' \
-e 's|INCLUDEDIR|include|' \
-e 's|LIBRARIES|$(PORT_PC_LIBS)|' \
-e 's|VERSION|$(PORT_PC_VERSION)|' $< > $@
endif
.PHONY: install-lib
install-lib: $(INSTALLED_LIB) $(INSTALLED_HEADERS) $(INSTALLED_PC)
@echo "installed lib: $(PORT_NAME) -> $(PORT_SYSROOT)"
.PHONY: clean-lib
clean-lib:
rm -rf $(OBJDIR) $(PORT_ARCHIVE) $(INSTALLED_LIB) $(INSTALLED_HEADERS) $(INSTALLED_PC)
else
# ==== Bundle assembly =================================================
# Ports depend on $(BUNDLE_FILES) to get manifest + icon + marker staged
# alongside the ELF their link rule produces.
BUNDLE_FILES := $(BUNDLE_DIR)/manifest.toml $(BUNDLE_DIR)/.port
ifneq ($(PORT_ICON),)
BUNDLE_FILES += $(BUNDLE_DIR)/$(PORT_ICON)
endif
$(BUNDLE_DIR)/manifest.toml: manifest.toml | $(BUNDLE_DIR)
cp $< $@
$(BUNDLE_DIR)/$(PORT_ICON): $(PORT_ICON) | $(BUNDLE_DIR)
cp $< $@
# Marker file consumed by the MontaukOS release step to prune ports from a
# base ISO. Carries the ABI version so mismatches are diagnosable.
$(BUNDLE_DIR)/.port: Makefile | $(BUNDLE_DIR)
printf 'port = "%s"\napi_version = "%s"\n' '$(PORT_NAME)' '$(SDK_API_VERSION)' > $@
$(BUNDLE_DIR):
mkdir -p $@
.PHONY: bundle
bundle: $(TARGET) $(BUNDLE_FILES)
.PHONY: clean-bundle
clean-bundle:
rm -rf $(OBJDIR) $(BUNDLE_DIR)
endif