src/tools/pginclude/README
-NOTE: headerscheck and headerscheck --cplusplus are in current use,
-and any problems they find should generally get fixed. The other
-scripts in this directory have not been used in some time, and have
-issues. pgrminclude in particular has a history of creating more
-problems than it fixes. Be very wary of applying their results
-blindly.
-
-
-pginclude
-=========
-
-These utilities help clean up #include file usage. They should be run
-in this order so that the include files have the proper includes before
-the C files are tested.
-
-pgfixinclude change #include's to <> or ""
-
-pgcompinclude [-v]
- report which #include files can not compile on their own
-
-pgrminclude [-v]
- remove extra #include's
-
-pgcheckdefines
- check for #ifdef tests on symbols defined in files that
- weren't included --- this is a necessary sanity check on
- pgrminclude
-
-pgdefine create macro calls for all defines in the file (used by
- the above routines)
-
-It is also a good idea to sort the pg-specific include files in
-alphabetic order. This is best done with a text editor. Typical usage
-order would be:
-
- pgfixinclude
- sort include references
- run multiple times:
- pgcompinclude
- pgrminclude /src/include
- pgrminclude /
- pgcheckdefines
-
-There is a complexity when modifying /src/include. If include file 1
-includes file 2, and file 2 includes file 3, then when file 1 is
-processed, it needs only file 2, not file 3. However, if later, include
-file 2 is processed, and file 3 is not needed by file 2 and is removed,
-file 1 might then need to include file 3. For this reason, the
-pgcompinclude and pgrminclude /src/include steps must be run several
-times until all includes compile cleanly.
-
-Also, tests should be done with configure settings of --enable-cassert
-and EXEC_BACKEND on and off. It is also wise to test a WIN32 compile.
-
-Another tools that does a similar task is at:
-
- http://code.google.com/p/include-what-you-use/
-
-An include file visualizer script is available at:
-
- http://archives.postgresql.org/pgsql-hackers/2011-09/msg00311.php
-
-
headerscheck
============
+++ /dev/null
-#! /usr/bin/perl
-
-# Copyright (c) 2021-2024, PostgreSQL Global Development Group
-
-#
-# This script looks for symbols that are referenced in #ifdef or defined()
-# tests without having #include'd the file that defines them. Since this
-# situation won't necessarily lead to any compiler message, it seems worth
-# having an automated check for it. In particular, use this to audit the
-# results of pgrminclude!
-#
-# Usage: configure and build a PG source tree (non-VPATH), then start this
-# script at the top level. It's best to enable as many configure options
-# as you can, especially --enable-cassert which is known to affect include
-# requirements. NB: you MUST use gcc, unless you have another compiler that
-# can be persuaded to spit out the names of referenced include files.
-#
-# The results are necessarily platform-dependent, so use care in interpreting
-# them. We try to process all .c files, even those not intended for the
-# current platform, so there will be some phony failures.
-#
-# src/tools/pginclude/pgcheckdefines
-#
-
-use strict;
-use warnings FATAL => 'all';
-
-use Cwd;
-use File::Basename;
-
-my $topdir = cwd();
-
-# Programs to use
-my $FIND = "find";
-my $MAKE = "make";
-
-#
-# Build arrays of all the .c and .h files in the tree
-#
-# We ignore .h files under src/include/port/, since only the one exposed as
-# src/include/port.h is interesting. (XXX Windows ports have additional
-# files there?) Ditto for .h files in src/backend/port/ subdirectories.
-# Including these .h files would clutter the list of define'd symbols and
-# cause a lot of false-positive results.
-#
-my (@cfiles, @hfiles);
-
-open my $pipe, '-|', "$FIND * -type f -name '*.c'"
- or die "can't fork: $!";
-while (<$pipe>)
-{
- chomp;
- push @cfiles, $_;
-}
-close $pipe or die "$FIND failed: $!";
-
-open $pipe, '-|', "$FIND * -type f -name '*.h'"
- or die "can't fork: $!";
-while (<$pipe>)
-{
- chomp;
- push @hfiles, $_
- unless m|^src/include/port/|
- || m|^src/backend/port/\w+/|;
-}
-close $pipe or die "$FIND failed: $!";
-
-#
-# For each .h file, extract all the symbols it #define's, and add them to
-# a hash table. To cover the possibility of multiple .h files defining
-# the same symbol, we make each hash entry a hash of filenames.
-#
-my %defines;
-
-foreach my $hfile (@hfiles)
-{
- open my $fh, '<', $hfile
- or die "can't open $hfile: $!";
- while (<$fh>)
- {
- if (m/^\s*#\s*define\s+(\w+)/)
- {
- $defines{$1}{$hfile} = 1;
- }
- }
- close $fh;
-}
-
-#
-# For each file (both .h and .c), run the compiler to get a list of what
-# files it #include's. Then extract all the symbols it tests for defined-ness,
-# and check each one against the previously built hashtable.
-#
-foreach my $file (@hfiles, @cfiles)
-{
- my ($fname, $fpath) = fileparse($file);
- chdir $fpath or die "can't chdir to $fpath: $!";
-
- #
- # Ask 'make' to parse the makefile so we can get the correct flags to
- # use. CPPFLAGS in particular varies for each subdirectory. If we are
- # processing a .h file, we might be in a subdirectory that has no
- # Makefile, in which case we have to fake it. Note that there seems
- # no easy way to prevent make from recursing into subdirectories and
- # hence printing multiple definitions --- we keep the last one, which
- # should come from the current Makefile.
- #
- my $MAKECMD;
-
- if (-f "Makefile" || -f "GNUmakefile")
- {
- $MAKECMD = "$MAKE -qp";
- }
- else
- {
- my $subdir = $fpath;
- chop $subdir;
- my $top_builddir = "..";
- my $tmp = $fpath;
- while (($tmp = dirname($tmp)) ne '.')
- {
- $top_builddir = $top_builddir . "/..";
- }
- $MAKECMD =
- "$MAKE -qp 'subdir=$subdir' 'top_builddir=$top_builddir' -f '$top_builddir/src/Makefile.global'";
- }
-
- my ($CPPFLAGS, $CFLAGS, $CFLAGS_SL, $PTHREAD_CFLAGS, $CC);
-
- open $pipe, '-|', "$MAKECMD"
- or die "can't fork: $!";
- while (<$pipe>)
- {
- if (m/^CPPFLAGS :?= (.*)/)
- {
- $CPPFLAGS = $1;
- }
- elsif (m/^CFLAGS :?= (.*)/)
- {
- $CFLAGS = $1;
- }
- elsif (m/^CFLAGS_SL :?= (.*)/)
- {
- $CFLAGS_SL = $1;
- }
- elsif (m/^PTHREAD_CFLAGS :?= (.*)/)
- {
- $PTHREAD_CFLAGS = $1;
- }
- elsif (m/^CC :?= (.*)/)
- {
- $CC = $1;
- }
- }
-
- # If make exits with status 1, it's not an error, it just means make
- # thinks some files may not be up-to-date. Only complain on status 2.
- close PIPE;
- die "$MAKE failed in $fpath\n" if $? != 0 && $? != 256;
-
- # Expand out stuff that might be referenced in CFLAGS
- $CFLAGS =~ s/\$\(CFLAGS_SL\)/$CFLAGS_SL/;
- $CFLAGS =~ s/\$\(PTHREAD_CFLAGS\)/$PTHREAD_CFLAGS/;
-
- #
- # Run the compiler (which had better be gcc) to get the inclusions.
- # "gcc -H" reports inclusions on stderr as "... filename" where the
- # number of dots varies according to nesting depth.
- #
- my @includes = ();
- my $COMPILE = "$CC $CPPFLAGS $CFLAGS -H -E $fname";
- open $pipe, '-|', "$COMPILE 2>&1 >/dev/null"
- or die "can't fork: $!";
- while (<$pipe>)
- {
- if (m/^\.+ (.*)/)
- {
- my $include = $1;
-
- # Ignore system headers (absolute paths); but complain if a
- # .c file includes a system header before any PG header.
- if ($include =~ m|^/|)
- {
- warn "$file includes $include before any Postgres inclusion\n"
- if $#includes == -1 && $file =~ m/\.c$/;
- next;
- }
-
- # Strip any "./" (assume this appears only at front)
- $include =~ s|^\./||;
-
- # Make path relative to top of tree
- my $ipath = $fpath;
- while ($include =~ s|^\.\./||)
- {
- $ipath = dirname($ipath) . "/";
- }
- $ipath =~ s|^\./||;
- push @includes, $ipath . $include;
- }
- else
- {
- warn "$CC: $_";
- }
- }
-
- # The compiler might fail, particularly if we are checking a file that's
- # not supposed to be compiled at all on the current platform, so don't
- # quit on nonzero status.
- close PIPE or warn "$COMPILE failed in $fpath\n";
-
- #
- # Scan the file to find #ifdef, #ifndef, and #if defined() constructs
- # We assume #ifdef isn't continued across lines, and that defined(foo)
- # isn't split across lines either
- #
- open my $fh, '<', $fname
- or die "can't open $file: $!";
- my $inif = 0;
- while (<$fh>)
- {
- my $line = $_;
- if ($line =~ m/^\s*#\s*ifdef\s+(\w+)/)
- {
- checkit($file, $1, @includes);
- }
- if ($line =~ m/^\s*#\s*ifndef\s+(\w+)/)
- {
- checkit($file, $1, @includes);
- }
- if ($line =~ m/^\s*#\s*if\s+/)
- {
- $inif = 1;
- }
- if ($inif)
- {
- while ($line =~ s/\bdefined(\s+|\s*\(\s*)(\w+)//)
- {
- checkit($file, $2, @includes);
- }
- if (!($line =~ m/\\$/))
- {
- $inif = 0;
- }
- }
- }
- close $fh;
-
- chdir $topdir or die "can't chdir to $topdir: $!";
-}
-
-exit 0;
-
-# Check an is-defined reference
-sub checkit
-{
- my ($file, $symbol, @includes) = @_;
-
- # Ignore if symbol isn't defined in any PG include files
- if (!defined $defines{$symbol})
- {
- return;
- }
-
- #
- # Try to match source(s) of symbol to the inclusions of the current file
- # (including itself). We consider it OK if any one matches.
- #
- # Note: these tests aren't bulletproof; in theory the inclusion might
- # occur after the use of the symbol. Given our normal file layout,
- # however, the risk is minimal.
- #
- foreach my $deffile (keys %{ $defines{$symbol} })
- {
- return if $deffile eq $file;
- foreach my $reffile (@includes)
- {
- return if $deffile eq $reffile;
- }
- }
-
- #
- # If current file is a .h file, it's OK for it to assume that one of the
- # base headers (postgres.h or postgres_fe.h) has been included.
- #
- if ($file =~ m/\.h$/)
- {
- foreach my $deffile (keys %{ $defines{$symbol} })
- {
- return if $deffile eq 'src/include/c.h';
- return if $deffile eq 'src/include/postgres.h';
- return if $deffile eq 'src/include/postgres_fe.h';
- return if $deffile eq 'src/include/pg_config.h';
- return if $deffile eq 'src/include/pg_config_manual.h';
- }
- }
-
- #
- my @places = keys %{ $defines{$symbol} };
- print "$file references $symbol, defined in @places\n";
-
- # print "includes: @includes\n";
-
- return;
-}
+++ /dev/null
-:
-# report which #include files can not compile on their own
-# takes -v option to display compile failure message and line numbers
-# src/tools/pginclude/pgcompinclude
-
-: ${CC:=cc}
-: ${PGSRC:=src}
-
-if ! pgdefine
-then echo "pgdefine must be in your PATH" 1>&2
- exit 1
-fi
-
-trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a" 0 1 2 3 15
-find . \( -name .git -a -prune \) -o -name '*.h' -type f -print | while read FILE
-do
- sed 's/->[a-zA-Z0-9_\.]*//g' "$FILE" >/tmp/$$a
- echo "#include \"postgres.h\"" >/tmp/$$.c
-
- # suppress fcinfo errors
- echo "struct {Datum arg[1];} *fcinfo;" >>/tmp/$$.c
-
- echo "#include \"/tmp/$$a\"" >>/tmp/$$.c
-
- echo "Datum include_test(void);" >>/tmp/$$.c
- echo "Datum include_test() {" >>/tmp/$$.c
-
- pgdefine "$FILE" >>/tmp/$$.c
-
- echo "return (Datum)0;" >>/tmp/$$.c
- echo "}" >>/tmp/$$.c
-
- # Use -O1 to get warnings only generated by optimization,
- # but -O2 is too slow.
- $CC -fsyntax-only -Werror -Wall -Wmissing-s \
- -Wmissing-declarations -I$PGSRC/include -I$PGSRC/backend \
- -I$PGSRC/interfaces/libpq -I`dirname $FILE` $CFLAGS -O1 -c /tmp/$$.c \
- -o /tmp/$$.o >/tmp/$$ 2>&1
- if [ "$?" -ne 0 ]
- then echo "$FILE"
- if [ "$1" = "-v" ]
- then cat /tmp/$$
- nl /tmp/$$.c
- echo
- fi
- fi
-done
+++ /dev/null
-:
-# create macro calls for all defines in the file
-
-# src/tools/pginclude/pgdefine
-
-trap "rm -f /tmp/$$" 0 1 2 3 15
-for FILE
-do
- cat "$FILE" | grep "^#define" >/tmp/$$
- cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*\)[ ][ ]*[^ ].*\\\\$/\1;/p'
- cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*\)[ ][ ]*[^ ].*[^\\\\]$/(void)\1;/p'
-
- (
- cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*([^)]*)\).*\\\\$/\1;/p'
- cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*([^)]*)\).*[^\\\\]$/(=void)\1;/p'
- ) |
- sed 's/([a-zA-Z0-9_ ][a-zA-Z0-9_ ]*)/(0)/g' |
- sed 's/([a-zA-Z0-9_ ]*,/(0,/g' |
- sed 's/,[a-zA-Z0-9_ ]*,/,0,/g' |
- sed 's/,[a-zA-Z0-9_ ]*)/,0)/g' |
- # do not cast 'return' macros as (void)
- sed 's/(=void)\(.*return\)/\1/g' |
- sed 's/(=void)\(.*RETURN\)/\1/g' |
- sed 's/(=void)/(void)/g'
-done
+++ /dev/null
-:
-# change #include's to <> or ""
-# src/tools/pginclude/pgfixinclude
-
-trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a /tmp/$$b" 0 1 2 3 15
-find . \( -name .git -a -prune \) -o -type f -name '*.[chyls]' -print |
-while read FILE
-do
- cat "$FILE" | grep "^#include" |
- sed 's/^#include[ ]*[<"]\([^>"]*\).*$/\1/g' |
- while read INCLUDE
- do
- if [ -s /usr/include/"$INCLUDE" ]
- then cat "$FILE" |
- sed 's;^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]$;#include <'"$INCLUDE"'>;g' >/tmp/$$
- else cat "$FILE" |
- sed 's;^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]$;#include "'"$INCLUDE"'";g' >/tmp/$$
- fi
- cat /tmp/$$ > "$FILE"
- done
-done
+++ /dev/null
-:
-# remove extra #include's
-
-# pgcompinclude must be run before and after pgrminclude. It must be
-# run before because we don't want include dependencies to into
-# the C program files, and after because removal of includes from headers
-# can cause new include unfulfilled dependencies.
-#
-# Limitations: 2011-09-24
-#
-# Pgrminclude, when processing header files, can cause includes to be
-# removed that require the addition of new illogical header files.
-# This is dependent on what order the header files are processed.
-# Manual review of header files now needed to satisfy pgcompinclude is
-# required.
-#
-# C program files that have #ifdef blocks that contain code that cannot
-# be compiled on the platform from which pgrminclude is run cannot be
-# processed, and are skipped.
-
-: ${CC:=cc}
-: ${PGSRC:=src}
-
-if ! pgdefine
-then echo "pgdefine must be in your PATH" 1>&2
- exit 1
-fi
-
-trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a /tmp/$$b" 0 1 2 3 15
-
-if [ "$1" = "-v" ]
-then VERBOSE="Y"
-else VERBOSE=""
-fi
-
-verbose_output() {
- if [ "$VERBOSE" ]
- then cat /tmp/$$
- cat /tmp/$$b
- nl /tmp/$$.c
- fi
-}
-
-process_includes_in_file() {
- # loop through all includes mentioned in the file
- cat "$FILE" |
- grep "^#include\>" |
- grep -v '/\* *pgrminclude *ignore *\*/' |
- sed 's/^#include[ ]*[<"]\([^>"]*\).*$/\1/g' |
- grep -v 'parser/kwlist\.h' |
- grep -v '\.c$' |
- while read INCLUDE
- do if [ "$VERBOSE" ]
- then echo "checking $FILE $INCLUDE"
- fi
- compile_file
- done
-}
-
-compile_file() {
- [ "$INCLUDE" -a -s /usr/include/"$INCLUDE" ] && continue
- [ "$INCLUDE" = "postgres.h" ] && continue
- [ "$INCLUDE" = "postgres_fe.h" ] && continue
- [ "$INCLUDE" = "pg_config.h" ] && continue
- [ "$INCLUDE" = "c.h" ] && continue
- # Stringify macros will expand undefined identifiers, so skip files that use it
- egrep -q '\<(CppAsString2?|CppConcat)\>' "$FILE" && continue
-
- # preserve configure-specific includes
- # these includes are surrounded by #ifdef's
- grep -B1 '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' "$FILE" |
- egrep -q '^#if|^#else|^#elif' && continue
- grep -A1 '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' "$FILE" |
- egrep -q '^#else|^#elif|^#endif' && continue
-
- # Remove all #if and #ifdef blocks because the blocks
- # might contain code that is not compiled on this platform.
- cat "$FILE" |
- grep -v "^#if" |
- grep -v "^#else" |
- grep -v "^#elif" |
- grep -v "^#endif" |
- # with #if blocks gone, now undef #defines to avoid redefine
- # warning and failure
- sed 's/#define[ ][ ]*\([A-Za-z0-9_]*\).*$/#undef \1\n&/' >/tmp/$$a
-
- # set up initial file contents
- grep -v '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' \
- /tmp/$$a >/tmp/$$b
-
- if [ "$IS_INCLUDE" = "Y" ]
- then echo "#include \"postgres.h\"" >/tmp/$$.c
- # suppress fcinfo errors
- echo "struct {Datum arg[1];} *fcinfo;" >>/tmp/$$.c
- else >/tmp/$$.c
- fi
-
- echo "#include \"/tmp/$$b\"" >>/tmp/$$.c
-
- if [ "$IS_INCLUDE" = "Y" ]
- then echo "Datum include_test(void);" >>/tmp/$$.c
- echo "Datum include_test() {" >>/tmp/$$.c
- pgdefine "$FILE" >>/tmp/$$.c
- echo "return (Datum)0;" >>/tmp/$$.c
- echo "}" >>/tmp/$$.c
- fi
-
- # Use -O1 to get warnings only generated by optimization,
- # but -O2 is too slow.
- $CC -fsyntax-only -Werror -Wall -Wmissing-s \
- -Wmissing-declarations -I$PGSRC/include -I$PGSRC/backend \
- -I$PGSRC/interfaces/libpq -I`dirname $FILE` $CFLAGS -O1 -c /tmp/$$.c \
- -o /tmp/$$.o >/tmp/$$ 2>&1
- if [ "$?" -eq 0 ]
- then [ "$INCLUDE" -o "$VERBOSE" ] && echo "$FILE $INCLUDE"
- grep -v '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' \
- "$FILE" >/tmp/$$b
- mv /tmp/$$b "$FILE"
- return 0
- else return 1
- fi
-}
-
-# Process include files first because they can affect the compilation
-# of *.c files.
-(find . \( -name .git -a -prune \) -o -type f -name '*.h' -print | sort;
- find . \( -name .git -a -prune \) -o -type f -name '*.c' -print | sort) |
-grep -v '/postgres.h$' |
-grep -v '/postgres_fe.h$' |
-grep -v '/pg_config.h$' |
-grep -v '\./c.h$' |
-while read FILE
-do
- if [ `expr $FILE : '.*\.h$'` -ne 0 ]
- then IS_INCLUDE="Y"
- else IS_INCLUDE="N"
- fi
-
- # Can we compile the file with all existing includes?
- INCLUDE=""
- compile_file
- # If the file can't be compiled on its own, there is no sense
- # trying to remove the include files.
- if [ "$?" -ne 0 ]
- then echo "cannot compile $FILE with existing includes"
- verbose_output
- else process_includes_in_file
- fi
-done