# 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.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// containing: # # .elf the binary # manifest.toml launcher metadata (name, binary, icon, category) # .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 /`, # 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) # 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) # 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_MAKE) -C $(1) $(NS_MAKE_ARGS) && \ cp $(1)/$(NS_BUILDDIR)/lib$(2).a $(PORT_ARCHIVE) ns_clean = $(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 $< $@ .PHONY: install-lib install-lib: $(INSTALLED_LIB) $(INSTALLED_HEADERS) @echo "installed lib: $(PORT_NAME) -> $(PORT_SYSROOT)" .PHONY: clean-lib clean-lib: rm -rf $(OBJDIR) $(PORT_ARCHIVE) $(INSTALLED_LIB) $(INSTALLED_HEADERS) 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