From 15dbe0c142472d4b7b0549015b1cf963d74843c9 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Thu, 20 Jun 2024 15:47:18 -0400 Subject: [PATCH] POC Trunk binary distribution format `trunk.mk` copies a bunch of the `install` target from PGXS and modifies it to install into a organized according to the [proposed format]. The adds the `make` variables `DISTVERSION`, `LICENSE`, and `LANGUAGE`, but otherwise depends on variables defined by PGXS. It uses `jq` to build `trunk.json`, `shasum` to build the `digests` file, and `tar` to create the `*.trunk` artifact as a tarball. `install_trunk` is a simple shell script to demonstrate unpacking, validating, and installing a trunk file. It too relies on `jq`, `shasum`, and `tar`, and uses `rsync` to do the actual installation. It also does basic trunk version, platform, and Postgres version validation. [proposed format]: https://github.com/orgs/pgxn/discussions/2 --- .gitignore | 2 +- Makefile | 3 +- install_trunk | 144 ++++++++++++++++++++++++++++++ trunk.mk | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 2 deletions(-) create mode 100755 install_trunk create mode 100644 trunk.mk diff --git a/.gitignore b/.gitignore index 3ad5050..011f240 100644 --- a/.gitignore +++ b/.gitignore @@ -4,12 +4,12 @@ results/ *.dll tmp/ *.o +*.bc regression.diffs regression.out /sql/semver--?.??.?.sql /semver-* /latest-changes.md -/src/*.bc /semver_binary_copy.bin /.vscode /*.bin diff --git a/Makefile b/Makefile index 8777ed1..2dd11c6 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ DISTVERSION = $(shell grep -m 1 '[[:space:]]\{3\}"version":' META.json | \ sed -e 's/[[:space:]]*"version":[[:space:]]*"\([^"]*\)",\{0,1\}/\1/') MODULEDIR = $(EXTENSION) -DATA = $(wildcard sql/*.sql) +DATA = $(wildcard sql/*--*.sql) DOCS = $(wildcard doc/*.mmd) TESTS = $(wildcard test/sql/*.sql) REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) @@ -22,6 +22,7 @@ endif PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) +include ./trunk.mk all: sql/$(EXTENSION)--$(EXTVERSION).sql diff --git a/install_trunk b/install_trunk new file mode 100755 index 0000000..c268bc0 --- /dev/null +++ b/install_trunk @@ -0,0 +1,144 @@ +#!/bin/bash + +# POC for installing Trunk format binaries created with trunk.mk. Requires: +# +# * bash +# * tar +# * shasum +# * jq +# * uname +# * pg_config +# * rsync + +trap 'exit' ERR +set -E + +install_trunk() { + local trunk=$1 + + if [ -z "$trunk" ]; then + printf "Usage:\n\n %s PACKAGE_PATH\n" "$0" + exit 1 + fi + + # Determine the platform. + local my_os my_osv my_arch + my_os=$(uname | tr '[:upper:]' '[:lower:]') + my_osv=$(uname -r) + my_arch="$(uname -m)" + if [ "$my_arch" == "x86_64" ]; then my_arch=amd64; fi + + # Unpack. + printf "Unpacking %s\n" "$trunk" + tar zxf "$trunk" + + # Go into the directory + local dir="${trunk%.*}" + cd "$dir" || exit + + # Verify the checksums. + printf "Verifying all checksums..." + # XXX This does not fail if a file isn't present in digests. + shasum --check -b --strict digests + printf "Done!\n" + + # Verify the Trunk version + printf "Verifying compatibility with Trunk package 0.1.0\n" + local tv + tv=$(jq -r .trunk trunk.json) + if [ "$tv" != "0.1.0" ]; then + printf "Unsupported Trunk format version %s\n" "$tv" + exit 1 + fi + + # Verify the Postgres version. + local pkg_pg my_pg + my_pg=$(pg_config --version | sed -E 's/^[^ ]+ ([^ ]+).*$/\1/') + printf "Verifying compatibility with PostgreSQL %s\n" "$my_pg" + pkg_pg=$(jq -r .postgres.version trunk.json) + if [ "$pkg_pg" != "$my_pg" ]; then + printf "Trunk package contains binaries for Postgres %s but this host runs Postgres %s\n" "$pkg_pg" "$my_pg" + exit 1 + fi + + printf "Verifying compatibility with %s/%s:%s\n" "$my_os" "$my_arch" "$my_osv" + local pkg_os + pkg_os=$(jq -r .platform.os trunk.json) + if [ "$pkg_os" != "any" ]; then + # Verify the OS + if [ "$pkg_os" != "$my_os" ]; then + printf "Trunk package contains %s binaries but this host runs %s\n" "$pkg_os" "$my_os" + exit 1 + fi + + # Verify the architecture. + local pkg_arch + pkg_arch=$(jq -r .platform.arch trunk.json) + if [ "$pkg_arch" != "$my_arch" ]; then + printf "Trunk package contains %s binaries but this host runs %s\n" "$pkg_arch" "$my_arch" + exit 1 + fi + fi + + # Make sure we have pgsql directory. + if [ ! -d 'pgsql' ]; then + printf "Package contains no install files; exiting\n" + exit 1 + fi + + cd 'pgsql' || exit + for subdir in *; do + [[ -d "$subdir" ]] || continue + case $subdir in + share) + install_dir "$subdir" "$(pg_config --sharedir)" + ;; + pkglib) + install_dir "$subdir" "$(pg_config --pkglibdir)" + ;; + pkginclude) + install_dir "$subdir" "$(pg_config --pkgincludedir)" + ;; + lib) + install_dir "$subdir" "$(pg_config --libdir)" + ;; + include) + install_dir "$subdir" "$(pg_config --includedir)" + ;; + bin) + install_dir "$subdir" "$(pg_config --bindir)" + ;; + doc) + install_dir "$subdir" "$(pg_config --docdir)" + ;; + man) + install_dir "$subdir" "$(pg_config --mandir)" + ;; + html) + install_dir "$subdir" "$(pg_config --htmldir)" + ;; + locale) + install_dir "$subdir" "$(pg_config --localedir)" + ;; + sysconf) + install_dir "$subdir" "$(pg_config --sysconfdir)" + ;; + *) + printf "Unknown install directory %s; skipping\n" "$subdir" + ;; + esac + done + +} + +install_dir() { + local src="$1" + local dst="$2" + printf "Installing %s into %s..." "$src" "$dst" + cd "$src" || exit + rsync -q -a -v . "$dst" || exit + printf "Done\n" + cd .. +} + +install_trunk "$@" diff --git a/trunk.mk b/trunk.mk new file mode 100644 index 0000000..4a23894 --- /dev/null +++ b/trunk.mk @@ -0,0 +1,239 @@ +# Trunk packaging. To use, create a standard PGXS Makefile for your extension. +# The only extra variables are DISTVERSION, LICENSE, and LANGUAGE. +# +# ``` make +# DISTVERSION = 1.2.1 +# LICENSE = mit +# LANGUAGE = c +# +# EXTENSION = bike +# MODULEDIR = $(EXTENSION) +# DATA = $(wildcard sql/*.sql) +# DOCS = $(wildcard doc/*.mmd) +# MODULES = $(patsubst %.c,%,$(wildcard src/*.c)) +# PG_CONFIG ?= pg_config +# +# PGXS := $(shell $(PG_CONFIG) --pgxs) +# include $(PGXS) +# include ./trunk.mk +# ``` +# +# Then build it: +# +# ``` sh +# make trunk +# ``` +# +# This will create a file with a name similar to +# +# bike-1.2.1+pg16-darwin-23.5.0-arm64 +# +# Requires: +# +# * pg_config +# * PGXS (from `pg_config --pgxs`) +# * tar +# * shasum +# * jq + +LAUNGUAGE ?= c +LICENSE ?= PostgreSQL + +pkg_arch := $(shell uname -m) +ifeq ($(pkg_arch),x86_64) +pkg_arch := amd64 +endif +ifeq ($(PORTNAME),darwin) +pkg_os_ver := $(shell uname -r) +pkg := $(EXTENSION)-$(DISTVERSION)+pg$(MAJORVERSION)-$(PORTNAME)-$(pkg_os_ver)-$(pkg_arch) +else +pkg := $(EXTENSION)-$(DISTVERSION)+pg$(MAJORVERSION)-$(PORTNAME)-$(pkg_arch) +endif +pkg_dir := $(pkg) +pkg_installdir := $(pkg_dir)/pgsql +pkg_sharedir := $(pkg_installdir)/share +pkg_libdir := $(pkg_installdir)/lib +pkg_pkglibdir := $(pkg_installdir)/pkglib +pkg_docdir := $(pkg_installdir)/doc/$(docmoduledir) +pkg_bindir := $(pkg_installdir)/bin +pkg_incdir := $(pkg_installdir)/include/server/$(docmoduledir) +ifdef SO_MAJOR_VERSION +pkg_pkgconfigdir = $(pkg_libdir)/pkgconfig +endif +pkg_info_files ?= $(wildcard README* readme* Readme* LICENSE* license* License* CHANGE* change* Change*) +EXTRA_CLEAN += $(EXTENSION)-$(DISTVERSION)+*/ + +# Phony target to create the trunk and OCI JSON files. +trunk: $(pkg).trunk + +$(pkg).trunk: package + tar zcvf $@ $(pkg_dir) + +# Use jq to build trunk.json. XXX Add dependencies +$(pkg_dir)/trunk.json: pkg_dirs + @pg=$$(jq -n \ + --arg version "$(VERSION)" \ + --arg major "$(MAJORVERSION)" \ + --argjson number "$(VERSION_NUM)" \ + --arg libs "$(LIBS)" \ + --arg cppflags "$(CPPFLAGS)" \ + --arg cflags "$(CFLAGS)" \ + --arg ldflags "$(LDFLAGS)" \ + '$$ARGS.named' \ + ) && pkg=$$(jq -n \ + --arg name "$(EXTENSION)" \ + --arg version $(DISTVERSION) \ + --arg language "$(LAUNGUAGE)" \ + --arg license "$(LICENSE)" \ + '$$ARGS.named' \ + ) && plat=$$(jq -n \ + --arg os "$(PORTNAME)" \ + --arg version "$(pkg_os_ver)" \ + --arg arch $(pkg_arch) \ + '$$ARGS.named | with_entries(select(.value |. !=null and . != ""))' \ + ) && jq -n \ + --arg trunk 0.1.0 \ + --argjson package "$$pkg" \ + --argjson postgres "$$pg" \ + --argjson platform "$$plat" \ + '$$ARGS.named' > $@ + + +# Based on install in https://github.com/postgres/postgres/blob/REL_17_BETA1/src/makefiles/pgxs.mk#L237C1-L276 +package: all pkg_dirs $(pkg_dir)/trunk.json +ifneq (,$(EXTENSION)) + $(INSTALL_DATA) $(addprefix $(srcdir)/, $(addsuffix .control, $(EXTENSION))) '$(pkg_sharedir)/extension/' +endif # EXTENSION +ifneq (,$(DATA)$(DATA_built)) + $(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) '$(pkg_sharedir)/$(datamoduledir)/' +endif # DATA +ifneq (,$(DATA_TSEARCH)) + $(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA_TSEARCH)) '$(pkg_sharedir)/tsearch_data/' +endif # DATA_TSEARCH +ifdef MODULES + $(INSTALL_SHLIB) $(addsuffix $(DLSUFFIX), $(MODULES)) '$(pkg_pkglibdir)/' +ifeq ($(with_llvm), yes) + $(foreach mod, $(MODULES), $(call package_llvm_module,$(mod),$(mod).bc)) +endif # with_llvm +endif # MODULES +ifdef DOCS +ifdef docdir + $(INSTALL_DATA) $(addprefix $(srcdir)/, $(DOCS)) '$(pkg_docdir)/' +endif # docdir +endif # DOCS +ifdef PROGRAM + $(INSTALL_PROGRAM) $(PROGRAM)$(X) '$(pkg_bindir)' +endif # PROGRAM +ifdef SCRIPTS + $(INSTALL_SCRIPT) $(addprefix $(srcdir)/, $(SCRIPTS)) '$(pkg_bindir)/' +endif # SCRIPTS +ifdef SCRIPTS_built + $(INSTALL_SCRIPT) $(SCRIPTS_built) '$(pkg_bindir)/' +endif # SCRIPTS_built +ifneq (,$(strip $(HEADER_dirs))) + $(foreach dir,$(HEADER_dirs),$(call package_headers,$(dir),$(HEADER_files_$(dir)))) +endif # HEADERS +ifdef MODULE_big +ifeq ($(with_llvm), yes) + $(call package_llvm_module,$(MODULE_big),$(OBJS)) +endif # with_llvm +package: package-lib +endif # MODULE_big +ifneq (,$(pkg_info_files)) + $(INSTALL_DATA) $(addprefix $(srcdir)/, $(pkg_info_files)) '$(pkg_dir)/' +endif + rm -f "$(pkg_dir)/digests" + cd "$(pkg_dir)/" && find * -type f | xargs shasum --tag -ba 256 > digests + +# Based on installdirs in https://github.com/postgres/postgres/blob/REL_17_BETA1/src/makefiles/pgxs.mk#L279C1-L303 +pkg_dirs: +ifneq (,$(EXTENSION)) + $(MKDIR_P) '$(pkg_sharedir)/extension' +endif +ifneq (,$(DATA)$(DATA_built)) + $(MKDIR_P) '$(pkg_sharedir)/$(datamoduledir)' +endif +ifneq (,$(DATA_TSEARCH)) + $(MKDIR_P) '$(pkg_sharedir)/tsearch_data' +endif +ifneq (,$(MODULES)) + $(MKDIR_P) '$(pkg_pkglibdir)' +endif +ifdef DOCS + $(MKDIR_P) '$(pkg_docdir)' +endif +ifneq (,$(PROGRAM)$(SCRIPTS)$(SCRIPTS_built)) + $(MKDIR_P) '$(pkg_bindir)' +endif +ifdef MODULE_big +pkg_dirs: pkg_dirs-lib +endif # MODULE_big + + +# Based on https://github.com/postgres/postgres/blob/REL_17_BETA1/src/Makefile.shlib#L364-L399. +.PHONY: package-lib package-lib-static package-lib-shared installdirs-lib +package-lib: package-lib-shared +ifdef soname +package-lib: package-lib-static +package-lib: package-lib-pc +endif + +package-lib-pc: lib$(NAME).pc installdirs-lib + $(INSTALL_DATA) $< '$(pkg_pkgconfigdir)/lib$(NAME).pc' + +package-lib-static: $(stlib) installdirs-lib + $(INSTALL_STLIB) $< '$(pkg_libdir)/$(stlib)' + +package-lib-shared: $(shlib) installdirs-lib +ifdef soname +# we don't install $(shlib) on AIX +# (see http://archives.postgresql.org/message-id/52EF20B2E3209443BC37736D00C3C1380A6E79FE@EXADV1.host.magwien.gv.at) +ifneq ($(PORTNAME), aix) + $(INSTALL_SHLIB) $< '$(pkg_libdir)/$(shlib)' +ifneq ($(PORTNAME), cygwin) +ifneq ($(PORTNAME), win32) +ifneq ($(shlib), $(shlib_major)) + cd '$(pkg_libdir)' && \ + rm -f $(shlib_major) && \ + $(LN_S) $(shlib) $(shlib_major) +endif +ifneq ($(shlib), $(shlib_bare)) + cd '$(pkg_libdir)' && \ + rm -f $(shlib_bare) && \ + $(LN_S) $(shlib) $(shlib_bare) +endif +endif # not win32 +endif # not cygwin +endif # not aix +ifneq (,$(findstring $(PORTNAME),win32 cygwin)) + $(INSTALL_SHLIB) $< '$(DESTDIR)$(bindir)/$(shlib)' +endif +else # no soname + $(INSTALL_SHLIB) $< '$(DESTDIR)$(pkglibdir)/$(shlib)' +endif + + +# Based on installdirs-lib in https://github.com/postgres/postgres/blob/REL_17_BETA1/src/Makefile.shlib#L402-L407. +pkg_dirs-lib: +ifdef soname + $(MKDIR_P) '$(pkg_libdir)' '$(pkg_pkgconfigdir)' $(if $(findstring $(PORTNAME),win32 cygwin),'$(pkg_bindir)') +else + $(MKDIR_P) '$(pkg_pkgconfigdir)' +endif + + +# Based on install_llvm_module in https://github.com/postgres/postgres/blob/REL_17_BETA1/src/Makefile.global.in#L1090-L1107. +define package_llvm_module +$(MKDIR_P) '$(DESTDIR)${bitcodedir}/$(1)' +$(MKDIR_P) $(sort $(dir $(addprefix '$(pkg_pkglibdir)/bitcode'/$(1)/, $(2)))) +$(foreach obj, ${2}, $(INSTALL_DATA) $(patsubst %.o,%.bc, $(obj)) '$(pkg_pkglibdir)/bitcode'/$(1)/$(dir $(obj)) +) +cd '$(pkg_pkglibdir)/bitcode' && $(LLVM_BINPATH)/llvm-lto -thinlto -thinlto-action=thinlink -o $(1).index.bc $(addprefix $(1)/,$(patsubst %.o,%.bc, $(2))) +endef + + +# Based on install_headers in https://github.com/postgres/postgres/blob/REL_17_BETA1/src/makefiles/pgxs.mk#L202-L207 +define package_headers +$(MKDIR_P) '$(pkg_incdir)/$(1)/' +$(INSTALL_DATA) $(2) '$(pkg_incdir)/$(1)/' +endef