aboutsummaryrefslogtreecommitdiff
path: root/src/tools/pginclude/headerscheck
blob: 9e86d0493621eb0d35c8a58e5661662d2f7ff13c (plain)
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#!/bin/sh

# Check (almost) all PostgreSQL include files for standalone build.
#
# Argument 1 is the top-level source directory, argument 2 the
# top-level build directory (they might be the same). If not set, they
# default to the current directory.
#
# Needs to be run after configuring and creating all generated headers.
# It's advisable to configure --with-perl --with-python, else you're
# likely to get errors from associated headers.
#
# No output if everything is OK, else compiler errors.
#
# src/tools/pginclude/headerscheck
# Copyright (c) 2009-2025, PostgreSQL Global Development Group

# option to check for C++ compatibility
if [ "$1" = "--cplusplus" ]; then
	cplusplus=true
	shift
else
	cplusplus=false
fi

if [ -z "$1" ]; then
    srcdir="."
else
    srcdir="$1"
fi

if [ -z "$2" ]; then
    builddir="."
else
    builddir="$2"
fi

me=`basename $0`

# These switches are g++ specific, you may override if necessary.
CXXFLAGS=${CXXFLAGS:- -fsyntax-only -Wall}

# Pull some info from configure's results.
MGLOB="$builddir/src/Makefile.global"
CPPFLAGS=`sed -n 's/^CPPFLAGS[ 	]*=[ 	]*//p' "$MGLOB"`
CFLAGS=`sed -n 's/^CFLAGS[ 	]*=[ 	]*//p' "$MGLOB"`
CC=`sed -n 's/^CC[ 	]*=[ 	]*//p' "$MGLOB"`
CXX=`sed -n 's/^CXX[ 	]*=[ 	]*//p' "$MGLOB"`
PG_SYSROOT=`sed -n 's/^PG_SYSROOT[ 	]*=[ 	]*//p' "$MGLOB"`
perl_includespec=`sed -n 's/^perl_includespec[ 	]*=[ 	]*//p' "$MGLOB"`
python_includespec=`sed -n 's/^python_includespec[ 	]*=[ 	]*//p' "$MGLOB"`

# needed on Darwin
CPPFLAGS=`echo "$CPPFLAGS" | sed "s|\\\$(PG_SYSROOT)|$PG_SYSROOT|g"`

# (EXTRAFLAGS is not set here, but user can pass it in if need be.)

if $cplusplus; then
	ext=cpp
	COMPILER=${CXX:-g++}
	# Extract any -I and -D switches from CPPFLAGS.
	for flag in $CPPFLAGS; do
	  case $flag in
	    -I*|-D*) CXXPPFLAGS="$CXXPPFLAGS $flag";;
	  esac
	done
	COMPILER_FLAGS="$CXXPPFLAGS $CXXFLAGS"
else
	ext=c
	COMPILER=${CC:-gcc}
	COMPILER_FLAGS="$CPPFLAGS $CFLAGS"
fi

# Create temp directory.
tmp=`mktemp -d /tmp/$me.XXXXXX`

trap "ret=$?; rm -rf $tmp; exit $ret" 0 1 2 3 15

exit_status=0

