-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsql.mk
More file actions
222 lines (200 loc) · 11.5 KB
/
Copy pathsql.mk
File metadata and controls
222 lines (200 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#
# sql.mk -- versioned SQL generation and everything that depends on it.
#
# Owns `include pgxntool/base.mk` (which pulls in control.mk and, at its end,
# PGXS), included at the top of this file before the DATA/rules below. The
# Makefile includes THIS file (once) and does NOT include base.mk itself:
# base.mk has no include guard, so a second include causes "overriding recipe"
# errors (upstream: https://github.com/Postgres-Extensions/pgxntool/issues/50).
# The code below uses vars from those includes: EXTENSION_VERSION_FILES
# (control.mk); PG_CONFIG / MAJORVER / $(call test,...) (base.mk); datadir (PGXS).
#
# ============================================================================
# GNU Make is TWO-PHASE, and we GENERATE the .sql we install -- beware.
# ============================================================================
# Phase 1 (parse) expands every $(wildcard ...) and CACHES directory listings
# before any recipe runs. Phase 2 (recipes) then generates our versioned SQL
# (sql/cat_tools--X.Y.Z.sql install scripts and sql/cat_tools--A.B.C--X.Y.Z.sql
# update scripts) from .sql.in sources. Those generated .sql are gitignored (see
# sql/.gitignore) and absent on a clean tree at parse time, so any parse-time
# $(wildcard) over them comes up short -- and the cached listing means Make never
# notices them later either.
#
# https://github.com/Postgres-Extensions/cat_tools/issues/28 is the canonical
# symptom: base.mk seeds DATA with $(wildcard sql/*--*--*.sql), a phase-1 glob
# that misses our generated update scripts on a clean tree, so `make install`
# skips them and a later `ALTER EXTENSION cat_tools UPDATE` fails with "no update
# path".
#
# The rule this file follows: glob what git TRACKS; name-DERIVE what we generate.
# We build the install/update lists from the .sql.in SOURCE names (present at
# parse time), not from the generated .sql, then add those names to DATA and
# $(sort) to dedup against base.mk's own wildcard. Such lists are parse-stable:
# identical on a clean tree and a built tree.
# ============================================================================
# See header: base.mk must be included exactly once, before the DATA/rules below.
include pgxntool/base.mk
LT95 = $(call test, $(MAJORVER), -lt, 95)
LT93 = $(call test, $(MAJORVER), -lt, 93)
#
# Parse-stable lists derived from the .sql.in SOURCES (see header). versioned_in
# globs the committed .sql.in; _out renames each to the .sql we generate.
#
versioned_in = $(wildcard sql/*--*.sql.in)
versioned_out = $(subst .sql.in,.sql,$(versioned_in))
# base.mk also seeds the update scripts via $(wildcard sql/*--*--*.sql), but that
# glob misses the generated ones on a clean tree (see header), so add by name.
upgrade_scripts_out = $(subst .sql.in,.sql,$(wildcard sql/*--*--*.sql.in))
#
# Historical install scripts committed directly as .sql with no .sql.in (the
# frozen cat_tools--0.1.* set; see sql/.gitignore). Globbing committed files is
# safe. Derive rather than hardcode: take every install-shaped .sql, then drop
# the update scripts and everything we generate from .sql.in. Parse-stable (the
# subtracted names are name-derived), and picks up new historical files auto.
historical_installs = $(filter-out $(versioned_out) $(wildcard sql/*--*--*.sql), $(wildcard sql/*--*.sql))
#
# DATA -- the INSTALL manifest: what `make install` copies into the extension
# dir. Installed != committed. The versioned .sql (install and update scripts)
# are BUILT from the committed .sql.in at build time and installed, but are
# gitignored, not tracked -- only the .sql.in are. The historical
# cat_tools--0.1.*.sql are the exception: committed directly (no .sql.in source).
# base.mk only seeds DATA; we append here.
#
DATA += $(historical_installs)
# Generated install scripts, minus the current-version file (EXTENSION_VERSION_
# FILES, from control.mk) and the update scripts.
DATA += $(filter-out $(EXTENSION_VERSION_FILES) $(upgrade_scripts_out), $(versioned_out))
# Generated update scripts: base.mk's parse-time wildcard misses these on a clean
# tree (see header), so add by name.
DATA += $(upgrade_scripts_out)
# Dedup against base.mk's wildcard on trees where the generated .sql already
# exist (also gives a stable order).
DATA := $(sort $(DATA))
all: sql/cat_tools.sql $(versioned_out)
installcheck: sql/cat_tools.sql $(versioned_out)
EXTRA_CLEAN += sql/cat_tools.sql $(versioned_out)
#
# relkind drift source generation
#
# Generate the canonical set of pg_class.relkind values from the server headers
# we are building against, for the relkind drift check in test/sql/relation__.sql.
# The output is gitignored (per-version). When postgresql-server-dev-NN is not
# installed the script emits an empty view so `make test` still works locally;
# CI runs check-relkind-source (below) so a missing header can never let the
# drift check pass silently, and `make test` warns about it locally.
RELKIND_SRC = test/.generated/pg_class_relkinds.sql
# Absolute path to the header we extract relkinds from. Computed each `make`;
# it changes when we build against a different PostgreSQL (pg_config differs).
RELKIND_HEADER := $(shell $(PG_CONFIG) --includedir-server)/catalog/pg_class.h
# Stamp recording the header path. FORCE runs the recipe every time, but it only
# rewrites the stamp (bumping its mtime) when the path actually changed, so the
# source is regenerated after a PostgreSQL-version switch even though the old
# header file itself is untouched. This is the same "name-derive the source"
# trick as versioned_out, but for a source (a server header) that lives OUTSIDE
# the tree: we cannot glob it into the dependency graph reliably, so we track its
# PATH in a committed-shaped stamp file instead.
RELKIND_STAMP = test/.generated/.relkind-header-path
# Directory the generated files live in. It is listed as an ORDER-ONLY
# prerequisite (after `|`) on the stamp and source recipes below: the directory
# must exist before we write into it, but we must NOT rebuild those files just
# because the directory changed. A directory's mtime bumps every time a file is
# added to or removed from it, so as a normal prerequisite it would force
# needless regeneration on every run; after `|` it means "ensure it exists, but
# its timestamp is not a rebuild trigger."
test/.generated:
@mkdir -p $@
.PHONY: FORCE
FORCE:
$(RELKIND_STAMP): FORCE | test/.generated
@echo '$(RELKIND_HEADER)' | cmp -s - "$@" 2>/dev/null || echo '$(RELKIND_HEADER)' > "$@"
# Real file target (not .PHONY): regenerate only when the generator, the header
# file, or the header path change -- not on every `make`. $(wildcard ...) yields
# no prereq (rather than an error) when the header is absent, in which case
# gen-relkinds.sh emits an empty view.
$(RELKIND_SRC): test/gen-relkinds.sh $(RELKIND_STAMP) $(wildcard $(RELKIND_HEADER)) | test/.generated
test/gen-relkinds.sh "$(RELKIND_HEADER)" > "$@"
.PHONY: gen-relkinds
gen-relkinds: $(RELKIND_SRC)
testdeps: $(RELKIND_SRC)
EXTRA_CLEAN += $(RELKIND_SRC) $(RELKIND_STAMP)
# Guard for CI: fail if the relkind source has no relkinds (server headers
# missing), so the drift check in test/sql/relation__.sql cannot silently pass
# on an empty view. CI runs `make check-relkind-source` before `make test` on
# every PostgreSQL version; local `make test` stays lenient (see
# warn-relkind-source).
.PHONY: check-relkind-source
check-relkind-source: $(RELKIND_SRC)
@grep -q 'RELKIND_' $(RELKIND_SRC) || { \
echo "ERROR: PostgreSQL catalog header not found at"; \
echo " $(RELKIND_HEADER)"; \
echo " so the relkind drift check in test/sql/relation__.sql would"; \
echo " pass without running. Install this PostgreSQL version's server"; \
echo " development headers so that path exists."; \
exit 1; \
}
@echo "check-relkind-source: $(RELKIND_SRC) is populated"
# Non-fatal counterpart, run at the end of `make test`: warn (do not fail) when
# the drift source is empty because the server headers are missing, so a local
# run without postgresql-server-dev-NN makes clear the drift check did not run.
# Listed as a `test` prerequisite after base.mk's, so it runs once tests are done.
.PHONY: warn-relkind-source
warn-relkind-source: $(RELKIND_SRC)
@grep -q 'RELKIND_' $(RELKIND_SRC) || echo "WARNING: PostgreSQL catalog header not found at $(RELKIND_HEADER); the relkind drift check in test/sql/relation__.sql did NOT run. Install this PostgreSQL version's server development headers to enable it."
test: warn-relkind-source
#
# Versioned-SQL generation from .sql.in
#
# 9.x version-conditional markers (dotted, e.g. -- SED: REQUIRES/PRIOR TO 9.5!)
# are handled by these two safesed blocks; 10+ (two-digit) markers by the awk in
# _apply_version_seds. The two sets are disjoint -- the awk's [1-9][0-9]+ can't
# match a dotted "9.5" -- and the sources still use the 9.x markers, so both are
# needed. Remove these blocks only once the sources drop all 9.x markers.
#
# FUTURE CLEANUP: once support for the relevant old PG versions is dropped, a
# .sql.in whose only version-conditional content targets those versions can be
# frozen to a raw committed .sql (dropping its .sql.in and thus its share of this
# processing chain). Precedent: the historical cat_tools--0.1.*.sql are already
# committed raw, with no .sql.in source.
#
# $@ is deferred (via =) and expands to the target name at recipe time.
ifeq ($(LT95),yes)
_sql_sed_95 = pgxntool/safesed $@.tmp -E -e 's/(.*)-- SED: REQUIRES 9\.5!/-- Requires 9.5: \1/'
else
_sql_sed_95 = pgxntool/safesed $@.tmp -E -e 's/(.*)-- SED: PRIOR TO 9\.5!/-- Not used prior to 9.5: \1/'
endif
ifeq ($(LT93),yes)
_sql_sed_93 = pgxntool/safesed $@.tmp -E -e 's/(.*)-- SED: REQUIRES 9\.3!/-- Requires 9.3: \1/'
else
_sql_sed_93 = pgxntool/safesed $@.tmp -E -e 's/(.*)-- SED: PRIOR TO 9\.3!/-- Not used prior to 9.3: \1/'
endif
# Apply all version-conditional SED markers to $@.tmp: 9.x via the safesed vars
# above; 10+ generically via awk (REQUIRES N -> commented if MAJORVER < N*10;
# PRIOR TO N -> commented if MAJORVER >= N*10). POSIX awk only (no gawk
# extensions like gensub() / 3-arg match()) -- portability is why this uses awk.
define _apply_version_seds
$(_sql_sed_95)
$(_sql_sed_93)
awk -v mv=$(MAJORVER) '\
/-- SED: REQUIRES [1-9][0-9]+!/ {t=$$0; sub(/.*REQUIRES /,"",t); sub(/!.*/,"",t); if(mv<t*10) $$0="-- Requires "t": "$$0}\
/-- SED: PRIOR TO [1-9][0-9]+!/ {t=$$0; sub(/.*PRIOR TO /,"",t); sub(/!.*/,"",t); if(mv>=t*10) $$0="-- Not used prior to "t": "$$0}\
{print}' $@.tmp > $@.tmp2 && mv $@.tmp2 $@.tmp
endef
# The recipe builds $@.tmp then moves it into place atomically.
# TODO: refactor the version handling into a function.
sql/%.sql: sql/%.sql.in pgxntool/safesed
(echo @generated@ && cat $< && echo @generated@) | sed -e 's#@generated@#-- GENERATED FILE! DO NOT EDIT! See $<#' > $@.tmp
$(_apply_version_seds)
mv $@.tmp $@
# Make the current version's .sql.in by copying the base source; the pattern
# rule above then turns it into the final .sql with SED substitutions applied.
# (EXTENSION_VERSION_FILES is just sql/cat_tools--<current version>.sql)
$(EXTENSION_VERSION_FILES:.sql=.sql.in): sql/cat_tools.sql.in cat_tools.control
cp $< $@
# Override control.mk's rule (which builds EXTENSION_VERSION_FILES straight from
# cat_tools.sql, skipping SED) so we build from the .sql.in above instead, with
# version-conditional substitutions applied. GNU Make's "overriding recipe"
# warning for this target is expected.
$(EXTENSION_VERSION_FILES): $(EXTENSION_VERSION_FILES:.sql=.sql.in) pgxntool/safesed
(echo @generated@ && cat $< && echo @generated@) | sed -e 's#@generated@#-- GENERATED FILE! DO NOT EDIT! See $<#' > $@.tmp
$(_apply_version_seds)
mv $@.tmp $@