Files
mtkports/dev-vcs/git/Makefile
T
2026-08-09 10:47:07 +02:00

219 lines
8.4 KiB
Makefile

# git for MontaukOS
#
# Upstream: https://github.com/git/git (submodule at upstream/, pinned to
# v2.39.5).
#
# git is built as a plain system program (0:/os/git.elf), not an app bundle:
# it is a command-line tool driven from the terminal.
#
# CONFIGURATION
# The build must not pick up any host defaults. git derives its platform
# settings from `uname -s` through config.mak.uname, which on this host
# would answer "Linux" and switch on clock_gettime, getdelim, /dev/tty and
# friends -- none of which the Montauk libc has. Every uname_* variable is
# therefore overridden to a name config.mak.uname does not know, leaving
# the knobs below as the only configuration.
#
# The NO_* list is not "things git can live without" so much as an
# inventory of what MontaukOS does not provide yet: no threads, no
# sockets, no iconv, no regex, no file-backed mmap. git ships portable
# fallbacks for all of them (compat/), which is what makes this port
# tractable at all.
#
# COMPAT SHIMS (compat/) supply the headers and functions the Montauk libc
# has no equivalent of -- see compat/README for what each one does and, more
# importantly, what it does not.
UPSTREAM := upstream
COMPAT := $(CURDIR)/compat
PORT_NAME := git
include ../../mk/port.mk
GIT_BIN := $(UPSTREAM)/git
GIT_INSTALL := $(MONTAUKOS)/programs/bin/os
# Where git's compiled-in paths point on the target. programs/bin/os is the
# staging directory for 0:/os, so GIT_PREFIX and GIT_STAGE are two names for
# the same place -- one as the OS sees it, one as the build host does.
# RUNTIME_PREFIX is off (it wants /proc/self/exe), so these are baked in:
#
# $(GIT_PREFIX)/share/git-core/templates `git init` skeleton
# $(GIT_PREFIX)/etc/gitconfig system-wide config
# $(GIT_PREFIX)/libexec/git-core external subcommands (none yet;
# every command in this build is a
# builtin, which is what makes git
# usable without fork/exec)
GIT_PREFIX := 0:/os/git
GIT_STAGE := $(GIT_INSTALL)/git
# `git version` string. Without an override GIT-VERSION-GEN runs `git
# describe` on the checkout and, because the patch series is applied in the
# working tree, reports "2.39.5.dirty" -- true but unhelpful. Name the port
# instead: the tree really is patched, and this says by whom.
GIT_UPSTREAM_TAG := $(shell git -C $(UPSTREAM) describe --tags --abbrev=0 2>/dev/null)
GIT_VERSION_STR := $(patsubst v%,%,$(GIT_UPSTREAM_TAG))-montauk
# ==== Toolchain flags =================================================
# CFLAGS/LDFLAGS/EXTLIBS must be passed on git's make COMMAND LINE, not
# through the environment: its Makefile assigns `CFLAGS = -g -O2 -Wall`
# outright, and a plain assignment beats an environment value. Only a
# command-line assignment wins. Overriding them is safe -- git keeps its own
# required includes and defines in BASIC_CFLAGS, which is separate and is
# appended to ALL_CFLAGS after ours.
GIT_CFLAGS := \
-std=gnu99 \
$(PORT_CFLAGS_COMMON) \
-Wno-unused-parameter \
-Wno-sign-compare \
-I $(COMPAT) \
-include $(COMPAT)/montauk-paths.h \
-include $(COMPAT)/montauk-ownership.h \
-isystem $(shell $(CC) -print-file-name=include)
# crt1.o carries _start; -nostdlib (from PORT_LDFLAGS_COMMON) drops the
# driver's own startup files, so name them here.
GIT_CRT := \
$(SDK_LIB)/libc/crt1.o \
$(SDK_LIB)/libc/crti.o \
$(SDK_LIB)/libc/crtn.o
COMPAT_LIB := $(CURDIR)/compat/libgitcompat.a
GIT_LDFLAGS := $(PORT_LDFLAGS_COMMON) $(GIT_CRT)
# EXTLIBS lands on the link line after the objects, which is where the compat
# archive, zlib and libc have to sit for the static link to resolve. Naming
# libz.a here rather than letting ZLIB_PATH add -lz is deliberate: overriding
# EXTLIBS drops git's own `EXTLIBS += -lz` append. ZLIB_PATH is still set, for
# the -I it contributes to BASIC_CFLAGS.
GIT_EXTLIBS := $(COMPAT_LIB) $(PORT_SYSROOT_LIB)/libz.a $(LIBC_A)
# ==== Build configuration =============================================
#
# DEFAULT_PAGER=cat is load-bearing, not a preference: git_pager() treats the
# literal "cat" as "no pager at all" and skips the fork entirely (pager.c).
# Left at its default it would try to spawn `less` on every `git log` and
# fail, since MontaukOS cannot fork.
#
# OBJECT_CREATION_USES_RENAMES=1 because there are no hard links. Every loose
# object is written to a temp file and then finalized; by default git tries
# link()+unlink() first and only falls back to rename() when link fails, so
# without this every single object write makes a doomed syscall and travels an
# error path. The knob picks rename() up front, which is what happens anyway.
#
# DEFAULT_EDITOR is deliberately NOT set. With an empty environment TERM is
# unset, so git considers the terminal dumb, git_editor() returns NULL, and
# `git commit` with no -m says "terminal is dumb, but EDITOR unset. Please
# supply the message using either -m or -F option." Naming an editor here
# would replace that with a failed spawn.
GIT_ARGS := \
CC=$(CC) \
AR=$(AR) \
CFLAGS="$(GIT_CFLAGS)" \
LDFLAGS="$(GIT_LDFLAGS)" \
EXTLIBS="$(GIT_EXTLIBS)" \
uname_S=MontaukOS \
uname_M=x86_64 \
uname_O=MontaukOS \
uname_R=1 \
uname_P=x86_64 \
uname_V=1 \
prefix=$(GIT_PREFIX) \
GIT_VERSION=$(GIT_VERSION_STR) \
ZLIB_PATH=$(PORT_SYSROOT) \
BLK_SHA1=1 \
NO_CURL=1 \
NO_EXPAT=1 \
NO_OPENSSL=1 \
NO_ICONV=1 \
NO_GETTEXT=1 \
NO_PTHREADS=1 \
NO_PERL=1 \
NO_PYTHON=1 \
NO_TCLTK=1 \
NO_REGEX=1 \
NO_MMAP=1 \
NO_IPV6=1 \
NO_INET_NTOP=1 \
NO_INET_PTON=1 \
NO_HSTRERROR=1 \
NO_SYS_SELECT_H=1 \
NO_UNIX_SOCKETS=1 \
NO_SETITIMER=1 \
NO_STRUCT_ITIMERVAL=1 \
NO_LIBGEN_H=1 \
NO_MEMMEM=1 \
NO_STRCASESTR=1 \
NO_STRLCPY=1 \
NO_STRTOUMAX=1 \
NO_MKDTEMP=1 \
NO_INITGROUPS=1 \
NO_GECOS_IN_PWENT=1 \
NO_NSEC=1 \
NO_INSTALL_HARDLINKS=1 \
NO_TRUSTABLE_FILEMODE=1 \
NO_SYMLINK_HEAD=1 \
INTERNAL_QSORT=1 \
OBJECT_CREATION_USES_RENAMES=1 \
DEFAULT_PAGER=cat
GIT_MAKE := MAKEFLAGS= $(MAKE)
.PHONY: all clean git-build install uninstall
all: git-build
$(COMPAT_LIB): $(wildcard compat/*.c) $(wildcard compat/*.h)
$(CC) $(GIT_CFLAGS) -c compat/montauk-compat.c -o compat/montauk-compat.o
$(AR) rcs $@ compat/montauk-compat.o
@echo "Built: $@"
# The templates/ sub-make builds the skeleton `git init` copies into a new
# repository (info/exclude, the sample hooks, the description file) into
# templates/blt. Without it every `git init` warns that templates are
# missing. It is a subdirectory build, not a target of the top Makefile, so
# it is invoked directly -- `make templates` up there just matches the
# directory name and does nothing.
git-build: $(COMPAT_LIB)
@$(call ns_patch,$(UPSTREAM))
@# git's link rule knows nothing about EXTLIBS or the crt objects, so a
@# rebuilt compat archive -- or a rebuilt libc, which is the one that
@# actually catches people out -- would otherwise leave the old binary
@# in place. Drop it and let the sub-make link again.
@for lib in $(GIT_EXTLIBS) $(GIT_CRT); do \
if [ -f $(GIT_BIN) ] && [ "$$lib" -nt $(GIT_BIN) ]; then \
echo " RELINK: $$(basename $$lib) is newer than $(GIT_BIN)"; \
rm -f $(GIT_BIN); \
fi; \
done
$(GIT_MAKE) -C $(UPSTREAM) $(GIT_ARGS) git
$(GIT_MAKE) -C $(UPSTREAM)/templates
@test -f $(GIT_BIN) || { echo "git: expected $(GIT_BIN)" >&2; exit 1; }
@echo "Built: $(GIT_BIN) ($$(stat -c%s $(GIT_BIN)) bytes)"
# Installed as a system program, not an app bundle: git is a command line
# tool with no window. It carries no manifest and no .port marker, so
# remember to uninstall before cutting a release ISO.
#
# The binary is STRIPPED on the way in -- the debug info is five sixths of
# its size (17 MB -> 3 MB), and it all has to fit in the ramdisk.
install: git-build
mkdir -p $(GIT_INSTALL)
$(TOOLCHAIN_PREFIX)strip -o $(GIT_INSTALL)/git.elf $(GIT_BIN)
mkdir -p $(GIT_STAGE)/share/git-core
rm -rf $(GIT_STAGE)/share/git-core/templates
cp -rL $(UPSTREAM)/templates/blt $(GIT_STAGE)/share/git-core/templates
@echo "installed: 0:/os/git.elf + $(GIT_PREFIX)/share/git-core/templates"
uninstall:
rm -rf $(GIT_INSTALL)/git.elf $(GIT_STAGE)
clean:
@$(GIT_MAKE) -C $(UPSTREAM) $(GIT_ARGS) clean >/dev/null 2>&1 || true
@git -C $(UPSTREAM) checkout -- . 2>/dev/null || true
rm -f $(COMPAT_LIB) compat/*.o