# Scan all of src/ and contrib/ for header files.
for f in `cd "$srcdir" && find src contrib -name '*.h' -print`
do
	# Ignore files that are unportable or intentionally not standalone.

	# These files are platform-specific, and c.h will include the
	# one that's relevant for our current platform anyway.
	test "$f" = src/include/port/cygwin.h && continue
	test "$f" = src/include/port/darwin.h && continue
	test "$f" = src/include/port/freebsd.h && continue
	test "$f" = src/include/port/linux.h && continue
	test "$f" = src/include/port/netbsd.h && continue
	test "$f" = src/include/port/openbsd.h && continue
	test "$f" = src/include/port/solaris.h && continue
	test "$f" = src/include/port/win32.h && continue

	# Additional Windows-specific headers.
	test "$f" = src/include/port/win32_port.h && continue
	test "$f" = src/include/port/win32/netdb.h && continue
	$cplusplus && test "$f" = src/include/port/win32/sys/resource.h && continue
	test "$f" = src/include/port/win32/sys/socket.h && continue
	test "$f" = src/include/port/win32_msvc/dirent.h && continue
	test "$f" = src/include/port/win32_msvc/utime.h && continue
	test "$f" = src/include/port/win32ntdll.h && continue
	test "$f" = src/port/pthread-win32.h && continue

	# Likewise, these files are platform-specific, and the one
	# relevant to our platform will be included by atomics.h.
	test "$f" = src/include/port/atomics/arch-arm.h && continue
	test "$f" = src/include/port/atomics/arch-ppc.h && continue
	test "$f" = src/include/port/atomics/arch-x86.h && continue
	test "$f" = src/include/port/atomics/fallback.h && continue
	test "$f" = src/include/port/atomics/generic.h && continue
	test "$f" = src/include/port/atomics/generic-gcc.h && continue
	test "$f" = src/include/port/atomics/generic-msvc.h && continue
	test "$f" = src/include/port/atomics/generic-sunpro.h && continue

	# sepgsql.h depends on headers that aren't there on most platforms.
	test "$f" = contrib/sepgsql/sepgsql.h && continue

	# nodetags.h cannot be included standalone: it's just a code fragment.
	test "$f" = src/include/nodes/nodetags.h && continue
	test "$f" = src/backend/nodes/nodetags.h && continue

	# These files are not meant to be included standalone, because
	# they contain lists that might have multiple use-cases.
	test "$f" = src/include/access/rmgrlist.h && continue
	test "$f" = src/include/parser/kwlist.h && continue
	test "$f" = src/pl/plpgsql/src/pl_reserved_kwlist.h && continue
	test "$f" = src/pl/plpgsql/src/pl_unreserved_kwlist.h && continue
	test "$f" = src/interfaces/ecpg/preproc/c_kwlist.h && continue
	test "$f" = src/interfaces/ecpg/preproc/ecpg_kwlist.h && continue
	test "$f" = src/include/regex/regerrs.h && continue
	test "$f" = src/include/storage/lwlocklist.h && continue
	test "$f" = src/include/tcop/cmdtaglist.h && continue
	test "$f" = src/pl/plpgsql/src/plerrcodes.h && continue
	test "$f" = src/pl/plpython/spiexceptions.h && continue
	test "$f" = src/pl/tcl/pltclerrcodes.h && continue

	# Also not meant to be included standalone.
	test "$f" = contrib/isn/EAN13.h && continue
	test "$f" = contrib/isn/ISBN.h && continue
	test "$f" = contrib/isn/ISMN.h && continue
	test "$f" = contrib/isn/ISSN.h && continue
	test "$f" = contrib/isn/UPC.h && continue

	test "$f" = src/include/common/unicode_nonspacing_table.h && continue
	test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue

	test "$f" = src/include/catalog/syscache_ids.h && continue
	test "$f" = src/include/catalog/syscache_info.h && continue

	# We can't make these Bison output files compilable standalone
	# without using "%code require", which old Bison versions lack.
	# parser/gram.h will be included by parser/gramparse.h anyway.
	test "$f" = contrib/cube/cubeparse.h && continue
	test "$f" = contrib/seg/segparse.h && continue
	test "$f" = src/backend/bootstrap/bootparse.h && continue
	test "$f" = src/backend/parser/gram.h && continue
	test "$f" = src/backend/replication/repl_gram.h && continue
	test "$f" = src/backend/replication/syncrep_gram.h && continue
	test "$f" = src/backend/utils/adt/jsonpath_gram.h && continue
	test "$f" = src/bin/pgbench/exprparse.h && continue
	test "$f" = src/pl/plpgsql/src/pl_gram.h && continue
	test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue
	test "$f" = src/test/isolation/specparse.h && continue

	# This produces a "no previous prototype" warning.
	! $cplusplus && test "$f" = src/include/storage/checksum_impl.h && continue

	# SectionMemoryManager.h is C++
	test "$f" = src/include/jit/SectionMemoryManager.h && continue

	# ppport.h is not under our control, so we can't make it standalone.
	test "$f" = src/pl/plperl/ppport.h && continue

	# regression.h is not actually C, but ECPG code.
	test "$f" = src/interfaces/ecpg/test/regression.h && continue
	# printf_hack.h produces "unused function" warnings.
	test "$f" = src/interfaces/ecpg/test/printf_hack.h && continue

	if $cplusplus; then
		# pg_trace.h and utils/probes.h can include sys/sdt.h from SystemTap,
		# which itself contains C++ code and so won't compile with a C++
		# compiler under extern "C" linkage.
		test "$f" = src/include/pg_trace.h && continue
		test "$f" = src/include/utils/probes.h && continue

		# pg_dump is not C++-clean because it uses "public" and "namespace"
		# as field names, which is unfortunate but we won't change it now.
		test "$f" = src/bin/pg_dump/compress_gzip.h && continue
		test "$f" = src/bin/pg_dump/compress_io.h && continue
		test "$f" = src/bin/pg_dump/compress_lz4.h && continue
		test "$f" = src/bin/pg_dump/compress_none.h && continue
		test "$f" = src/bin/pg_dump/compress_zstd.h && continue
		test "$f" = src/bin/pg_dump/parallel.h && continue
		test "$f" = src/bin/pg_dump/pg_backup_archiver.h && continue
		test "$f" = src/bin/pg_dump/pg_dump.h && continue
	fi

	# OK, create .c file to include this .h file.
	{
	    $cplusplus && echo 'extern "C" {'
	    # Ideally we'd pre-include only the appropriate one of
	    # postgres.h, postgres_fe.h, or c.h.  We don't always have enough
	    # info to guess which, but in some subdirectories there's a
	    # reasonable choice to make, and otherwise we use postgres.h.
	    # Also, those three files should compile with no pre-include, as
	    # should src/interfaces headers meant to be exposed to clients.
	    case "$f" in
		src/include/postgres.h) ;;
		src/include/postgres_fe.h) ;;
		src/include/c.h) ;;
		src/interfaces/libpq/libpq-fe.h) ;;
		src/interfaces/libpq/libpq-events.h) ;;
		src/interfaces/ecpg/ecpglib/ecpglib_extern.h)
		    echo '#include "postgres_fe.h"' ;;
		src/interfaces/ecpg/ecpglib/*) ;;
		src/interfaces/*)
		    echo '#include "postgres_fe.h"' ;;
		src/bin/*)
		    echo '#include "postgres_fe.h"' ;;
		src/fe_utils/*)
		    echo '#include "postgres_fe.h"' ;;
		src/port/*) ;;
		src/common/*)
		    echo '#include "c.h"' ;;
		*)
		    echo '#include "postgres.h"' ;;
	    esac
	    echo "#include \"$f\""
	    $cplusplus && echo '};'
	} >$tmp/test.$ext

	# Some subdirectories need extra -I switches.
	case "$f" in
	    src/pl/plperl/*)
		EXTRAINCLUDES="$perl_includespec" ;;
	    src/pl/plpython/*)
		EXTRAINCLUDES="$python_includespec" ;;
	    src/interfaces/ecpg/*)
		EXTRAINCLUDES="-I $builddir/src/interfaces/ecpg/include -I $srcdir/src/interfaces/ecpg/include" ;;
	    src/backend/parser/*)
		EXTRAINCLUDES="-I $builddir/src/backend/parser/" ;;
	    src/backend/utils/adt/*)
		EXTRAINCLUDES="-I $builddir/src/backend/utils/adt/" ;;
	    *)
		EXTRAINCLUDES="" ;;
	esac

	# Run the test.
	if ! $COMPILER $COMPILER_FLAGS -I $builddir -I $srcdir \
		-I $builddir/src/include -I $srcdir/src/include \
		-I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \
		$EXTRAINCLUDES $EXTRAFLAGS -c $tmp/test.$ext -o $tmp/test.o
	then
		exit_status=1
	fi
done

exit $exit_status