# 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