diff options
Diffstat (limited to 'src/tools')
79 files changed, 14725 insertions, 0 deletions
diff --git a/src/tools/PerfectHash.pm b/src/tools/PerfectHash.pm new file mode 100644 index 0000000..4ffb6bd --- /dev/null +++ b/src/tools/PerfectHash.pm @@ -0,0 +1,380 @@ +#---------------------------------------------------------------------- +# +# PerfectHash.pm +# Perl module that constructs minimal perfect hash functions +# +# This code constructs a minimal perfect hash function for the given +# set of keys, using an algorithm described in +# "An optimal algorithm for generating minimal perfect hash functions" +# by Czech, Havas and Majewski in Information Processing Letters, +# 43(5):256-264, October 1992. +# This implementation is loosely based on NetBSD's "nbperf", +# which was written by Joerg Sonnenberger. +# +# The resulting hash function is perfect in the sense that if the presented +# key is one of the original set, it will return the key's index in the set +# (in range 0..N-1). However, the caller must still verify the match, +# as false positives are possible. Also, the hash function may return +# values that are out of range (negative or >= N), due to summing unrelated +# hashtable entries. This indicates that the presented key is definitely +# not in the set. +# +# +# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/PerfectHash.pm +# +#---------------------------------------------------------------------- + +package PerfectHash; + +use strict; +use warnings; + + +# At runtime, we'll compute two simple hash functions of the input key, +# and use them to index into a mapping table. The hash functions are just +# multiply-and-add in uint32 arithmetic, with different multipliers and +# initial seeds. All the complexity in this module is concerned with +# selecting hash parameters that will work and building the mapping table. + +# We support making case-insensitive hash functions, though this only +# works for a strict-ASCII interpretation of case insensitivity, +# ie, A-Z maps onto a-z and nothing else. +my $case_fold = 0; + + +# +# Construct a C function implementing a perfect hash for the given keys. +# The C function definition is returned as a string. +# +# The keys should be passed as an array reference. They can be any set +# of Perl strings; it is caller's responsibility that there not be any +# duplicates. (Note that the "strings" can be binary data, but hashing +# e.g. OIDs has endianness hazards that callers must overcome.) +# +# The name to use for the function is specified as the second argument. +# It will be a global function by default, but the caller may prepend +# "static " to the result string if it wants a static function. +# +# Additional options can be specified as keyword-style arguments: +# +# case_fold => bool +# If specified as true, the hash function is case-insensitive, for the +# limited idea of case-insensitivity explained above. +# +# fixed_key_length => N +# If specified, all keys are assumed to have length N bytes, and the +# hash function signature will be just "int f(const void *key)" +# rather than "int f(const void *key, size_t keylen)". +# +sub generate_hash_function +{ + my ($keys_ref, $funcname, %options) = @_; + + # It's not worth passing this around as a parameter; just use a global. + $case_fold = $options{case_fold} || 0; + + # Try different hash function parameters until we find a set that works + # for these keys. The multipliers are chosen to be primes that are cheap + # to calculate via shift-and-add, so don't change them without care. + # (Commonly, random seeds are tried, but we want reproducible results + # from this program so we don't do that.) + my $hash_mult1 = 257; + my $hash_mult2; + my $hash_seed1; + my $hash_seed2; + my @subresult; + FIND_PARAMS: + for ($hash_seed1 = 0; $hash_seed1 < 10; $hash_seed1++) + { + + for ($hash_seed2 = 0; $hash_seed2 < 10; $hash_seed2++) + { + foreach (17, 31, 127, 8191) + { + $hash_mult2 = $_; # "foreach $hash_mult2" doesn't work + @subresult = _construct_hash_table( + $keys_ref, $hash_mult1, $hash_mult2, + $hash_seed1, $hash_seed2); + last FIND_PARAMS if @subresult; + } + } + } + + # Choke if we couldn't find a workable set of parameters. + die "failed to generate perfect hash" if !@subresult; + + # Extract info from _construct_hash_table's result array. + my $elemtype = $subresult[0]; + my @hashtab = @{ $subresult[1] }; + my $nhash = scalar(@hashtab); + + # OK, construct the hash function definition including the hash table. + my $f = ''; + $f .= sprintf "int\n"; + if (defined $options{fixed_key_length}) + { + $f .= sprintf "%s(const void *key)\n{\n", $funcname; + } + else + { + $f .= sprintf "%s(const void *key, size_t keylen)\n{\n", $funcname; + } + $f .= sprintf "\tstatic const %s h[%d] = {\n\t\t", $elemtype, $nhash; + for (my $i = 0; $i < $nhash; $i++) + { + # Hash element. + $f .= sprintf "%d", $hashtab[$i]; + next if ($i == $nhash - 1); + + # Optional indentation and newline, with eight items per line. + $f .= sprintf ",%s", + ($i % 8 == 7 ? "\n\t\t" : ' ' x (6 - length($hashtab[$i]))); + } + $f .= sprintf "\n" if ($nhash % 8 != 0); + $f .= sprintf "\t};\n\n"; + $f .= sprintf "\tconst unsigned char *k = (const unsigned char *) key;\n"; + $f .= sprintf "\tsize_t\t\tkeylen = %d;\n", $options{fixed_key_length} + if (defined $options{fixed_key_length}); + $f .= sprintf "\tuint32\t\ta = %d;\n", $hash_seed1; + $f .= sprintf "\tuint32\t\tb = %d;\n\n", $hash_seed2; + $f .= sprintf "\twhile (keylen--)\n\t{\n"; + $f .= sprintf "\t\tunsigned char c = *k++"; + $f .= sprintf " | 0x20" if $case_fold; # see comment below + $f .= sprintf ";\n\n"; + $f .= sprintf "\t\ta = a * %d + c;\n", $hash_mult1; + $f .= sprintf "\t\tb = b * %d + c;\n", $hash_mult2; + $f .= sprintf "\t}\n"; + $f .= sprintf "\treturn h[a %% %d] + h[b %% %d];\n", $nhash, $nhash; + $f .= sprintf "}\n"; + + return $f; +} + + +# Calculate a hash function as the run-time code will do. +# +# If we are making a case-insensitive hash function, we implement that +# by OR'ing 0x20 into each byte of the key. This correctly transforms +# upper-case ASCII into lower-case ASCII, while not changing digits or +# dollar signs. (It does change '_', as well as other characters not +# likely to appear in keywords; this has little effect on the hash's +# ability to discriminate keywords.) +sub _calc_hash +{ + my ($key, $mult, $seed) = @_; + + my $result = $seed; + for my $c (split //, $key) + { + my $cn = ord($c); + $cn |= 0x20 if $case_fold; + $result = ($result * $mult + $cn) % 4294967296; + } + return $result; +} + + +# Attempt to construct a mapping table for a minimal perfect hash function +# for the given keys, using the specified hash parameters. +# +# Returns an array containing the mapping table element type name as the +# first element, and a ref to an array of the table values as the second. +# +# Returns an empty array on failure; then caller should choose different +# hash parameter(s) and try again. +sub _construct_hash_table +{ + my ($keys_ref, $hash_mult1, $hash_mult2, $hash_seed1, $hash_seed2) = @_; + my @keys = @{$keys_ref}; + + # This algorithm is based on a graph whose edges correspond to the + # keys and whose vertices correspond to entries of the mapping table. + # A key's edge links the two vertices whose indexes are the outputs of + # the two hash functions for that key. For K keys, the mapping + # table must have at least 2*K+1 entries, guaranteeing that there's at + # least one unused entry. (In principle, larger mapping tables make it + # easier to find a workable hash and increase the number of inputs that + # can be rejected due to touching unused hashtable entries. In practice, + # neither effect seems strong enough to justify using a larger table.) + my $nedges = scalar @keys; # number of edges + my $nverts = 2 * $nedges + 1; # number of vertices + + # However, it would be very bad if $nverts were exactly equal to either + # $hash_mult1 or $hash_mult2: effectively, that hash function would be + # sensitive to only the last byte of each key. Cases where $nverts is a + # multiple of either multiplier likewise lose information. (But $nverts + # can't actually divide them, if they've been intelligently chosen as + # primes.) We can avoid such problems by adjusting the table size. + while ($nverts % $hash_mult1 == 0 + || $nverts % $hash_mult2 == 0) + { + $nverts++; + } + + # Initialize the array of edges. + my @E = (); + foreach my $kw (@keys) + { + # Calculate hashes for this key. + # The hashes are immediately reduced modulo the mapping table size. + my $hash1 = _calc_hash($kw, $hash_mult1, $hash_seed1) % $nverts; + my $hash2 = _calc_hash($kw, $hash_mult2, $hash_seed2) % $nverts; + + # If the two hashes are the same for any key, we have to fail + # since this edge would itself form a cycle in the graph. + return () if $hash1 == $hash2; + + # Add the edge for this key. + push @E, { left => $hash1, right => $hash2 }; + } + + # Initialize the array of vertices, giving them all empty lists + # of associated edges. (The lists will be hashes of edge numbers.) + my @V = (); + for (my $v = 0; $v < $nverts; $v++) + { + push @V, { edges => {} }; + } + + # Insert each edge in the lists of edges connected to its vertices. + for (my $e = 0; $e < $nedges; $e++) + { + my $v = $E[$e]{left}; + $V[$v]{edges}->{$e} = 1; + + $v = $E[$e]{right}; + $V[$v]{edges}->{$e} = 1; + } + + # Now we attempt to prove the graph acyclic. + # A cycle-free graph is either empty or has some vertex of degree 1. + # Removing the edge attached to that vertex doesn't change this property, + # so doing that repeatedly will reduce the size of the graph. + # If the graph is empty at the end of the process, it was acyclic. + # We track the order of edge removal so that the next phase can process + # them in reverse order of removal. + my @output_order = (); + + # Consider each vertex as a possible starting point for edge-removal. + for (my $startv = 0; $startv < $nverts; $startv++) + { + my $v = $startv; + + # If vertex v is of degree 1 (i.e. exactly 1 edge connects to it), + # remove that edge, and then consider the edge's other vertex to see + # if it is now of degree 1. The inner loop repeats until reaching a + # vertex not of degree 1. + while (scalar(keys(%{ $V[$v]{edges} })) == 1) + { + # Unlink its only edge. + my $e = (keys(%{ $V[$v]{edges} }))[0]; + delete($V[$v]{edges}->{$e}); + + # Unlink the edge from its other vertex, too. + my $v2 = $E[$e]{left}; + $v2 = $E[$e]{right} if ($v2 == $v); + delete($V[$v2]{edges}->{$e}); + + # Push e onto the front of the output-order list. + unshift @output_order, $e; + + # Consider v2 on next iteration of inner loop. + $v = $v2; + } + } + + # We succeeded only if all edges were removed from the graph. + return () if (scalar(@output_order) != $nedges); + + # OK, build the hash table of size $nverts. + my @hashtab = (0) x $nverts; + # We need a "visited" flag array in this step, too. + my @visited = (0) x $nverts; + + # The goal is that for any key, the sum of the hash table entries for + # its first and second hash values is the desired output (i.e., the key + # number). By assigning hash table values in the selected edge order, + # we can guarantee that that's true. This works because the edge first + # removed from the graph (and hence last to be visited here) must have + # at least one vertex it shared with no other edge; hence it will have at + # least one vertex (hashtable entry) still unvisited when we reach it here, + # and we can assign that unvisited entry a value that makes the sum come + # out as we wish. By induction, the same holds for all the other edges. + foreach my $e (@output_order) + { + my $l = $E[$e]{left}; + my $r = $E[$e]{right}; + if (!$visited[$l]) + { + # $hashtab[$r] might be zero, or some previously assigned value. + $hashtab[$l] = $e - $hashtab[$r]; + } + else + { + die "oops, doubly used hashtab entry" if $visited[$r]; + # $hashtab[$l] might be zero, or some previously assigned value. + $hashtab[$r] = $e - $hashtab[$l]; + } + # Now freeze both of these hashtab entries. + $visited[$l] = 1; + $visited[$r] = 1; + } + + # Detect range of values needed in hash table. + my $hmin = $nedges; + my $hmax = 0; + for (my $v = 0; $v < $nverts; $v++) + { + $hmin = $hashtab[$v] if $hashtab[$v] < $hmin; + $hmax = $hashtab[$v] if $hashtab[$v] > $hmax; + } + + # Choose width of hashtable entries. In addition to the actual values, + # we need to be able to store a flag for unused entries, and we wish to + # have the property that adding any other entry value to the flag gives + # an out-of-range result (>= $nedges). + my $elemtype; + my $unused_flag; + + if ( $hmin >= -0x7F + && $hmax <= 0x7F + && $hmin + 0x7F >= $nedges) + { + # int8 will work + $elemtype = 'int8'; + $unused_flag = 0x7F; + } + elsif ($hmin >= -0x7FFF + && $hmax <= 0x7FFF + && $hmin + 0x7FFF >= $nedges) + { + # int16 will work + $elemtype = 'int16'; + $unused_flag = 0x7FFF; + } + elsif ($hmin >= -0x7FFFFFFF + && $hmax <= 0x7FFFFFFF + && $hmin + 0x3FFFFFFF >= $nedges) + { + # int32 will work + $elemtype = 'int32'; + $unused_flag = 0x3FFFFFFF; + } + else + { + die "hash table values too wide"; + } + + # Set any unvisited hashtable entries to $unused_flag. + for (my $v = 0; $v < $nverts; $v++) + { + $hashtab[$v] = $unused_flag if !$visited[$v]; + } + + return ($elemtype, \@hashtab); +} + +1; diff --git a/src/tools/RELEASE_CHANGES b/src/tools/RELEASE_CHANGES new file mode 100644 index 0000000..e8de724 --- /dev/null +++ b/src/tools/RELEASE_CHANGES @@ -0,0 +1,248 @@ +For All Releases (major, minor, beta, RC) +================ + +* Release version number changes + o run src/tools/version_stamp.pl, then run autoconf + (by packager) (beta) + +* Release notes + o run git log and, if useful, src/tools/git_changelog + o update doc/src/sgml/release-NN.sgml in relevant branches + o run spellchecker on result + o add SGML markup + +* Update timezone data to match latest IANA timezone database and new + Windows releases, if any (see src/timezone/README) + +* Translation updates + 1. Check out the messages repository (of the right branch) from + <http://git.postgresql.org/git/pgtranslation/messages.git>. + 2. Check out the admin repository from + <http://git.postgresql.org/git/pgtranslation/admin.git>. + 3. From babel.postgresql.org, download the "qualified list" + for the respective branch. + 4. Run ".../admin/cp-po -L qualified-list-xxx.txt -g .../messages .../postgresql". + This creates a commit in the postgresql repository. + 5. Push everything. + + +For Major Releases +================== +(in addition to the above) + +Note that once the release branch has been forked off in git, +release-note editing happens in that branch, not in master. +Updates to the rest of the documentation usually need to happen +in both master and the branch. + +* Release notes + o use src/tools/git_changelog + o retain the relevant commits + o new features and options + o major performance improvements + o bug fixes for serious or common bugs + o incompatibilities and significant user-visible changes + o major source code changes + o update TODO list, http://wiki.postgresql.org/wiki/Todo + o verify items marked as completed are completed + o mark additional items as completed + o remove completed items + o group items into categories + o select incompatibilities + o add documentation links for items + o select major features + +* Documentation + o document all new features + o update help output from inside the programs + o doc/src/sgml/ref manual pages + +* Ports + o update ports list in doc/src/sgml/installation.sgml + + +Pre-Beta Tasks +============== + +These things should be done at least once per development cycle. +Typically we do them between feature freeze and start of beta test, +but there may be reasons to do them at other times as well. + +* Run mechanical code beautification tools: + pgindent, pgperltidy, and "make reformat-dat-files" + (complete steps from src/tools/pgindent/README) + +* Renumber any manually-assigned OIDs between 8000 and 9999 + to lower numbers, using renumber_oids.pl (see notes in bki.sgml) + +* Update config.guess and config.sub + (from https://savannah.gnu.org/projects/config) + +* Update Unicode data: Edit UNICODE_VERSION and CLDR_VERSION in + src/Makefile.global.in, run make update-unicode, and commit. + + +Starting a New Development Cycle +================================ + +* Typically, we do pgindent and perltidy runs just before branching, + as well as before beta (complete steps from src/tools/pgindent/README) + +* Create a branch in git for maintenance of the previous release + o on master branch, do: + git pull # be sure you have the latest "master" + git branch "new-branch-name" + git push -u origin "new-branch-name" + for example, + git branch REL_11_STABLE + git push -u origin REL_11_STABLE + +* Add new branch's name to list in src/tools/git_changelog + +* Increment the major version number in src/tools/version_stamp.pl + +* Run "src/tools/version_stamp.pl devel", then run autoconf + +* Create a new doc/src/sgml/release-NN.sgml file (initially just a + placeholder), "git rm" the previous one, and update release.sgml and + filelist.sgml to match. + +* Notify the private committers email list, to ensure all committers + are aware of the new branch even if they're not paying close attention + to pgsql-hackers. + +* Get the buildfarm's 'branches_of_interest.txt' file updated with the new + branch. Once the buildfarm server is accepting reports, notify the + buildfarm owners' email list, for the benefit of owners who use manual + branch scheduling. + + +Creating Back-Branch Release Notes +================================== + +* Run src/tools/git_changelog to generate a list of relevant commits. + You can also run 'git log' in each branch. Be sure to use the --since + branch tag and not the release date, as commits could have been done + between branch stamping and the release date. + +* Remember to include any older branch commits not in the newest branch. + This can be accomplished by diff'ing the newest and older branch commit + logs and looking for lines that only appear in the older branch, e.g.: + + diff commit-N.N.log commit-O.O.log | grep '^>' + +* On the most recent release branch (*not* in master), edit and create SGML + markup for relevant changes in that branch's release-NN.sgml file. + Minor release notes should include more small change details because + testing is limited. + +* Copy this text into older branches' release-NN.sgml files, then remove + items that do not apply based on commit logs for that branch. + +* The minor release notes for the oldest active branch should always + include a warning about its impending EOL. Use the same boilerplate + text used in previous branches. + + +Retiring a Branch +================= + +* Notify the private committers email list, to ensure all committers + are aware of the branch being dead. + +* Get the buildfarm's 'branches_of_interest.txt' file updated to remove + the retired branch. Then notify the buildfarm owners' email list, + for the benefit of owners who use manual branch scheduling. + + + +--------------------------------------------------------------------------- + + Library Version Changes + ======================= + +Major Version +============= + +The major version number should be updated whenever the source of the +library changes to make it binary incompatible. Such changes include, +but are not limited to: + +1. Removing a public function or structure (or typedef, enum, ...) + +2. Modifying a public functions arguments. + +3. Removing a field from a public structure. + +4. Adding a field to a public structure, unless steps have been +previously taken to shield users from such a change, for example by +such structures only ever being allocated/instantiated by a library +function which would give the new field a suitable default value. + +Adding a new function should NOT force an increase in the major version +number. (Packagers will see the standard minor number update and install +the new library.) When the major version is increased all applications +which link to the library MUST be recompiled - this is not desirable. + +Minor Version +============= + +The minor version number should be updated whenever the functionality of +the library has changed, typically a change in source code between releases +would mean an increase in the minor version number so long as it does not +require a major version increase. + +Given that we make at least some changes to our libraries in every major +PostgreSQL version, we always bump all minor library version numbers in +each development cycle as a matter of policy. This is currently mechanized +by referencing the MAJORVERSION make macro in the value of SO_MINOR_VERSION +for each shared library. As of v10, SO_MINOR_VERSION is simply equal to +MAJORVERSION in all cases. If we ever make an incompatible break in a +library's API, forcing a major version bump, we could continue to increase +SO_MINOR_VERSION (thus, perhaps, going from libpq.so.5.12 to libpq.so.6.13), +or we could reset SO_MINOR_VERSION to zero, using makefile code along the +lines of + SO_MINOR_VERSION= $(shell expr $(MAJORVERSION) - 13) +so that the number continues to increase automatically in later branches. +For now, that complication is not necessary. + +Minimizing Changes +================== + +When modifying public functions arguments, steps should be taken to +maintain binary compatibility across minor PostgreSQL releases (e.g. the +7.2 series, the 7.3 series, the 7.4/8.0 series). Consider the following +function: + + void print_stuff(int arg1, int arg2) + { + printf("stuff: %d %d\n", arg1, arg2); + } + +If we wanted to add a third argument: + + void print_stuff(int arg1, int arg2, int arg3) + { + printf("stuff: %d %d %d\n", arg1, arg2, arg3); + } + +Then doing it like this: + + void print_stuff2(int arg1, int arg2, int arg3) + { + printf("stuff: %d %d %d\n", arg1, arg2, arg3); + } + + void print_stuff(int arg1, int arg2) + { + print_stuff2(arg1, arg2, 0); + } + +would maintain binary compatibility. Obviously this would add a fair +bit of cruft if used extensively, but considering the changes between +minor versions would probably be worthwhile to avoid bumping library +major version. Naturally in the next major version print_stuff() would +assume the functionality and arguments of print_stuff2(). + + +Lee Kindness diff --git a/src/tools/ccsym b/src/tools/ccsym new file mode 100755 index 0000000..972c843 --- /dev/null +++ b/src/tools/ccsym @@ -0,0 +1,4 @@ +#!/bin/sh + +# display gcc predefined preprocessor macros +gcc -dM -E - < /dev/null diff --git a/src/tools/check_bison_recursion.pl b/src/tools/check_bison_recursion.pl new file mode 100755 index 0000000..6f748ae --- /dev/null +++ b/src/tools/check_bison_recursion.pl @@ -0,0 +1,90 @@ +#! /usr/bin/perl + +################################################################# +# +# check_bison_recursion.pl -- check for right recursion in Bison grammars +# +# The standard way to parse list constructs in Bison grammars is via left +# recursion, wherein a nonterminal symbol has itself as the first symbol +# in one of its expansion rules. It is also possible to parse a list via +# right recursion, wherein a nonterminal symbol has itself as the last +# symbol of an expansion; but that's a bad way to write it because a long +# enough list will result in parser stack overflow. Since Bison doesn't +# have any built-in way to warn about use of right recursion, we use this +# script when we want to check for the problem. +# +# To use: run bison with the -v switch, then feed the produced y.output +# file to this script. +# +# Copyright (c) 2011-2022, PostgreSQL Global Development Group +# +# src/tools/check_bison_recursion.pl +################################################################# + +use strict; +use warnings; + +my $debug = 0; + +# must retain this across input lines +my $cur_nonterminal; + +# We parse the input and emit warnings on the fly. +my $in_grammar = 0; + +while (<>) +{ + my $rule_number; + my $rhs; + + # We only care about the "Grammar" part of the input. + if (m/^Grammar$/) + { + $in_grammar = 1; + } + elsif (m/^Terminal/) + { + $in_grammar = 0; + } + elsif ($in_grammar) + { + if (m/^\s*(\d+)\s+(\S+):\s+(.*)$/) + { + + # first rule for nonterminal + $rule_number = $1; + $cur_nonterminal = $2; + $rhs = $3; + } + elsif (m/^\s*(\d+)\s+\|\s+(.*)$/) + { + + # additional rule for nonterminal + $rule_number = $1; + $rhs = $2; + } + } + + # Process rule if we found one + if (defined $rule_number) + { + + # deconstruct the RHS + $rhs =~ s|^/\* empty \*/$||; + my @rhs = split '\s', $rhs; + print "Rule $rule_number: $cur_nonterminal := @rhs\n" if $debug; + + # We complain if the nonterminal appears as the last RHS element + # but not elsewhere, since "expr := expr + expr" is reasonable + my $lastrhs = pop @rhs; + if ( defined $lastrhs + && $cur_nonterminal eq $lastrhs + && !grep { $cur_nonterminal eq $_ } @rhs) + { + print + "Right recursion in rule $rule_number: $cur_nonterminal := $rhs\n"; + } + } +} + +exit 0; diff --git a/src/tools/ci/README b/src/tools/ci/README new file mode 100644 index 0000000..6c1cac8 --- /dev/null +++ b/src/tools/ci/README @@ -0,0 +1,67 @@ +Postgres Continuous Integration (CI) +==================================== + +Postgres has two forms of CI: + +1) All supported branches in the main postgres repository are continuously + tested via the buildfarm. As this covers only the main repository, it + cannot be used during development of features. + + For details see https://buildfarm.postgresql.org/ + +2) For not yet merged development work, CI can be enabled for some git hosting + providers. This allows developers to test patches on a number of platforms + before they are merged (or even submitted). + + +Configuring CI on personal repositories +======================================= + +Currently postgres contains CI support utilizing cirrus-ci. cirrus-ci +currently is only available for github. + + +Enabling cirrus-ci in a github repository +========================================= + +To enable cirrus-ci on a repository, go to +https://github.com/marketplace/cirrus-ci and select "Public +Repositories". Then "Install it for free" and "Complete order". The next page +allows to configure which repositories cirrus-ci has access to. Choose the +relevant repository and "Install". + +See also https://cirrus-ci.org/guide/quick-start/ + +Once enabled on a repository, future commits and pull-requests in that +repository will automatically trigger CI builds. These are visible from the +commit history / PRs, and can also be viewed in the cirrus-ci UI at +https://cirrus-ci.com/github/<username>/<reponame>/ + +Hint: all build log files are uploaded to cirrus-ci and can be downloaded +from the "Artifacts" section fron the cirrus-ci UI after clicking into a +specific task on a build's summary page. + + +Images used for CI +================== + +To keep CI times tolerable, most platforms use pre-generated images. Some +platforms use containers, others use full VMs. Images for both are generated +separately from CI runs, otherwise each git repository that is being tested +would need to build its own set of containers, which would be wasteful (both +in space and time. + +These images are built, on a daily basis, from the specifications in +github.com/anarazel/pg-vm-images/ + + +Controlling CI via commit messages +================================== + +The behavior of CI can be controlled by special content in commit +messages. Currently the following controls are available: + +- ci-os-only: {(freebsd|linux|macos|windows)} + + Only runs CI on operating systems specified. This can be useful when + addressing portability issues affecting only a subset of platforms. diff --git a/src/tools/ci/cores_backtrace.sh b/src/tools/ci/cores_backtrace.sh new file mode 100755 index 0000000..28d3cec --- /dev/null +++ b/src/tools/ci/cores_backtrace.sh @@ -0,0 +1,50 @@ +#! /bin/sh + +if [ $# -ne 2 ]; then + echo "cores_backtrace.sh <os> <directory>" + exit 1 +fi + +os=$1 +directory=$2 + +case $os in + freebsd|linux|macos) + ;; + *) + echo "unsupported operating system ${os}" + exit 1 + ;; +esac + +first=1 +for corefile in $(find "$directory" -type f) ; do + if [ "$first" -eq 1 ]; then + first=0 + else + # to make it easier to separate the different crash reports + echo -e '\n\n' + fi + + if [ "$os" = 'macos' ]; then + lldb -c $corefile --batch -o 'thread backtrace all' -o 'quit' + else + auxv=$(gdb --quiet --core ${corefile} --batch -ex 'info auxv' 2>/dev/null) + if [ $? -ne 0 ]; then + echo "could not process ${corefile}" + continue + fi + + if [ "$os" = 'freebsd' ]; then + binary=$(echo "$auxv" | grep AT_EXECPATH | perl -pe "s/^.*\"(.*)\"\$/\$1/g") + elif [ "$os" = 'linux' ]; then + binary=$(echo "$auxv" | grep AT_EXECFN | perl -pe "s/^.*\"(.*)\"\$/\$1/g") + else + echo 'should not get here' + exit 1 + fi + + echo "dumping ${corefile} for ${binary}" + gdb --batch --quiet -ex "thread apply all bt full" -ex "quit" "$binary" "$corefile" 2>/dev/null + fi +done diff --git a/src/tools/ci/gcp_freebsd_repartition.sh b/src/tools/ci/gcp_freebsd_repartition.sh new file mode 100755 index 0000000..2d5e173 --- /dev/null +++ b/src/tools/ci/gcp_freebsd_repartition.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e +set -x + +# The default filesystem on freebsd gcp images is very slow to run tests on, +# due to its 32KB block size +# +# XXX: It'd probably better to fix this in the image, using something like +# https://people.freebsd.org/~lidl/blog/re-root.html + +# fix backup partition table after resize +gpart recover da0 +gpart show da0 +# kill swap, so we can delete a partition +swapoff -a || true +# (apparently we can only have 4!?) +gpart delete -i 3 da0 +gpart add -t freebsd-ufs -l data8k -a 4096 da0 +gpart show da0 +newfs -U -b 8192 /dev/da0p3 + +# Migrate working directory +du -hs $CIRRUS_WORKING_DIR +mv $CIRRUS_WORKING_DIR $CIRRUS_WORKING_DIR.orig +mkdir $CIRRUS_WORKING_DIR +mount -o noatime /dev/da0p3 $CIRRUS_WORKING_DIR +cp -r $CIRRUS_WORKING_DIR.orig/* $CIRRUS_WORKING_DIR/ diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf new file mode 100644 index 0000000..d8faa9c --- /dev/null +++ b/src/tools/ci/pg_ci_base.conf @@ -0,0 +1,14 @@ +# Tends to produce too many core files, taking a long time +restart_after_crash = false + +# So that tests using the "manually" started postgres on windows can use +# prepared statements +max_prepared_transactions = 10 + +# Settings that make logs more useful +log_autovacuum_min_duration = 0 +log_checkpoints = true +log_connections = true +log_disconnections = true +log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] ' +log_lock_waits = true diff --git a/src/tools/ci/windows_build_config.pl b/src/tools/ci/windows_build_config.pl new file mode 100644 index 0000000..59268a0 --- /dev/null +++ b/src/tools/ci/windows_build_config.pl @@ -0,0 +1,13 @@ +use strict; +use warnings; + +our $config; + +$config->{"tap_tests"} = 1; +$config->{"asserts"} = 1; + +$config->{"openssl"} = "c:/openssl/1.1/"; +$config->{"perl"} = "c:/strawberry/$ENV{DEFAULT_PERL_VERSION}/perl/"; +$config->{"python"} = "c:/python/"; + +1; diff --git a/src/tools/codelines b/src/tools/codelines new file mode 100755 index 0000000..11e86ac --- /dev/null +++ b/src/tools/codelines @@ -0,0 +1,7 @@ +#!/bin/sh + +# src/tools/codelines + +# This script is used to compute the total number of "C" lines in the release +# This should be run from the top of the Git tree after a 'make distclean' +find . -name '*.[chyl]' | xargs cat| wc -l diff --git a/src/tools/copyright.pl b/src/tools/copyright.pl new file mode 100755 index 0000000..182177e --- /dev/null +++ b/src/tools/copyright.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl +################################################################# +# copyright.pl -- update copyright notices throughout the source tree, idempotently. +# +# Copyright (c) 2011-2022, PostgreSQL Global Development Group +# +# src/tools/copyright.pl +# +# FYI, Tie adds a trailing newline on the last line if missing. +################################################################# + +use strict; +use warnings; + +use File::Find; +use File::Basename; +use Tie::File; + +my $pgdg = 'PostgreSQL Global Development Group'; +my $cc = 'Copyright \(c\)'; +my $ccliteral = 'Copyright (c)'; + +# year-1900 is what localtime(time) puts in element 5 +my $year = 1900 + ${ [ localtime(time) ] }[5]; + +print "Using current year: $year\n"; + +find({ wanted => \&wanted, no_chdir => 1 }, '.'); + +sub wanted +{ + + # prevent corruption of git indexes by ignoring any .git/ + if (basename($_) eq '.git') + { + $File::Find::prune = 1; + return; + } + + return if !-f $File::Find::name || -l $File::Find::name; + + # skip file names with binary extensions + # How are these updated? bjm 2012-01-02 + return if ($_ =~ m/\.(ico|bin|po|key)$/); + + my @lines; + tie @lines, "Tie::File", $File::Find::name; + + # We process all lines because some files have copyright + # strings embedded in them, e.g. src/bin/psql/help.c + foreach my $line (@lines) + { + + # We only care about lines with a copyright notice. + next unless $line =~ m/$cc.*$pgdg/i; + + # Skip line if it already matches the current year; if not + # we get $year-$year, e.g. 2012-2012. + next if $line =~ m/$cc $year, $pgdg/i; + + # Skip already-updated lines too, to avoid unnecessary + # file updates. + next if $line =~ m/$cc \d{4}-$year, $pgdg/i; + + # Apply the update, relying on Tie::File to write the file. + $line =~ s/$cc (\d{4})-\d{4}, $pgdg/$ccliteral $1-$year, $pgdg/i; + $line =~ s/$cc (\d{4}), $pgdg/$ccliteral $1-$year, $pgdg/i; + } + untie @lines; + return; +} + +print "Manually update:\n"; +print " ./doc/src/sgml/legal.sgml in head and back branches\n"; +print " ./COPYRIGHT in back branches\n"; diff --git a/src/tools/editors/emacs.samples b/src/tools/editors/emacs.samples new file mode 100644 index 0000000..529c98a --- /dev/null +++ b/src/tools/editors/emacs.samples @@ -0,0 +1,91 @@ +;; -*- mode: emacs-lisp -*- + +;; This file contains code to set up Emacs to edit PostgreSQL source +;; code. Copy these snippets into your .emacs file or equivalent, or +;; use load-file to load this file directly. +;; +;; Note also that there is a .dir-locals.el file at the top of the +;; PostgreSQL source tree, which contains many of the settings shown +;; here (but not all, mainly because not all settings are allowed as +;; local variables). So for light editing, you might not need any +;; additional Emacs configuration. + + +;;; C files + +;; Style that matches the formatting used by +;; src/tools/pgindent/pgindent. Many extension projects also use this +;; style. +(c-add-style "postgresql" + '("bsd" + (c-auto-align-backslashes . nil) + (c-basic-offset . 4) + (c-offsets-alist . ((case-label . +) + (label . -) + (statement-case-open . +))) + (fill-column . 78) + (indent-tabs-mode . t) + (tab-width . 4))) + +(add-hook 'c-mode-hook + (defun postgresql-c-mode-hook () + (when (string-match "/postgres\\(ql\\)?/" buffer-file-name) + (c-set-style "postgresql") + ;; Don't override the style we just set with the style in + ;; `dir-locals-file'. Emacs 23.4.1 needs this; it is obsolete, + ;; albeit harmless, by Emacs 24.3.1. + (set (make-local-variable 'ignored-local-variables) + (append '(c-file-style) ignored-local-variables))))) + + +;;; Perl files + +;; Style that matches the formatting used by +;; src/tools/pgindent/perltidyrc. +(defun pgsql-perl-style () + "Perl style adjusted for PostgreSQL project" + (interactive) + (setq perl-brace-imaginary-offset 0) + (setq perl-brace-offset 0) + (setq perl-continued-statement-offset 2) + (setq perl-continued-brace-offset (- perl-continued-statement-offset)) + (setq perl-indent-level 4) + (setq perl-label-offset -2) + ;; Next two aren't marked safe-local-variable, so .dir-locals.el omits them. + (setq perl-indent-continued-arguments 4) + (setq perl-indent-parens-as-block t) + (setq indent-tabs-mode t) + (setq tab-width 4)) + +(add-hook 'perl-mode-hook + (defun postgresql-perl-mode-hook () + (when (string-match "/postgres\\(ql\\)?/" buffer-file-name) + (pgsql-perl-style)))) + + +;;; documentation files + +;; *.sgml files are actually XML +(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*\\.sgml\\'" . nxml-mode)) + +(add-hook 'nxml-mode-hook + (defun postgresql-xml-mode-hook () + (when (string-match "/postgres\\(ql\\)?/" buffer-file-name) + (setq fill-column 78) + (setq indent-tabs-mode nil)))) + +;; The *.xsl files use 2-space indent, which is consistent with +;; docbook-xsl sources and also the nxml-mode default. But the *.sgml +;; files use 1-space indent, mostly for historical reasons at this +;; point. +(add-hook 'nxml-mode-hook + (defun postgresql-xml-src-mode-hook () + (when (string-match "/postgres\\(ql\\)?/.*\\.sgml\\'" buffer-file-name) + (setq nxml-child-indent 1)))) + + +;;; Makefiles + +;; use GNU make mode instead of plain make mode +(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*Makefile.*" . makefile-gmake-mode)) +(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*\\.mk\\'" . makefile-gmake-mode)) diff --git a/src/tools/editors/vim.samples b/src/tools/editors/vim.samples new file mode 100644 index 0000000..ccbc93f --- /dev/null +++ b/src/tools/editors/vim.samples @@ -0,0 +1,17 @@ +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" These settings are appropriate for editing PostgreSQL code with vim +" +" You would copy this into your .vimrc or equivalent +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +:if match(getcwd(), "/pgsql") >=0 || match(getcwd(), "/postgresql") >= 0 + +: set cinoptions=(0 +: set tabstop=4 +: set shiftwidth=4 + +:endif + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/tools/find_badmacros b/src/tools/find_badmacros new file mode 100755 index 0000000..58f4308 --- /dev/null +++ b/src/tools/find_badmacros @@ -0,0 +1,22 @@ +#!/bin/sh + +# This script attempts to find bad ifdef's, i.e. ifdef's that use braces +# but not the do { ... } while (0) syntax +# +# src/tools/find_badmacros +# +# This is useful for running before pgindent + +for FILE +do + awk ' BEGIN {was_define = "N"} + { if (was_define == "Y" && + $0 ~ /^{/) + printf "%s %d\n", FILENAME, NR + if ($0 ~ /^#define/) + was_define = "Y" + else + was_define = "N" + }' "$FILE" + grep -on '^#define.*{' "$FILE" | grep -v 'do[ ]*{' +done diff --git a/src/tools/find_static b/src/tools/find_static new file mode 100755 index 0000000..1cc9ec3 --- /dev/null +++ b/src/tools/find_static @@ -0,0 +1,50 @@ +#!/bin/sh + +# src/tools/find_static + +trap "rm -f /tmp/$$" 0 1 2 3 15 + +# This script finds functions that are either never called, or +# should be static. +# Some functions, like library functions and debug_print functions, +# should remain unchanged. + +# Run on a compiled source tree, from the top of the source tree + +# My nm utility has 9 characters of address which I strip, then a 'type' +# character, with T as a text function, and U as an undefined function +# symbol, then the function name. + +find . -name '[a-z]*.o' -type f -print | while read FILE +do nm $FILE | cut -c17-100 |awk '{printf "%s\t%s\t%s\n", "'"$FILE"'",$1,$2}' +done >/tmp/$$ +dropdb debug +createdb debug +echo " + create table debug (file text, scope char, func text); + + copy debug from '/tmp/"$$"'; + + select * + into table debug2 + from debug; + + create index idebug on debug(scope,func); + create index idebug2 on debug2(func,scope); + vacuum debug; + vacuum debug2; + + update debug2 + set scope = '_' + from debug + where debug2.func = debug.func and + debug2.scope = 'T' and debug.scope = 'U'; + + delete from debug2 + where scope = '_'; + + select * + from debug2 + where scope = 'T' and func != 'main' + order by file, func; +" |psql -X debug diff --git a/src/tools/find_typedef b/src/tools/find_typedef new file mode 100755 index 0000000..24e9b76 --- /dev/null +++ b/src/tools/find_typedef @@ -0,0 +1,53 @@ +#!/bin/sh + +# src/tools/find_typedef + +# This script attempts to find all typedef's in the postgres binaries +# by using 'objdump' or local equivalent to print typedef debugging symbols. +# We need this because pgindent needs a list of typedef names. +# +# For this program to work, you must have compiled all code with +# debugging symbols. +# +# We intentionally examine all files in the targeted directories so as to +# find both .o files and executables. Therefore, ignore error messages about +# unsuitable files being fed to objdump. +# +# This is known to work on Linux and on some BSDen, including macOS. +# +# Caution: on the platforms we use, this only prints typedefs that are used +# to declare at least one variable or struct field. If you have say +# "typedef struct foo { ... } foo;", and then the structure is only ever +# referenced as "struct foo", "foo" will not be reported as a typedef, +# causing pgindent to indent the typedef definition oddly. This is not a +# huge problem, since by definition there's just the one misindented line. +# +# We get typedefs by reading "STABS": +# http://www.informatik.uni-frankfurt.de/doc/texi/stabs_toc.html + + +if [ "$#" -eq 0 -o ! -d "$1" ] +then echo "Usage: $0 postgres_binary_directory [...]" 1>&2 + exit 1 +fi + +for DIR +do # if objdump -W is recognized, only one line of error should appear + if [ `objdump -W 2>&1 | wc -l` -eq 1 ] + then # Linux + objdump -W "$DIR"/* | + egrep -A3 '\(DW_TAG_typedef\)' | + awk ' $2 == "DW_AT_name" {print $NF}' + elif [ `readelf -w 2>&1 | wc -l` -gt 1 ] + then # FreeBSD, similar output to Linux + readelf -w "$DIR"/* | + egrep -A3 '\(DW_TAG_typedef\)' | + awk ' $1 == "DW_AT_name" {print $NF}' + fi +done | +grep -v ' ' | # some typedefs have spaces, remove them +sort | +uniq | +# these are used both for typedefs and variable names +# so do not include them +egrep -v '^(date|interval|timestamp|ANY)$' diff --git a/src/tools/fix-old-flex-code.pl b/src/tools/fix-old-flex-code.pl new file mode 100644 index 0000000..8059cb5 --- /dev/null +++ b/src/tools/fix-old-flex-code.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl +#---------------------------------------------------------------------- +# +# fix-old-flex-code.pl +# +# flex versions before 2.5.36, with certain option combinations, produce +# code that causes an "unused variable" warning. That's annoying, so +# let's suppress it by inserting a dummy reference to the variable. +# (That's exactly what 2.5.36 and later do ...) +# +# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/fix-old-flex-code.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; + +# Get command line argument. +usage() if $#ARGV != 0; +my $filename = shift; + +# Suck in the whole file. +local $/ = undef; +my $cfile; +open($cfile, '<', $filename) || die "opening $filename for reading: $!"; +my $ccode = <$cfile>; +close($cfile); + +# No need to do anything if it's not flex 2.5.x for x < 36. +exit 0 if $ccode !~ m/^#define YY_FLEX_MAJOR_VERSION 2$/m; +exit 0 if $ccode !~ m/^#define YY_FLEX_MINOR_VERSION 5$/m; +exit 0 if $ccode !~ m/^#define YY_FLEX_SUBMINOR_VERSION (\d+)$/m; +exit 0 if $1 >= 36; + +# Apply the desired patch. +$ccode =~ + s|(struct yyguts_t \* yyg = \(struct yyguts_t\*\)yyscanner; /\* This var may be unused depending upon options. \*/ +.*?) + return yy_is_jam \? 0 : yy_current_state; +|$1 + (void) yyg; + return yy_is_jam ? 0 : yy_current_state; +|s; + +# Write the modified file back out. +open($cfile, '>', $filename) || die "opening $filename for writing: $!"; +print $cfile $ccode; +close($cfile); + +exit 0; + + +sub usage +{ + die <<EOM; +Usage: fix-old-flex-code.pl c-file-name + +fix-old-flex-code.pl modifies a flex output file to suppress +an unused-variable warning that occurs with older flex versions. + +Report bugs to <pgsql-bugs\@lists.postgresql.org>. +EOM +} diff --git a/src/tools/gen_keywordlist.pl b/src/tools/gen_keywordlist.pl new file mode 100644 index 0000000..88ea78f --- /dev/null +++ b/src/tools/gen_keywordlist.pl @@ -0,0 +1,197 @@ +#---------------------------------------------------------------------- +# +# gen_keywordlist.pl +# Perl script that transforms a list of keywords into a ScanKeywordList +# data structure that can be passed to ScanKeywordLookup(). +# +# The input is a C header file containing a series of macro calls +# PG_KEYWORD("keyword", ...) +# Lines not starting with PG_KEYWORD are ignored. The keywords are +# implicitly numbered 0..N-1 in order of appearance in the header file. +# Currently, the keywords are required to appear in ASCII order. +# +# The output is a C header file that defines a "const ScanKeywordList" +# variable named according to the -v switch ("ScanKeywords" by default). +# The variable is marked "static" unless the -e switch is given. +# +# ScanKeywordList uses hash-based lookup, so this script also selects +# a minimal perfect hash function for the keyword set, and emits a +# static hash function that is referenced in the ScanKeywordList struct. +# The hash function is case-insensitive unless --no-case-fold is specified. +# Note that case folding works correctly only for all-ASCII keywords! +# +# +# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/gen_keywordlist.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; +use Getopt::Long; + +use FindBin; +use lib $FindBin::RealBin; + +use PerfectHash; + +my $output_path = ''; +my $extern = 0; +my $case_fold = 1; +my $varname = 'ScanKeywords'; + +GetOptions( + 'output:s' => \$output_path, + 'extern' => \$extern, + 'case-fold!' => \$case_fold, + 'varname:s' => \$varname) || usage(); + +my $kw_input_file = shift @ARGV || die "No input file.\n"; + +# Make sure output_path ends in a slash if needed. +if ($output_path ne '' && substr($output_path, -1) ne '/') +{ + $output_path .= '/'; +} + +$kw_input_file =~ /(\w+)\.h$/ + || die "Input file must be named something.h.\n"; +my $base_filename = $1 . '_d'; +my $kw_def_file = $output_path . $base_filename . '.h'; + +open(my $kif, '<', $kw_input_file) || die "$kw_input_file: $!\n"; +open(my $kwdef, '>', $kw_def_file) || die "$kw_def_file: $!\n"; + +# Opening boilerplate for keyword definition header. +printf $kwdef <<EOM, $base_filename, uc $base_filename, uc $base_filename; +/*------------------------------------------------------------------------- + * + * %s.h + * List of keywords represented as a ScanKeywordList. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * NOTES + * ****************************** + * *** DO NOT EDIT THIS FILE! *** + * ****************************** + * + * It has been GENERATED by src/tools/gen_keywordlist.pl + * + *------------------------------------------------------------------------- + */ + +#ifndef %s_H +#define %s_H + +#include "common/kwlookup.h" + +EOM + +# Parse input file for keyword names. +my @keywords; +while (<$kif>) +{ + if (/^PG_KEYWORD\("(\w+)"/) + { + push @keywords, $1; + } +} + +# When being case-insensitive, insist that the input be all-lower-case. +if ($case_fold) +{ + foreach my $kw (@keywords) + { + die qq|The keyword "$kw" is not lower-case in $kw_input_file\n| + if ($kw ne lc $kw); + } +} + +# Error out if the keyword names are not in ASCII order. +# +# While this isn't really necessary with hash-based lookup, it's still +# helpful because it provides a cheap way to reject duplicate keywords. +# Also, insisting on sorted order ensures that code that scans the keyword +# table linearly will see the keywords in a canonical order. +for my $i (0 .. $#keywords - 1) +{ + die + qq|The keyword "$keywords[$i + 1]" is out of order in $kw_input_file\n| + if ($keywords[$i] cmp $keywords[ $i + 1 ]) >= 0; +} + +# Emit the string containing all the keywords. + +printf $kwdef qq|static const char %s_kw_string[] =\n\t"|, $varname; +print $kwdef join qq|\\0"\n\t"|, @keywords; +print $kwdef qq|";\n\n|; + +# Emit an array of numerical offsets which will be used to index into the +# keyword string. Also determine max keyword length. + +printf $kwdef "static const uint16 %s_kw_offsets[] = {\n", $varname; + +my $offset = 0; +my $max_len = 0; +foreach my $name (@keywords) +{ + my $this_length = length($name); + + print $kwdef "\t$offset,\n"; + + # Calculate the cumulative offset of the next keyword, + # taking into account the null terminator. + $offset += $this_length + 1; + + # Update max keyword length. + $max_len = $this_length if $max_len < $this_length; +} + +print $kwdef "};\n\n"; + +# Emit a macro defining the number of keywords. +# (In some places it's useful to have access to that as a constant.) + +printf $kwdef "#define %s_NUM_KEYWORDS %d\n\n", uc $varname, scalar @keywords; + +# Emit the definition of the hash function. + +my $funcname = $varname . "_hash_func"; + +my $f = PerfectHash::generate_hash_function(\@keywords, $funcname, + case_fold => $case_fold); + +printf $kwdef qq|static %s\n|, $f; + +# Emit the struct that wraps all this lookup info into one variable. + +printf $kwdef "static " if !$extern; +printf $kwdef "const ScanKeywordList %s = {\n", $varname; +printf $kwdef qq|\t%s_kw_string,\n|, $varname; +printf $kwdef qq|\t%s_kw_offsets,\n|, $varname; +printf $kwdef qq|\t%s,\n|, $funcname; +printf $kwdef qq|\t%s_NUM_KEYWORDS,\n|, uc $varname; +printf $kwdef qq|\t%d\n|, $max_len; +printf $kwdef "};\n\n"; + +printf $kwdef "#endif\t\t\t\t\t\t\t/* %s_H */\n", uc $base_filename; + + +sub usage +{ + die <<EOM; +Usage: gen_keywordlist.pl [--output/-o <path>] [--varname/-v <varname>] [--extern/-e] [--[no-]case-fold] input_file + --output Output directory (default '.') + --varname Name for ScanKeywordList variable (default 'ScanKeywords') + --extern Allow the ScanKeywordList variable to be globally visible + --no-case-fold Keyword matching is to be case-sensitive + +gen_keywordlist.pl transforms a list of keywords into a ScanKeywordList. +The output filename is derived from the input file by inserting _d, +for example kwlist_d.h is produced from kwlist.h. +EOM +} diff --git a/src/tools/git-external-diff b/src/tools/git-external-diff new file mode 100644 index 0000000..39ddd01 --- /dev/null +++ b/src/tools/git-external-diff @@ -0,0 +1,59 @@ +#!/bin/bash + +# This script is used to produce git context diffs + +# Supplied parameters: +# $1 $2 $3 $4 $5 $6 $7 +# path old-file old-hash old-mode new-file new-hash new-mode +# 'path' is the git-tree-relative path of the file being diff'ed + +=comment + +This info is copied from the old wiki page on Working with git: + +Context diffs with Git + +Copy git-external-diff into libexec/git-core/ of your git installation +and configure git to use that wrapper with: + + git config [--global] diff.external git-external-diff + +--global makes the configuration global for your user - otherwise it is +just configured for the current repository. + +For every command which displays diffs in some way you can use the +parameter "--[no-]-ext-diff" to enable respectively disable using the +external diff command. + +For the git diff command --ext-diff is enabled by default - for any +other command like git log -p or git format-patch it is not! + +This method should work on all platforms supported by git. + +If you do not want to configure the external wrapper permanently or you +want to overwrite it you can also invoke git like: + + export GIT_EXTERNAL_DIFF=git-external-diff + git diff --[no-]ext-diff + +Alternatively, configure a git alias in ~/.gitconfig or .git/config: + + [alias] + cdiff = !GIT_EXTERNAL_DIFF=git-context-diff git diff +=cut + +old_hash="$3" +new_hash=$(git hash-object "$5") + +# no change? +[ "$old_hash" = "$new_hash" ] && exit 0 + +[ "$DIFF_OPTS" = "" ] && DIFF_OPTS='-pcd' + +echo "diff --git a/$1 b/$1" +echo "new file mode $7" +echo "index ${old_hash:0:7}..${new_hash:0:7}" + +diff --label a/"$1" --label b/"$1" $DIFF_OPTS "$2" "$5" + +exit 0 diff --git a/src/tools/git_changelog b/src/tools/git_changelog new file mode 100755 index 0000000..189cfc8 --- /dev/null +++ b/src/tools/git_changelog @@ -0,0 +1,419 @@ +#!/usr/bin/perl + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# +# src/tools/git_changelog +# +# Display all commits on active branches, merging together commits from +# different branches that occur close together in time and with identical +# log messages. +# +# By default, commits are annotated with branch and release info thus: +# Branch: REL8_3_STABLE Release: REL8_3_2 [92c3a8004] 2008-03-29 00:15:37 +0000 +# This shows that the commit on REL8_3_STABLE was released in 8.3.2. +# Commits on master will usually instead have notes like +# Branch: master Release: REL8_4_BR [6fc9d4272] 2008-03-29 00:15:28 +0000 +# showing that this commit is ancestral to release branches 8.4 and later. +# If no Release: marker appears, the commit hasn't yet made it into any +# release. +# +# The --brief option shortens that to a format like: +# YYYY-MM-DD [hash] abbreviated commit subject line +# Since the branch isn't shown, this is mainly useful in conjunction +# with --master-only. +# +# Most of the time, matchable commits occur in the same order on all branches, +# and we print them out in that order. However, if commit A occurs before +# commit B on branch X and commit B occurs before commit A on branch Y, then +# there's no ordering which is consistent with both branches. In such cases +# we sort a merged commit according to its timestamp on the newest branch +# it appears in. +# +# The default output of this script is meant for generating minor release +# notes, where we need to know which branches a merged commit affects. +# +# To generate major release notes, use: +# git_changelog --master-only --brief --oldest-first --since='start-date' +# To find the appropriate start date, use: +# git show --summary $(git merge-base REL_12_STABLE master) +# where the branch to mention is the previously forked-off branch. This +# shows the last commit before that branch was made. +# +# Note that --master-only is an imperfect filter, since it will not detect +# cases where a master patch was back-patched awhile later or with a slightly +# different commit message. To find such cases, it's a good idea to look +# through the output of +# git_changelog --non-master-only --oldest-first --since='start-date' +# and then remove anything from the --master-only output that would be +# duplicative. + + +use strict; +use warnings; +require Time::Local; +require Getopt::Long; +require IPC::Open2; + +# Adjust this list when the set of interesting branches changes. +# (We could get this from "git branches", but not worth the trouble.) +# NB: master must be first! +my @BRANCHES = qw(master + REL_14_STABLE REL_13_STABLE + REL_12_STABLE REL_11_STABLE REL_10_STABLE REL9_6_STABLE REL9_5_STABLE + REL9_4_STABLE REL9_3_STABLE REL9_2_STABLE REL9_1_STABLE REL9_0_STABLE + REL8_4_STABLE REL8_3_STABLE REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE + REL7_4_STABLE REL7_3_STABLE REL7_2_STABLE REL7_1_STABLE REL7_0_PATCHES + REL6_5_PATCHES REL6_4); + +# Might want to make this parameter user-settable. +my $timestamp_slop = 24 * 60 * 60; + +my $brief = 0; +my $details_after = 0; +my $post_date = 0; +my $master_only = 0; +my $non_master_only = 0; +my $oldest_first = 0; +my $since; +my @output_buffer; +my $output_line = ''; + +Getopt::Long::GetOptions( + 'brief' => \$brief, + 'details-after' => \$details_after, + 'master-only' => \$master_only, + 'non-master-only' => \$non_master_only, + 'post-date' => \$post_date, + 'oldest-first' => \$oldest_first, + 'since=s' => \$since) || usage(); +usage() if @ARGV; + +my @git = qw(git log --format=fuller --date=iso); +push @git, '--since=' . $since if defined $since; + +# Collect the release tag data +my %rel_tags; + +{ + my $cmd = "git for-each-ref refs/tags"; + my $pid = IPC::Open2::open2(my $git_out, my $git_in, $cmd) + || die "can't run $cmd: $!"; + while (my $line = <$git_out>) + { + if ($line =~ m|^([a-f0-9]+)\s+commit\s+refs/tags/(\S+)|) + { + my $commit = $1; + my $tag = $2; + if ( $tag =~ /^REL_\d+_\d+$/ + || $tag =~ /^REL\d+_\d+$/ + || $tag =~ /^REL\d+_\d+_\d+$/) + { + $rel_tags{$commit} = $tag; + } + } + } + waitpid($pid, 0); + my $child_exit_status = $? >> 8; + die "$cmd failed" if $child_exit_status != 0; +} + +# Collect the commit data +my %all_commits; +my %all_commits_by_branch; + +# This remembers where each branch sprouted from master. Note the values +# will be wrong if --since terminates the log listing before the branch +# sprouts; but in that case it doesn't matter since we also won't reach +# the part of master where it would matter. +my %sprout_tags; + +for my $branch (@BRANCHES) +{ + my @cmd = @git; + if ($branch eq "master") + { + push @cmd, "origin/$branch"; + } + else + { + push @cmd, "--parents"; + push @cmd, "master..origin/$branch"; + } + my $pid = IPC::Open2::open2(my $git_out, my $git_in, @cmd) + || die "can't run @cmd: $!"; + my $last_tag = undef; + my $last_parent; + my %commit; + while (my $line = <$git_out>) + { + if ($line =~ /^commit\s+(\S+)/) + { + push_commit(\%commit) if %commit; + $last_tag = $rel_tags{$1} if defined $rel_tags{$1}; + %commit = ( + 'branch' => $branch, + 'commit' => $1, + 'last_tag' => $last_tag, + 'message' => '',); + if ($line =~ /^commit\s+\S+\s+(\S+)/) + { + $last_parent = $1; + } + else + { + $last_parent = undef; + } + } + elsif ($line =~ /^Author:\s+(.*)/) + { + $commit{'author'} = $1; + } + elsif ($line =~ /^CommitDate:\s+(.*)/) + { + $commit{'date'} = $1; + } + elsif ($line =~ /^\s\s/) + { + $commit{'message'} .= $line; + } + } + push_commit(\%commit) if %commit; + $sprout_tags{$last_parent} = $branch if defined $last_parent; + waitpid($pid, 0); + my $child_exit_status = $? >> 8; + die "@cmd failed" if $child_exit_status != 0; +} + +# Run through the master branch and apply tags. We already tagged the other +# branches, but master needs a separate pass after we've acquired the +# sprout_tags data. Also, in post-date mode we need to add phony entries +# for branches that sprouted after a particular master commit was made. +{ + my $last_tag = undef; + my %sprouted_branches; + for my $cc (@{ $all_commits_by_branch{'master'} }) + { + my $commit = $cc->{'commit'}; + my $c = $cc->{'commits'}->[0]; + $last_tag = $rel_tags{$commit} if defined $rel_tags{$commit}; + if (defined $sprout_tags{$commit}) + { + $last_tag = $sprout_tags{$commit}; + + # normalize branch names for making sprout tags + $last_tag =~ s/^(REL_\d+).*/$1_BR/; + $last_tag =~ s/^(REL\d+_\d+).*/$1_BR/; + } + $c->{'last_tag'} = $last_tag; + if ($post_date) + { + if (defined $sprout_tags{$commit}) + { + $sprouted_branches{ $sprout_tags{$commit} } = 1; + } + + # insert new commits between master and any other commits + my @new_commits = (shift @{ $cc->{'commits'} }); + for my $branch (reverse sort keys %sprouted_branches) + { + my $ccopy = { %{$c} }; + $ccopy->{'branch'} = $branch; + push @new_commits, $ccopy; + } + $cc->{'commits'} = [ @new_commits, @{ $cc->{'commits'} } ]; + } + } +} + +my %position; +for my $branch (@BRANCHES) +{ + $position{$branch} = 0; +} + +while (1) +{ + my $best_branch; + my $best_timestamp; + for my $branch (@BRANCHES) + { + my $leader = $all_commits_by_branch{$branch}->[ $position{$branch} ]; + next if !defined $leader; + if (!defined $best_branch + || $leader->{'timestamp'} > $best_timestamp) + { + $best_branch = $branch; + $best_timestamp = $leader->{'timestamp'}; + } + } + last if !defined $best_branch; + my $winner = + $all_commits_by_branch{$best_branch}->[ $position{$best_branch} ]; + + my $print_it = 1; + if ($master_only) + { + $print_it = (@{ $winner->{'commits'} } == 1) + && ($winner->{'commits'}[0]->{'branch'} eq 'master'); + } + elsif ($non_master_only) + { + foreach my $c (@{ $winner->{'commits'} }) + { + $print_it = 0 if ($c->{'branch'} eq 'master'); + } + } + + if ($print_it) + { + output_details($winner) if (!$details_after); + output_str("%s", $winner->{'message'} . "\n"); + output_details($winner) if ($details_after); + unshift(@output_buffer, $output_line) if ($oldest_first); + $output_line = ''; + } + + $winner->{'done'} = 1; + for my $branch (@BRANCHES) + { + my $leader = $all_commits_by_branch{$branch}->[ $position{$branch} ]; + if (defined $leader && $leader->{'done'}) + { + ++$position{$branch}; + redo; + } + } +} + +print @output_buffer if ($oldest_first); + +sub push_commit +{ + my ($c) = @_; + my $ht = hash_commit($c); + my $ts = parse_datetime($c->{'date'}); + my $cc; + + # Note that this code will never merge two commits on the same branch, + # even if they have the same hash (author/message) and nearby + # timestamps. This means that there could be multiple potential + # matches when we come to add a commit from another branch. Prefer + # the closest-in-time one. + for my $candidate (@{ $all_commits{$ht} }) + { + my $diff = abs($ts - $candidate->{'timestamp'}); + if ($diff < $timestamp_slop + && !exists $candidate->{'branch_position'}{ $c->{'branch'} }) + { + if (!defined $cc + || $diff < abs($ts - $cc->{'timestamp'})) + { + $cc = $candidate; + } + } + } + if (!defined $cc) + { + $cc = { + 'author' => $c->{'author'}, + 'message' => $c->{'message'}, + 'commit' => $c->{'commit'}, + 'commits' => [], + 'timestamp' => $ts + }; + push @{ $all_commits{$ht} }, $cc; + } + + # stash only the fields we'll need later + my $smallc = { + 'branch' => $c->{'branch'}, + 'commit' => $c->{'commit'}, + 'date' => $c->{'date'}, + 'last_tag' => $c->{'last_tag'} + }; + push @{ $cc->{'commits'} }, $smallc; + push @{ $all_commits_by_branch{ $c->{'branch'} } }, $cc; + $cc->{'branch_position'}{ $c->{'branch'} } = + -1 + @{ $all_commits_by_branch{ $c->{'branch'} } }; + return; +} + +sub hash_commit +{ + my ($c) = @_; + return $c->{'author'} . "\0" . $c->{'message'}; +} + +sub parse_datetime +{ + my ($dt) = @_; + $dt =~ + /^(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)\s+([-+])(\d\d)(\d\d)$/; + my $gm = Time::Local::timegm($6, $5, $4, $3, $2 - 1, $1); + my $tzoffset = ($8 * 60 + $9) * 60; + $tzoffset = -$tzoffset if $7 eq '-'; + return $gm - $tzoffset; +} + +sub output_str +{ + ($oldest_first) ? ($output_line .= sprintf(shift, @_)) : printf(@_); + return; +} + +sub output_details +{ + my $item = shift; + + if ($details_after) + { + $item->{'author'} =~ m{^(.*?)\s*<[^>]*>$}; + + # output only author name, not email address + output_str("(%s)\n", $1); + } + else + { + output_str("Author: %s\n", $item->{'author'}); + } + foreach my $c (@{ $item->{'commits'} }) + { + if ($brief) + { + $item->{'message'} =~ m/^\s*(.*)/; + + output_str( + "%s [%s] %s\n", + substr($c->{'date'}, 0, 10), + substr($c->{'commit'}, 0, 9), + substr($1, 0, 56)); + } + else + { + output_str("Branch: %s ", $c->{'branch'}) + if (!$master_only); + output_str("Release: %s ", $c->{'last_tag'}) + if (defined $c->{'last_tag'}); + output_str("[%s] %s\n", substr($c->{'commit'}, 0, 9), + $c->{'date'}); + } + } + output_str("\n"); + return; +} + +sub usage +{ + print STDERR <<EOM; +Usage: git_changelog [--brief/-b] [--details-after/-d] [--master-only/-m] [--non-master-only/-n] [--oldest-first/-o] [--post-date/-p] [--since=SINCE] + --brief Shorten commit descriptions, omitting branch identification + --details-after Show branch and author info after the commit description + --master-only Show only commits made just in the master branch + --non-master-only Show only commits made just in back branches + --oldest-first Show oldest commits first + --post-date Show branches made after a commit occurred + --since Show only commits dated since SINCE +EOM + exit 1; +} diff --git a/src/tools/ifaddrs/.gitignore b/src/tools/ifaddrs/.gitignore new file mode 100644 index 0000000..2e90032 --- /dev/null +++ b/src/tools/ifaddrs/.gitignore @@ -0,0 +1 @@ +/test_ifaddrs diff --git a/src/tools/ifaddrs/Makefile b/src/tools/ifaddrs/Makefile new file mode 100644 index 0000000..2abffaf --- /dev/null +++ b/src/tools/ifaddrs/Makefile @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/tools/ifaddrs +# +# Copyright (c) 2003-2022, PostgreSQL Global Development Group +# +# src/tools/ifaddrs/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/tools/ifaddrs +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +libpq_backend_dir = $(top_builddir)/src/backend/libpq + +override CPPFLAGS := -I$(libpq_backend_dir) $(CPPFLAGS) + +OBJS = \ + test_ifaddrs.o + +all: test_ifaddrs + +test_ifaddrs: test_ifaddrs.o $(libpq_backend_dir)/ifaddr.o + $(CC) $(CFLAGS) test_ifaddrs.o $(libpq_backend_dir)/ifaddr.o $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) + +clean distclean maintainer-clean: + rm -f test_ifaddrs$(X) $(OBJS) diff --git a/src/tools/ifaddrs/README b/src/tools/ifaddrs/README new file mode 100644 index 0000000..9f92803 --- /dev/null +++ b/src/tools/ifaddrs/README @@ -0,0 +1,12 @@ +src/tools/ifaddrs/README + +test_ifaddrs +============ + +This program prints the addresses and netmasks of all the IPv4 and IPv6 +interfaces on the local machine. It is useful for testing that this +functionality works on various platforms. If "samehost" and "samenet" +in pg_hba.conf don't seem to work right, run this program to see what +is happening. + +Usage: test_ifaddrs diff --git a/src/tools/ifaddrs/test_ifaddrs.c b/src/tools/ifaddrs/test_ifaddrs.c new file mode 100644 index 0000000..b8dbb84 --- /dev/null +++ b/src/tools/ifaddrs/test_ifaddrs.c @@ -0,0 +1,73 @@ +/* + * src/tools/ifaddrs/test_ifaddrs.c + * + * + * test_ifaddrs.c + * test pg_foreach_ifaddr() + */ + +#include "postgres.h" + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "libpq/ifaddr.h" + + +static void +print_addr(struct sockaddr *addr) +{ + char buffer[256]; + int ret, + len; + + switch (addr->sa_family) + { + case AF_INET: + len = sizeof(struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; +#endif + default: + len = sizeof(struct sockaddr_storage); + break; + } + + ret = getnameinfo(addr, len, buffer, sizeof(buffer), NULL, 0, + NI_NUMERICHOST); + if (ret != 0) + printf("[unknown: family %d]", addr->sa_family); + else + printf("%s", buffer); +} + +static void +callback(struct sockaddr *addr, struct sockaddr *mask, void *unused) +{ + printf("addr: "); + print_addr(addr); + printf(" mask: "); + print_addr(mask); + printf("\n"); +} + +int +main(int argc, char *argv[]) +{ +#ifdef WIN32 + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + fprintf(stderr, "WSAStartup failed\n"); + return 1; + } +#endif + + if (pg_foreach_ifaddr(callback, NULL) < 0) + fprintf(stderr, "pg_foreach_ifaddr failed: %s\n", strerror(errno)); + return 0; +} diff --git a/src/tools/make_ctags b/src/tools/make_ctags new file mode 100755 index 0000000..9e952ce --- /dev/null +++ b/src/tools/make_ctags @@ -0,0 +1,53 @@ +#!/bin/sh + +# src/tools/make_ctags + +command -v ctags >/dev/null || \ + { echo "'ctags' program not found" 1>&2; exit 1; } + +trap "rm -f /tmp/$$" 0 1 2 3 15 +rm -f ./tags + +IS_EXUBERANT="" +ctags --version 2>&1 | grep Exuberant && IS_EXUBERANT="Y" + +# List of kinds supported by Exuberant Ctags 5.8 +# generated by ctags --list-kinds +# --c-kinds was called --c-types before 2003 +# c classes +# d macro definitions +# e enumerators (values inside an enumeration) +# f function definitions +# g enumeration names +# l local variables [off] +# m class, struct, and union members +# n namespaces +# p function prototypes [off] +# s structure names +# t typedefs +# u union names +# v variable definitions +# x external and forward variable declarations [off] + +if [ "$IS_EXUBERANT" ] +then FLAGS="--c-kinds=+dfmstuv" +else FLAGS="-dt" +fi + +# this is outputting the tags into the file 'tags', and appending +find `pwd`/ -type f -name '*.[chyl]' -print | + xargs ctags -a -f tags "$FLAGS" + +# Sorting non-Exuberant ctags file allows for fast searching of the tags file. +# Since etags file has a header that we cannot sort in with the other entries +# we skip the sort step. +if [ ! "$IS_EXUBERANT" -a ! "$EMACS_MODE" ] +then LC_ALL=C + export LC_ALL + sort tags >/tmp/$$ && mv /tmp/$$ tags +fi + +find . \( -name 'CVS' -prune \) -o \( -name .git -prune \) -o -type d -print | +while read DIR +do [ "$DIR" != "." ] && ln -f -s `echo "$DIR" | sed 's;/[^/]*;/..;g'`/tags "$DIR"/tags +done diff --git a/src/tools/make_etags b/src/tools/make_etags new file mode 100755 index 0000000..9288ef7 --- /dev/null +++ b/src/tools/make_etags @@ -0,0 +1,16 @@ +#!/bin/sh + +# src/tools/make_etags + +command -v etags >/dev/null || \ + { echo "'etags' program not found" 1>&2; exit 1; } + +rm -f ./TAGS + +find `pwd`/ -type f -name '*.[chyl]' -print | + xargs etags --append -o TAGS + +find . \( -name CVS -prune \) -o \( -name .git -prune \) -o -type d -print | +while read DIR +do [ "$DIR" != "." ] && ln -f -s `pwd`/TAGS "$DIR" +done diff --git a/src/tools/make_mkid b/src/tools/make_mkid new file mode 100755 index 0000000..6f16061 --- /dev/null +++ b/src/tools/make_mkid @@ -0,0 +1,11 @@ +#!/bin/sh + +# src/tools/make_mkid + +mkid `find \`pwd\`/ \( -name _deadcode -a -prune \) -o \ + -type f -name '*.[chyl]' -print|sed 's;//;/;g'` + +find . \( -name .git -a -prune \) -o -type d -print |while read DIR +do + [ "$DIR" != "." ] && ln -f -s `echo "$DIR" | sed 's;/[^/]*;/..;g'`/ID $DIR/ID +done diff --git a/src/tools/mark_pgdllimport.pl b/src/tools/mark_pgdllimport.pl new file mode 100755 index 0000000..7b51ae3 --- /dev/null +++ b/src/tools/mark_pgdllimport.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl + +#---------------------------------------------------------------------- +# +# mark_pgdllimport.pl +# Perl script that tries to add PGDLLIMPORT markings to PostgreSQL +# header files. +# +# This relies on a few idiosyncracies of the PostgreSQL coding style, +# such as the fact that we always use "extern" in function +# declarations, and that we don't use // comments. It's not very +# smart and may not catch all cases. +# +# It's probably a good idea to run pgindent on any files that this +# script modifies before committing. This script uses as arguments +# a list of the header files to scan for the markings. +# +# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/mark_pgdllimport.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; + +for my $include_file (@ARGV) +{ + open(my $rfh, '<', $include_file) || die "$include_file: $!"; + my $buffer = ''; + my $num_pgdllimport_added = 0; + + while (my $raw_line = <$rfh>) + { + my $needs_pgdllimport = 1; + + # By convention we declare global variables explicitly extern. We're + # looking for those not already marked with PGDLLIMPORT. + $needs_pgdllimport = 0 + if $raw_line !~ /^extern\s+/ + || $raw_line =~ /PGDLLIMPORT/; + + # Make a copy of the line and perform a simple-minded comment strip. + # Also strip trailing whitespace. + my $stripped_line = $raw_line; + $stripped_line =~ s/\/\*.*\*\///g; + $stripped_line =~ s/\s+$//; + + # Variable declarations should end in a semicolon. If we see an + # opening parenthesis, it's probably a function declaration. + $needs_pgdllimport = 0 + if $stripped_line !~ /;$/ + || $stripped_line =~ /\(/; + + # Add PGDLLIMPORT marker, if required. + if ($needs_pgdllimport) + { + $raw_line =~ s/^extern/extern PGDLLIMPORT/; + ++$num_pgdllimport_added; + } + + # Add line to buffer. + $buffer .= $raw_line; + } + + close($rfh); + + # If we added any PGDLLIMPORT markers, rewrite the file. + if ($num_pgdllimport_added > 0) + { + printf "%s: adding %d PGDLLIMPORT markers\n", + $include_file, $num_pgdllimport_added; + open(my $wfh, '>', $include_file) || die "$include_file: $!"; + print $wfh $buffer; + close($wfh); + } +} diff --git a/src/tools/msvc/.gitignore b/src/tools/msvc/.gitignore new file mode 100644 index 0000000..2470e78 --- /dev/null +++ b/src/tools/msvc/.gitignore @@ -0,0 +1,3 @@ +# Custom configuration files for MSVC build +/config.pl +/buildenv.pl diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm new file mode 100644 index 0000000..8de79c6 --- /dev/null +++ b/src/tools/msvc/Install.pm @@ -0,0 +1,779 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Install; + +# +# Package that provides 'make install' functionality for msvc builds +# +# src/tools/msvc/Install.pm +# +use strict; +use warnings; +use Carp; +use File::Basename; +use File::Copy; +use File::Find (); + +use Exporter; +our (@ISA, @EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT_OK = qw(Install); + +my $insttype; +my @client_contribs = ('oid2name', 'pgbench', 'vacuumlo'); +my @client_program_files = ( + 'clusterdb', 'createdb', 'createuser', 'dropdb', + 'dropuser', 'ecpg', 'libecpg', 'libecpg_compat', + 'libpgtypes', 'libpq', 'pg_amcheck', 'pg_basebackup', + 'pg_config', 'pg_dump', 'pg_dumpall', 'pg_isready', + 'pg_receivewal', 'pg_recvlogical', 'pg_restore', 'psql', + 'reindexdb', 'vacuumdb', @client_contribs); + +sub lcopy +{ + my $src = shift; + my $target = shift; + + if (-f $target) + { + unlink $target || confess "Could not delete $target\n"; + } + + (my $retval = copy($src, $target)) + || confess "Could not copy $src to $target\n"; + + return $retval; +} + +sub Install +{ + $| = 1; + + my $target = shift; + $insttype = shift; + $insttype = "all" unless ($insttype); + + # if called from vcregress, the config will be passed to us + # so no need to re-include these + our $config = shift; + unless ($config) + { + + # suppress warning about harmless redeclaration of $config + no warnings 'misc'; + do "./config_default.pl"; + do "./config.pl" if (-f "config.pl"); + } + + # Move to the root path depending on the current location. + if (-f "../../../configure") + { + chdir("../../.."); + } + elsif (-f "../../../../configure") + { + chdir("../../../.."); + } + + my $conf = ""; + if (-d "debug") + { + $conf = "debug"; + } + if (-d "release") + { + $conf = "release"; + } + die "Could not find debug or release binaries" if ($conf eq ""); + my $majorver = DetermineMajorVersion(); + print "Installing version $majorver for $conf in $target\n"; + + my @client_dirs = ('bin', 'lib', 'share', 'symbols'); + my @all_dirs = ( + @client_dirs, 'doc', 'doc/contrib', 'doc/extension', 'share/contrib', + 'share/extension', 'share/timezonesets', 'share/tsearch_data'); + if ($insttype eq "client") + { + EnsureDirectories($target, @client_dirs); + } + else + { + EnsureDirectories($target, @all_dirs); + } + + CopySolutionOutput($conf, $target); + my $sample_files = []; + my @top_dir = ("src"); + @top_dir = ("src\\bin", "src\\interfaces") if ($insttype eq "client"); + File::Find::find( + { + wanted => sub { + /^.*\.sample\z/s + && push(@$sample_files, $File::Find::name); + + # Don't find files of in-tree temporary installations. + $_ eq 'share' and $File::Find::prune = 1; + } + }, + @top_dir); + CopySetOfFiles('config files', $sample_files, $target . '/share/'); + CopyFiles( + 'Import libraries', + $target . '/lib/', + "$conf\\", "postgres\\postgres.lib", "libpgcommon\\libpgcommon.lib", + "libpgport\\libpgport.lib"); + CopyContribFiles($config, $target); + CopyIncludeFiles($target); + + if ($insttype ne "client") + { + CopySetOfFiles( + 'timezone names', + [ glob('src\timezone\tznames\*.txt') ], + $target . '/share/timezonesets/'); + CopyFiles( + 'timezone sets', + $target . '/share/timezonesets/', + 'src/timezone/tznames/', 'Default', 'Australia', 'India'); + CopySetOfFiles( + 'BKI files', + [ glob("src\\backend\\catalog\\postgres.*") ], + $target . '/share/'); + CopySetOfFiles( + 'SQL files', + [ glob("src\\backend\\catalog\\*.sql") ], + $target . '/share/'); + CopyFiles( + 'Information schema data', $target . '/share/', + 'src/backend/catalog/', 'sql_features.txt'); + CopyFiles( + 'Error code data', $target . '/share/', + 'src/backend/utils/', 'errcodes.txt'); + GenerateTimezoneFiles($target, $conf); + GenerateTsearchFiles($target); + CopySetOfFiles( + 'Stopword files', + [ glob("src\\backend\\snowball\\stopwords\\*.stop") ], + $target . '/share/tsearch_data/'); + CopySetOfFiles( + 'Dictionaries sample files', + [ glob("src\\backend\\tsearch\\dicts\\*_sample*") ], + $target . '/share/tsearch_data/'); + + my $pl_extension_files = []; + my @pldirs = ('src/pl/plpgsql/src'); + push @pldirs, "src/pl/plperl" if $config->{perl}; + push @pldirs, "src/pl/plpython" if $config->{python}; + push @pldirs, "src/pl/tcl" if $config->{tcl}; + File::Find::find( + { + wanted => sub { + /^(.*--.*\.sql|.*\.control)\z/s + && push(@$pl_extension_files, $File::Find::name); + + # Don't find files of in-tree temporary installations. + $_ eq 'share' and $File::Find::prune = 1; + } + }, + @pldirs); + CopySetOfFiles('PL Extension files', + $pl_extension_files, $target . '/share/extension/'); + } + + GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls}); + + print "Installation complete.\n"; + return; +} + +sub EnsureDirectories +{ + my $target = shift; + mkdir $target unless -d ($target); + while (my $d = shift) + { + mkdir $target . '/' . $d unless -d ($target . '/' . $d); + } + return; +} + +sub CopyFiles +{ + my $what = shift; + my $target = shift; + my $basedir = shift; + + print "Copying $what"; + while (my $f = shift) + { + print "."; + $f = $basedir . $f; + die "No file $f\n" if (!-f $f); + lcopy($f, $target . basename($f)) || croak "Could not copy $f: $!\n"; + } + print "\n"; + return; +} + +sub CopySetOfFiles +{ + my $what = shift; + my $flist = shift; + my $target = shift; + print "Copying $what" if $what; + foreach (@$flist) + { + my $tgt = $target . basename($_); + print "."; + lcopy($_, $tgt) || croak "Could not copy $_: $!\n"; + } + print "\n"; + return; +} + +sub CopySolutionOutput +{ + my $conf = shift; + my $target = shift; + my $rem = + qr{Project\("\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942\}"\) = "([^"]+)"}; + + my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n"; + + my $vcproj = 'vcproj'; + if ($sln =~ + /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/ + && $1 >= 11) + { + $vcproj = 'vcxproj'; + } + + print "Copying build output files..."; + while ($sln =~ $rem) + { + my $pf = $1; + + # Hash-of-arrays listing where to install things. For each + # subdirectory there's a hash key, and the value is an array + # of file extensions to install in that subdirectory. Example: + # { 'bin' => [ 'dll', 'lib' ], + # 'lib' => [ 'lib' ] } + my %install_list; + my $is_sharedlib = 0; + + $sln =~ s/$rem//; + + next + if ($insttype eq "client" && !grep { $_ eq $pf } + @client_program_files); + + my $proj = read_file("$pf.$vcproj") + || croak "Could not open $pf.$vcproj\n"; + + # Check if this project uses a shared library by looking if + # SO_MAJOR_VERSION is defined in its Makefile, whose path + # can be found using the resource file of this project. + if (( $vcproj eq 'vcxproj' + && $proj =~ qr{ResourceCompile\s*Include="([^"]+)"}) + || ( $vcproj eq 'vcproj' + && $proj =~ qr{File\s*RelativePath="([^\"]+)\.rc"})) + { + my $projpath = dirname($1); + my $mfname = + -e "$projpath/GNUmakefile" + ? "$projpath/GNUmakefile" + : "$projpath/Makefile"; + my $mf = read_file($mfname) || croak "Could not open $mfname\n"; + + $is_sharedlib = 1 if ($mf =~ /^SO_MAJOR_VERSION\s*=\s*(.*)$/mg); + } + + if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"}) + { + if ($1 == 1) + { + push(@{ $install_list{'bin'} }, "exe"); + } + elsif ($1 == 2) + { + push(@{ $install_list{'lib'} }, "dll"); + if ($is_sharedlib) + { + push(@{ $install_list{'bin'} }, "dll"); + push(@{ $install_list{'lib'} }, "lib"); + } + } + else + { + + # Static libraries, such as libpgport, only used internally + # during build, don't install. + next; + } + } + elsif ($vcproj eq 'vcxproj' + && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>}) + { + if ($1 eq 'Application') + { + push(@{ $install_list{'bin'} }, "exe"); + } + elsif ($1 eq 'DynamicLibrary') + { + push(@{ $install_list{'lib'} }, "dll"); + if ($is_sharedlib) + { + push(@{ $install_list{'bin'} }, "dll"); + push(@{ $install_list{'lib'} }, "lib"); + } + } + else # 'StaticLibrary' + { + + # Static lib, such as libpgport, only used internally + # during build, don't install. + next; + } + } + else + { + croak "Could not parse $pf.$vcproj\n"; + } + + # Install each element + foreach my $dir (keys %install_list) + { + foreach my $ext (@{ $install_list{$dir} }) + { + lcopy("$conf\\$pf\\$pf.$ext", "$target\\$dir\\$pf.$ext") + || croak "Could not copy $pf.$ext\n"; + } + } + lcopy("$conf\\$pf\\$pf.pdb", "$target\\symbols\\$pf.pdb") + || croak "Could not copy $pf.pdb\n"; + print "."; + } + print "\n"; + return; +} + +sub GenerateTimezoneFiles +{ + my $target = shift; + my $conf = shift; + my $mf = read_file("src/timezone/Makefile"); + $mf =~ s{\\\r?\n}{}g; + + $mf =~ /^TZDATAFILES\s*:?=\s*(.*)$/m + || die "Could not find TZDATAFILES line in timezone makefile\n"; + my @tzfiles = split /\s+/, $1; + + print "Generating timezone files..."; + + my @args = ("$conf/zic/zic", '-d', "$target/share/timezone"); + foreach (@tzfiles) + { + my $tzfile = $_; + $tzfile =~ s|\$\(srcdir\)|src/timezone|; + push(@args, $tzfile); + } + + system(@args); + print "\n"; + return; +} + +sub GenerateTsearchFiles +{ + my $target = shift; + + print "Generating tsearch script..."; + my $F; + my $tmpl = read_file('src/backend/snowball/snowball.sql.in'); + my $mf = read_file('src/backend/snowball/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ /^LANGUAGES\s*=\s*(.*)$/m + || die "Could not find LANGUAGES line in snowball Makefile\n"; + my @pieces = split /\s+/, $1; + open($F, '>', "$target/share/snowball_create.sql") + || die "Could not write snowball_create.sql"; + print $F read_file('src/backend/snowball/snowball_func.sql.in'); + + while ($#pieces > 0) + { + my $lang = shift @pieces || last; + my $asclang = shift @pieces || last; + my $txt = $tmpl; + my $stop = ''; + + if (-s "src/backend/snowball/stopwords/$lang.stop") + { + $stop = ", StopWords=$lang"; + } + + $txt =~ s#_LANGNAME_#${lang}#gs; + $txt =~ s#_DICTNAME_#${lang}_stem#gs; + $txt =~ s#_CFGNAME_#${lang}#gs; + $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs; + $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs; + $txt =~ s#_STOPWORDS_#$stop#gs; + print $F $txt; + print "."; + } + close($F); + print "\n"; + return; +} + +sub CopyContribFiles +{ + my $config = shift; + my $target = shift; + + print "Copying contrib data files..."; + foreach my $subdir ('contrib', 'src/test/modules') + { + my $D; + opendir($D, $subdir) || croak "Could not opendir on $subdir!\n"; + while (my $d = readdir($D)) + { + # These configuration-based exclusions must match vcregress.pl + next if ($d eq "uuid-ossp" && !defined($config->{uuid})); + next if ($d eq "sslinfo" && !defined($config->{openssl})); + next if ($d eq "pgcrypto" && !defined($config->{openssl})); + next if ($d eq "xml2" && !defined($config->{xml})); + next if ($d =~ /_plperl$/ && !defined($config->{perl})); + next if ($d =~ /_plpython$/ && !defined($config->{python})); + next if ($d eq "sepgsql"); + + CopySubdirFiles($subdir, $d, $config, $target); + } + } + print "\n"; + return; +} + +sub CopySubdirFiles +{ + my $subdir = shift; + my $module = shift; + my $config = shift; + my $target = shift; + + return if ($module =~ /^\./); + return unless (-f "$subdir/$module/Makefile"); + return + if ($insttype eq "client" && !grep { $_ eq $module } @client_contribs); + + my $mf = read_file("$subdir/$module/Makefile"); + $mf =~ s{\\\r?\n}{}g; + + # Note: we currently don't support setting MODULEDIR in the makefile + my $moduledir = 'contrib'; + + my $flist = ''; + if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 } + if ($flist ne '') + { + $moduledir = 'extension'; + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f.control", + "$target/share/extension/$f.control") + || croak("Could not copy file $f.control in contrib $module"); + print '.'; + } + } + + $flist = ''; + if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) { $flist .= $1 } + if ($mf =~ /^DATA\s*=\s*(.*)$/m) { $flist .= " $1" } + $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built + + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", + "$target/share/$moduledir/" . basename($f)) + || croak("Could not copy file $f in contrib $module"); + print '.'; + } + } + + $flist = ''; + if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 } + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", + "$target/share/tsearch_data/" . basename($f)) + || croak("Could not copy file $f in $subdir $module"); + print '.'; + } + } + + { + $flist = ''; + if ($mf =~ /^HEADERS\s*=\s*(.*)$/m) { $flist .= $1 } + my @modlist = (); + my %fmodlist = (); + while ($mf =~ /^HEADERS_([^\s=]+)\s*=\s*(.*)$/mg) + { + $fmodlist{$1} .= $2; + } + + if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m) + { + push @modlist, $1; + if ($flist ne '') + { + $fmodlist{$1} = $flist; + $flist = ''; + } + } + elsif ($mf =~ /^MODULES\s*=\s*(.*)$/m) + { + push @modlist, split /\s+/, $1; + } + + croak "HEADERS requires MODULE_big in $subdir $module" + if $flist ne ''; + + foreach my $mod (keys %fmodlist) + { + croak "HEADERS_$mod for unknown module in $subdir $module" + unless grep { $_ eq $mod } @modlist; + $flist = ParseAndCleanRule($fmodlist{$mod}, $mf); + EnsureDirectories($target, "include", "include/server", + "include/server/$moduledir", + "include/server/$moduledir/$mod"); + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", + "$target/include/server/$moduledir/$mod/" . basename($f)) + || croak("Could not copy file $f in $subdir $module"); + print '.'; + } + } + } + + $flist = ''; + if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 } + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + # Special case for contrib/spi + $flist = + "autoinc.example insert_username.example moddatetime.example refint.example" + if ($module eq 'spi'); + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", "$target/doc/$moduledir/$f") + || croak("Could not copy file $f in contrib $module"); + print '.'; + } + } + return; +} + +sub ParseAndCleanRule +{ + my $flist = shift; + my $mf = shift; + + # Strip out $(addsuffix) rules + if (index($flist, '$(addsuffix ') >= 0) + { + my $pcount = 0; + my $i; + for ( + $i = index($flist, '$(addsuffix ') + 12; + $i < length($flist); + $i++) + { + $pcount++ if (substr($flist, $i, 1) eq '('); + $pcount-- if (substr($flist, $i, 1) eq ')'); + last if ($pcount < 0); + } + $flist = + substr($flist, 0, index($flist, '$(addsuffix ')) + . substr($flist, $i + 1); + } + return $flist; +} + +sub CopyIncludeFiles +{ + my $target = shift; + + EnsureDirectories($target, 'include', 'include/libpq', 'include/internal', + 'include/internal/libpq', 'include/server', 'include/server/parser'); + + CopyFiles( + 'Public headers', $target . '/include/', + 'src/include/', 'postgres_ext.h', + 'pg_config.h', 'pg_config_ext.h', + 'pg_config_os.h', 'pg_config_manual.h'); + lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/') + || croak 'Could not copy libpq-fs.h'; + + CopyFiles( + 'Libpq headers', + $target . '/include/', + 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h'); + CopyFiles( + 'Libpq internal headers', + $target . '/include/internal/', + 'src/interfaces/libpq/', 'libpq-int.h', 'fe-auth-sasl.h', + 'pqexpbuffer.h'); + + CopyFiles( + 'Internal headers', + $target . '/include/internal/', + 'src/include/', 'c.h', 'port.h', 'postgres_fe.h'); + lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/') + || croak 'Could not copy pqcomm.h'; + + CopyFiles( + 'Server headers', + $target . '/include/server/', + 'src/include/', 'pg_config.h', 'pg_config_ext.h', 'pg_config_os.h'); + CopyFiles( + 'Grammar header', + $target . '/include/server/parser/', + 'src/backend/parser/', 'gram.h'); + CopySetOfFiles( + '', + [ glob("src\\include\\*.h") ], + $target . '/include/server/'); + my $D; + opendir($D, 'src/include') || croak "Could not opendir on src/include!\n"; + + CopyFiles( + 'PL/pgSQL header', + $target . '/include/server/', + 'src/pl/plpgsql/src/', 'plpgsql.h'); + + # some xcopy progs don't like mixed slash style paths + (my $ctarget = $target) =~ s!/!\\!g; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next if ($d eq '.git'); + next if ($d eq 'CVS'); + next unless (-d "src/include/$d"); + + EnsureDirectories("$target/include/server/$d"); + my @args = ( + 'xcopy', '/s', '/i', '/q', '/r', '/y', "src\\include\\$d\\*.h", + "$ctarget\\include\\server\\$d\\"); + system(@args) && croak("Failed to copy include directory $d\n"); + } + closedir($D); + + my $mf = read_file('src/interfaces/ecpg/include/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ /^ecpg_headers\s*=\s*(.*)$/m + || croak "Could not find ecpg_headers line\n"; + CopyFiles( + 'ECPG headers', + $target . '/include/', + 'src/interfaces/ecpg/include/', + 'ecpg_config.h', split /\s+/, $1); + $mf =~ /^informix_headers\s*=\s*(.*)$/m + || croak "Could not find informix_headers line\n"; + EnsureDirectories($target . '/include', 'informix', 'informix/esql'); + CopyFiles( + 'ECPG informix headers', + $target . '/include/informix/esql/', + 'src/interfaces/ecpg/include/', + split /\s+/, $1); + return; +} + +sub GenerateNLSFiles +{ + my $target = shift; + my $nlspath = shift; + my $majorver = shift; + + print "Installing NLS files..."; + EnsureDirectories($target, "share/locale"); + my @flist; + File::Find::find( + { + wanted => sub { + /^nls\.mk\z/s + && !push(@flist, $File::Find::name); + } + }, + "src"); + foreach (@flist) + { + my $prgm = DetermineCatalogName($_); + s/nls.mk/po/; + my $dir = $_; + next unless ($dir =~ /([^\/]+)\/po$/); + foreach (glob("$dir/*.po")) + { + my $lang; + next unless /([^\/]+)\.po/; + $lang = $1; + + EnsureDirectories($target, "share/locale/$lang", + "share/locale/$lang/LC_MESSAGES"); + my @args = ( + "$nlspath\\bin\\msgfmt", + '-o', + "$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo", + $_); + system(@args) && croak("Could not run msgfmt on $dir\\$_"); + print "."; + } + } + print "\n"; + return; +} + +sub DetermineMajorVersion +{ + my $f = read_file('src/include/pg_config.h') + || croak 'Could not open pg_config.h'; + $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m + || croak 'Could not determine major version'; + return $1; +} + +sub DetermineCatalogName +{ + my $filename = shift; + + my $f = read_file($filename) || croak "Could not open $filename"; + $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m + || croak "Could not determine catalog name in $filename"; + return $1; +} + +sub read_file +{ + my $filename = shift; + my $F; + local $/ = undef; + open($F, '<', $filename) || die "Could not open file $filename\n"; + my $txt = <$F>; + close($F); + + return $txt; +} + +1; diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm new file mode 100644 index 0000000..f24d9e5 --- /dev/null +++ b/src/tools/msvc/MSBuildProject.pm @@ -0,0 +1,533 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package MSBuildProject; + +# +# Package that encapsulates a MSBuild project file (Visual C++ 2013 or greater) +# +# src/tools/msvc/MSBuildProject.pm +# + +use Carp; +use strict; +use warnings; +use base qw(Project); + +no warnings qw(redefine); ## no critic + +sub _new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{filenameExtension} = '.vcxproj'; + $self->{ToolsVersion} = '4.0'; + + return $self; +} + +sub WriteHeader +{ + my ($self, $f) = @_; + + print $f <<EOF; +<?xml version="1.0" encoding="Windows-1252"?> +<Project DefaultTargets="Build" ToolsVersion="$self->{ToolsVersion}" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +EOF + $self->WriteConfigurationHeader($f, 'Debug'); + $self->WriteConfigurationHeader($f, 'Release'); + print $f <<EOF; + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$self->{guid}</ProjectGuid> +EOF + # Check whether WindowsSDKVersion env variable is present. + # Add WindowsTargetPlatformVersion node if so. + my $sdkVersion = $ENV{'WindowsSDKVersion'}; + if (defined($sdkVersion)) + { + # remove trailing backslash if necessary. + $sdkVersion =~ s/\\$//; + print $f <<EOF + <WindowsTargetPlatformVersion>$sdkVersion</WindowsTargetPlatformVersion> +EOF + } + print $f <<EOF; + </PropertyGroup> + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" /> +EOF + $self->WriteConfigurationPropertyGroup($f, 'Release', + { wholeopt => 'false' }); + $self->WriteConfigurationPropertyGroup($f, 'Debug', + { wholeopt => 'false' }); + print $f <<EOF; + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> +EOF + $self->WritePropertySheetsPropertyGroup($f, 'Release'); + $self->WritePropertySheetsPropertyGroup($f, 'Debug'); + print $f <<EOF; + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> +EOF + $self->WriteAdditionalProperties($f, 'Debug'); + $self->WriteAdditionalProperties($f, 'Release'); + print $f <<EOF; + </PropertyGroup> +EOF + + $self->WriteItemDefinitionGroup( + $f, 'Debug', + { + defs => "_DEBUG;DEBUG=1", + opt => 'Disabled', + strpool => 'false', + runtime => 'MultiThreadedDebugDLL' + }); + $self->WriteItemDefinitionGroup( + $f, + 'Release', + { + defs => "", + opt => 'Full', + strpool => 'true', + runtime => 'MultiThreadedDLL' + }); + return; +} + +sub AddDefine +{ + my ($self, $def) = @_; + + $self->{defines} .= $def . ';'; + return; +} + +sub WriteReferences +{ + my ($self, $f) = @_; + + my @references = @{ $self->{references} }; + + if (scalar(@references)) + { + print $f <<EOF; + <ItemGroup> +EOF + foreach my $ref (@references) + { + print $f <<EOF; + <ProjectReference Include="$ref->{name}$ref->{filenameExtension}"> + <Project>$ref->{guid}</Project> + </ProjectReference> +EOF + } + print $f <<EOF; + </ItemGroup> +EOF + } + return; +} + +sub WriteFiles +{ + my ($self, $f) = @_; + print $f <<EOF; + <ItemGroup> +EOF + my @grammarFiles = (); + my @resourceFiles = (); + my %uniquefiles; + foreach my $fileNameWithPath (sort keys %{ $self->{files} }) + { + confess "Bad format filename '$fileNameWithPath'\n" + unless ($fileNameWithPath =~ m!^(.*)/([^/]+)\.(c|cpp|y|l|rc)$!); + my $dir = $1; + my $fileName = $2; + if ($fileNameWithPath =~ /\.y$/ or $fileNameWithPath =~ /\.l$/) + { + push @grammarFiles, $fileNameWithPath; + } + elsif ($fileNameWithPath =~ /\.rc$/) + { + push @resourceFiles, $fileNameWithPath; + } + elsif (defined($uniquefiles{$fileName})) + { + + # File already exists, so fake a new name + my $obj = $dir; + $obj =~ s!/!_!g; + + print $f <<EOF; + <ClCompile Include="$fileNameWithPath"> + <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">.\\debug\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName> + <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">.\\release\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName> + </ClCompile> +EOF + } + else + { + $uniquefiles{$fileName} = 1; + print $f <<EOF; + <ClCompile Include="$fileNameWithPath" /> +EOF + } + + } + print $f <<EOF; + </ItemGroup> +EOF + if (scalar(@grammarFiles)) + { + print $f <<EOF; + <ItemGroup> +EOF + foreach my $grammarFile (@grammarFiles) + { + (my $outputFile = $grammarFile) =~ s/\.(y|l)$/.c/; + if ($grammarFile =~ /\.y$/) + { + $outputFile =~ + s{^src\\pl\\plpgsql\\src\\gram.c$}{src\\pl\\plpgsql\\src\\pl_gram.c}; + print $f <<EOF; + <CustomBuild Include="$grammarFile"> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running bison on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running bison on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + </CustomBuild> +EOF + } + else #if ($grammarFile =~ /\.l$/) + { + print $f <<EOF; + <CustomBuild Include="$grammarFile"> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running flex on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running flex on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + </CustomBuild> +EOF + } + } + print $f <<EOF; + </ItemGroup> +EOF + } + if (scalar(@resourceFiles)) + { + print $f <<EOF; + <ItemGroup> +EOF + foreach my $rcFile (@resourceFiles) + { + print $f <<EOF; + <ResourceCompile Include="$rcFile" /> +EOF + } + print $f <<EOF; + </ItemGroup> +EOF + } + return; +} + +sub WriteConfigurationHeader +{ + my ($self, $f, $cfgname) = @_; + print $f <<EOF; + <ProjectConfiguration Include="$cfgname|$self->{platform}"> + <Configuration>$cfgname</Configuration> + <Platform>$self->{platform}</Platform> + </ProjectConfiguration> +EOF + return; +} + +sub WriteConfigurationPropertyGroup +{ + my ($self, $f, $cfgname, $p) = @_; + my $cfgtype = + ($self->{type} eq "exe") + ? 'Application' + : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary'); + + print $f <<EOF; + <PropertyGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="Configuration"> + <ConfigurationType>$cfgtype</ConfigurationType> + <UseOfMfc>false</UseOfMfc> + <CharacterSet>MultiByte</CharacterSet> + <WholeProgramOptimization>$p->{wholeopt}</WholeProgramOptimization> + <PlatformToolset>$self->{PlatformToolset}</PlatformToolset> + </PropertyGroup> +EOF + return; +} + +sub WritePropertySheetsPropertyGroup +{ + my ($self, $f, $cfgname) = @_; + print $f <<EOF; + <ImportGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="PropertySheets"> + <Import Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> +EOF + return; +} + +sub WriteAdditionalProperties +{ + my ($self, $f, $cfgname) = @_; + print $f <<EOF; + <OutDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</OutDir> + <IntDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</IntDir> + <LinkIncremental Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">false</LinkIncremental> +EOF + return; +} + +sub WriteItemDefinitionGroup +{ + my ($self, $f, $cfgname, $p) = @_; + my $cfgtype = + ($self->{type} eq "exe") + ? 'Application' + : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary'); + my $libs = $self->GetAdditionalLinkerDependencies($cfgname, ';'); + + my $targetmachine = + $self->{platform} eq 'Win32' ? 'MachineX86' : 'MachineX64'; + + my $includes = join ';', @{ $self->{includes} }, ""; + + print $f <<EOF; + <ItemDefinitionGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'"> + <ClCompile> + <Optimization>$p->{opt}</Optimization> + <AdditionalIncludeDirectories>$self->{prefixincludes}src/include;src/include/port/win32;src/include/port/win32_msvc;$includes\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_WINDOWS;__WINDOWS__;__WIN32__;WIN32_STACK_RLIMIT=4194304;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE$self->{defines}$p->{defs}\%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>$p->{strpool}</StringPooling> + <RuntimeLibrary>$p->{runtime}</RuntimeLibrary> + <DisableSpecificWarnings>$self->{disablewarnings};\%(DisableSpecificWarnings)</DisableSpecificWarnings> + <AdditionalOptions>/MP \%(AdditionalOptions)</AdditionalOptions> + <AssemblerOutput> + </AssemblerOutput> + <AssemblerListingLocation>.\\$cfgname\\$self->{name}\\</AssemblerListingLocation> + <ObjectFileName>.\\$cfgname\\$self->{name}\\</ObjectFileName> + <ProgramDataBaseFileName>.\\$cfgname\\$self->{name}\\</ProgramDataBaseFileName> + <BrowseInformation>false</BrowseInformation> + <WarningLevel>Level3</WarningLevel> + <SuppressStartupBanner>true</SuppressStartupBanner> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <CompileAs>Default</CompileAs> + </ClCompile> + <Link> + <OutputFile>.\\$cfgname\\$self->{name}\\$self->{name}.$self->{type}</OutputFile> + <AdditionalDependencies>$libs;\%(AdditionalDependencies)</AdditionalDependencies> + <SuppressStartupBanner>true</SuppressStartupBanner> + <AdditionalLibraryDirectories>\%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <IgnoreSpecificDefaultLibraries>libc;\%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries> + <StackReserveSize>4194304</StackReserveSize> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>.\\$cfgname\\$self->{name}\\$self->{name}.pdb</ProgramDatabaseFile> + <GenerateMapFile>false</GenerateMapFile> + <MapFileName>.\\$cfgname\\$self->{name}\\$self->{name}.map</MapFileName> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <!-- Permit links to MinGW-built, 32-bit DLLs (default before VS2012). --> + <ImageHasSafeExceptionHandlers/> + <SubSystem>Console</SubSystem> + <TargetMachine>$targetmachine</TargetMachine> +EOF + if ($self->{disablelinkerwarnings}) + { + print $f + " <AdditionalOptions>/ignore:$self->{disablelinkerwarnings} \%(AdditionalOptions)</AdditionalOptions>\n"; + } + if ($self->{implib}) + { + my $l = $self->{implib}; + $l =~ s/__CFGNAME__/$cfgname/g; + print $f " <ImportLibrary>$l</ImportLibrary>\n"; + } + if ($self->{def}) + { + my $d = $self->{def}; + $d =~ s/__CFGNAME__/$cfgname/g; + print $f " <ModuleDefinitionFile>$d</ModuleDefinitionFile>\n"; + } + print $f <<EOF; + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>src\\include;\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ResourceCompile> +EOF + if ($self->{builddef}) + { + print $f <<EOF; + <PreLinkEvent> + <Message>Generate DEF file</Message> + <Command>perl src\\tools\\msvc\\gendef.pl $cfgname\\$self->{name} $self->{platform}</Command> + </PreLinkEvent> +EOF + } + print $f <<EOF; + </ItemDefinitionGroup> +EOF + return; +} + +sub Footer +{ + my ($self, $f) = @_; + $self->WriteReferences($f); + + print $f <<EOF; + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> +EOF + return; +} + +package VC2013Project; + +# +# Package that encapsulates a Visual C++ 2013 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '12.00'; + $self->{PlatformToolset} = 'v120'; + $self->{ToolsVersion} = '12.0'; + + return $self; +} + +package VC2015Project; + +# +# Package that encapsulates a Visual C++ 2015 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '14.00'; + $self->{PlatformToolset} = 'v140'; + $self->{ToolsVersion} = '14.0'; + + return $self; +} + +package VC2017Project; + +# +# Package that encapsulates a Visual C++ 2017 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '15.00'; + $self->{PlatformToolset} = 'v141'; + $self->{ToolsVersion} = '15.0'; + + return $self; +} + +package VC2019Project; + +# +# Package that encapsulates a Visual C++ 2019 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '16.00'; + $self->{PlatformToolset} = 'v142'; + $self->{ToolsVersion} = '16.0'; + + return $self; +} + +package VC2022Project; + +# +# Package that encapsulates a Visual C++ 2022 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '17.00'; + $self->{PlatformToolset} = 'v143'; + $self->{ToolsVersion} = '17.0'; + + return $self; +} + +1; diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm new file mode 100644 index 0000000..990c223 --- /dev/null +++ b/src/tools/msvc/Mkvcbuild.pm @@ -0,0 +1,1204 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Mkvcbuild; + +# +# Package that generates build files for msvc build +# +# src/tools/msvc/Mkvcbuild.pm +# +use strict; +use warnings; + +use Carp; +use if ($^O eq "MSWin32"), 'Win32'; +use Project; +use Solution; +use Cwd; +use File::Copy; +use Config; +use VSObjectFactory; +use List::Util qw(first); + +use Exporter; +our (@ISA, @EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT_OK = qw(Mkvcbuild); + +my $solution; +my $libpgport; +my $libpgcommon; +my $libpgfeutils; +my $postgres; +my $libpq; +my @unlink_on_exit; + +# Set of variables for modules in contrib/ and src/test/modules/ +my $contrib_defines = {}; +my @contrib_uselibpq = (); +my @contrib_uselibpgport = (); +my @contrib_uselibpgcommon = (); +my $contrib_extralibs = { 'libpq_pipeline' => ['ws2_32.lib'] }; +my $contrib_extraincludes = {}; +my $contrib_extrasource = {}; +my @contrib_excludes = ( + 'bool_plperl', 'commit_ts', + 'hstore_plperl', 'hstore_plpython', + 'intagg', 'jsonb_plperl', + 'jsonb_plpython', 'ltree_plpython', + 'sepgsql', 'brin', + 'test_extensions', 'test_misc', + 'test_pg_dump', 'snapshot_too_old', + 'unsafe_tests'); + +# Set of variables for frontend modules +my $frontend_defines = { 'initdb' => 'FRONTEND' }; +my @frontend_uselibpq = + ('pg_amcheck', 'pg_ctl', 'pg_upgrade', 'pgbench', 'psql', 'initdb'); +my @frontend_uselibpgport = ( + 'pg_amcheck', 'pg_archivecleanup', + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_waldump', + 'pgbench'); +my @frontend_uselibpgcommon = ( + 'pg_amcheck', 'pg_archivecleanup', + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_waldump', + 'pgbench'); +my $frontend_extralibs = { + 'initdb' => ['ws2_32.lib'], + 'pg_amcheck' => ['ws2_32.lib'], + 'pg_restore' => ['ws2_32.lib'], + 'pgbench' => ['ws2_32.lib'], + 'psql' => ['ws2_32.lib'] +}; +my $frontend_extraincludes = { + 'initdb' => ['src/timezone'], + 'psql' => ['src/backend'] +}; +my $frontend_extrasource = { + 'psql' => ['src/bin/psql/psqlscanslash.l'], + 'pgbench' => + [ 'src/bin/pgbench/exprscan.l', 'src/bin/pgbench/exprparse.y' ] +}; +my @frontend_excludes = ( + 'pgevent', 'pg_basebackup', 'pg_rewind', 'pg_dump', + 'pg_waldump', 'scripts'); + +sub mkvcbuild +{ + our $config = shift; + + chdir('../../..') if (-d '../msvc' && -d '../../../src'); + die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + + my $vsVersion = DetermineVisualStudioVersion(); + + $solution = CreateSolution($vsVersion, $config); + + our @pgportfiles = qw( + chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c + getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c + snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c + dirent.c dlopen.c getopt.c getopt_long.c link.c + pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c + pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c + pqsignal.c mkdtemp.c qsort.c qsort_arg.c bsearch_arg.c quotes.c system.c + strerror.c tar.c + win32common.c win32env.c win32error.c win32fseek.c win32ntdll.c + win32security.c win32setlocale.c win32stat.c); + + push(@pgportfiles, 'strtof.c') if ($vsVersion < '14.00'); + + if ($vsVersion >= '9.00') + { + push(@pgportfiles, 'pg_crc32c_sse42_choose.c'); + push(@pgportfiles, 'pg_crc32c_sse42.c'); + push(@pgportfiles, 'pg_crc32c_sb8.c'); + } + else + { + push(@pgportfiles, 'pg_crc32c_sb8.c'); + } + + our @pgcommonallfiles = qw( + archive.c base64.c checksum_helper.c compression.c + config_info.c controldata_utils.c d2s.c encnames.c exec.c + f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c + keywords.c kwlookup.c link-canary.c md5_common.c + pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c + rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c + username.c wait_error.c wchar.c); + + if ($solution->{options}->{openssl}) + { + push(@pgcommonallfiles, 'cryptohash_openssl.c'); + push(@pgcommonallfiles, 'hmac_openssl.c'); + push(@pgcommonallfiles, 'protocol_openssl.c'); + } + else + { + push(@pgcommonallfiles, 'cryptohash.c'); + push(@pgcommonallfiles, 'hmac.c'); + push(@pgcommonallfiles, 'md5.c'); + push(@pgcommonallfiles, 'sha1.c'); + push(@pgcommonallfiles, 'sha2.c'); + } + + our @pgcommonfrontendfiles = ( + @pgcommonallfiles, qw(fe_memutils.c + logging.c restricted_token.c sprompt.c)); + + our @pgcommonbkndfiles = @pgcommonallfiles; + + our @pgfeutilsfiles = qw( + archive.c cancel.c conditional.c connect_utils.c mbprint.c option_utils.c + parallel_slot.c print.c psqlscan.l psqlscan.c query_utils.c simple_list.c + string_utils.c recovery_gen.c); + + $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); + $libpgport->AddDefine('FRONTEND'); + $libpgport->AddFiles('src/port', @pgportfiles); + + $libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc'); + $libpgcommon->AddDefine('FRONTEND'); + $libpgcommon->AddFiles('src/common', @pgcommonfrontendfiles); + + $libpgfeutils = $solution->AddProject('libpgfeutils', 'lib', 'misc'); + $libpgfeutils->AddDefine('FRONTEND'); + $libpgfeutils->AddIncludeDir('src/interfaces/libpq'); + $libpgfeutils->AddFiles('src/fe_utils', @pgfeutilsfiles); + + $postgres = $solution->AddProject('postgres', 'exe', '', 'src/backend'); + $postgres->AddIncludeDir('src/backend'); + $postgres->AddDir('src/backend/port/win32'); + $postgres->AddFile('src/backend/utils/fmgrtab.c'); + $postgres->ReplaceFile('src/backend/port/pg_sema.c', + 'src/backend/port/win32_sema.c'); + $postgres->ReplaceFile('src/backend/port/pg_shmem.c', + 'src/backend/port/win32_shmem.c'); + $postgres->AddFiles('src/port', @pgportfiles); + $postgres->AddFiles('src/common', @pgcommonbkndfiles); + $postgres->AddDir('src/timezone'); + + # We need source files from src/timezone, but that directory's resource + # file pertains to "zic", not to the backend. + $postgres->RemoveFile('src/timezone/win32ver.rc'); + $postgres->AddFiles('src/backend/parser', 'scan.l', 'gram.y'); + $postgres->AddFiles('src/backend/bootstrap', 'bootscanner.l', + 'bootparse.y'); + $postgres->AddFiles('src/backend/utils/misc', 'guc-file.l'); + $postgres->AddFiles( + 'src/backend/replication', 'repl_scanner.l', + 'repl_gram.y', 'syncrep_scanner.l', + 'syncrep_gram.y'); + $postgres->AddFiles('src/backend/utils/adt', 'jsonpath_scan.l', + 'jsonpath_gram.y'); + $postgres->AddDefine('BUILDING_DLL'); + $postgres->AddLibrary('secur32.lib'); + $postgres->AddLibrary('ws2_32.lib'); + $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); + $postgres->FullExportDLL('postgres.lib'); + + # The OBJS scraper doesn't know about ifdefs, so remove appropriate files + # if building without OpenSSL. + if (!$solution->{options}->{openssl}) + { + $postgres->RemoveFile('src/backend/libpq/be-secure-common.c'); + $postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c'); + } + if (!$solution->{options}->{gss}) + { + $postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c'); + $postgres->RemoveFile('src/backend/libpq/be-secure-gssapi.c'); + } + + my $snowball = $solution->AddProject('dict_snowball', 'dll', '', + 'src/backend/snowball'); + + # This Makefile uses VPATH to find most source files in a subdirectory. + $snowball->RelocateFiles( + 'src/backend/snowball/libstemmer', + sub { + return shift !~ /(dict_snowball.c|win32ver.rc)$/; + }); + $snowball->AddIncludeDir('src/include/snowball'); + $snowball->AddReference($postgres); + + my $plpgsql = + $solution->AddProject('plpgsql', 'dll', 'PLs', 'src/pl/plpgsql/src'); + $plpgsql->AddFiles('src/pl/plpgsql/src', 'pl_gram.y'); + $plpgsql->AddReference($postgres); + + if ($solution->{options}->{tcl}) + { + my $found = 0; + my $pltcl = + $solution->AddProject('pltcl', 'dll', 'PLs', 'src/pl/tcl'); + $pltcl->AddIncludeDir($solution->{options}->{tcl} . '/include'); + $pltcl->AddReference($postgres); + + for my $tclver (qw(86t 86 85 84)) + { + my $tcllib = $solution->{options}->{tcl} . "/lib/tcl$tclver.lib"; + if (-e $tcllib) + { + $pltcl->AddLibrary($tcllib); + $found = 1; + last; + } + } + die "Unable to find $solution->{options}->{tcl}/lib/tcl<version>.lib" + unless $found; + } + + $libpq = $solution->AddProject('libpq', 'dll', 'interfaces', + 'src/interfaces/libpq'); + $libpq->AddDefine('FRONTEND'); + $libpq->AddDefine('UNSAFE_STAT_OK'); + $libpq->AddIncludeDir('src/port'); + $libpq->AddLibrary('secur32.lib'); + $libpq->AddLibrary('ws2_32.lib'); + $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); + $libpq->UseDef('src/interfaces/libpq/libpqdll.def'); + $libpq->AddReference($libpgcommon, $libpgport); + + # The OBJS scraper doesn't know about ifdefs, so remove appropriate files + # if building without OpenSSL. + if (!$solution->{options}->{openssl}) + { + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c'); + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c'); + } + if (!$solution->{options}->{gss}) + { + $libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c'); + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c'); + } + + my $libpqwalreceiver = + $solution->AddProject('libpqwalreceiver', 'dll', '', + 'src/backend/replication/libpqwalreceiver'); + $libpqwalreceiver->AddIncludeDir('src/interfaces/libpq'); + $libpqwalreceiver->AddReference($postgres, $libpq); + + my $libpq_testclient = + $solution->AddProject('libpq_testclient', 'exe', 'misc', + 'src/interfaces/libpq/test'); + $libpq_testclient->AddFile( + 'src/interfaces/libpq/test/libpq_testclient.c'); + $libpq_testclient->AddIncludeDir('src/interfaces/libpq'); + $libpq_testclient->AddReference($libpgport, $libpq); + $libpq_testclient->AddLibrary('ws2_32.lib'); + + my $libpq_uri_regress = + $solution->AddProject('libpq_uri_regress', 'exe', 'misc', + 'src/interfaces/libpq/test'); + $libpq_uri_regress->AddFile( + 'src/interfaces/libpq/test/libpq_uri_regress.c'); + $libpq_uri_regress->AddIncludeDir('src/interfaces/libpq'); + $libpq_uri_regress->AddReference($libpgport, $libpq); + $libpq_uri_regress->AddLibrary('ws2_32.lib'); + + my $pgoutput = $solution->AddProject('pgoutput', 'dll', '', + 'src/backend/replication/pgoutput'); + $pgoutput->AddReference($postgres); + + my $pgtypes = $solution->AddProject( + 'libpgtypes', 'dll', + 'interfaces', 'src/interfaces/ecpg/pgtypeslib'); + $pgtypes->AddDefine('FRONTEND'); + $pgtypes->AddReference($libpgcommon, $libpgport); + $pgtypes->UseDef('src/interfaces/ecpg/pgtypeslib/pgtypeslib.def'); + $pgtypes->AddIncludeDir('src/interfaces/ecpg/include'); + + my $libecpg = $solution->AddProject('libecpg', 'dll', 'interfaces', + 'src/interfaces/ecpg/ecpglib'); + $libecpg->AddDefine('FRONTEND'); + $libecpg->AddIncludeDir('src/interfaces/ecpg/include'); + $libecpg->AddIncludeDir('src/interfaces/libpq'); + $libecpg->AddIncludeDir('src/port'); + $libecpg->UseDef('src/interfaces/ecpg/ecpglib/ecpglib.def'); + $libecpg->AddLibrary('ws2_32.lib'); + $libecpg->AddReference($libpq, $pgtypes, $libpgport); + + my $libecpgcompat = $solution->AddProject( + 'libecpg_compat', 'dll', + 'interfaces', 'src/interfaces/ecpg/compatlib'); + $libecpgcompat->AddDefine('FRONTEND'); + $libecpgcompat->AddIncludeDir('src/interfaces/ecpg/include'); + $libecpgcompat->AddIncludeDir('src/interfaces/libpq'); + $libecpgcompat->UseDef('src/interfaces/ecpg/compatlib/compatlib.def'); + $libecpgcompat->AddReference($pgtypes, $libecpg, $libpgport, + $libpgcommon); + + my $ecpg = $solution->AddProject('ecpg', 'exe', 'interfaces', + 'src/interfaces/ecpg/preproc'); + $ecpg->AddIncludeDir('src/interfaces/ecpg/include'); + $ecpg->AddIncludeDir('src/interfaces/ecpg/ecpglib'); + $ecpg->AddIncludeDir('src/interfaces/libpq'); + $ecpg->AddPrefixInclude('src/interfaces/ecpg/preproc'); + $ecpg->AddFiles('src/interfaces/ecpg/preproc', 'pgc.l', 'preproc.y'); + $ecpg->AddReference($libpgcommon, $libpgport); + + my $pgregress_ecpg = + $solution->AddProject('pg_regress_ecpg', 'exe', 'misc'); + $pgregress_ecpg->AddFile('src/interfaces/ecpg/test/pg_regress_ecpg.c'); + $pgregress_ecpg->AddFile('src/test/regress/pg_regress.c'); + $pgregress_ecpg->AddIncludeDir('src/port'); + $pgregress_ecpg->AddIncludeDir('src/test/regress'); + $pgregress_ecpg->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress_ecpg->AddLibrary('ws2_32.lib'); + $pgregress_ecpg->AddDirResourceFile('src/interfaces/ecpg/test'); + $pgregress_ecpg->AddReference($libpgcommon, $libpgport); + + my $isolation_tester = + $solution->AddProject('isolationtester', 'exe', 'misc'); + $isolation_tester->AddFile('src/test/isolation/isolationtester.c'); + $isolation_tester->AddFile('src/test/isolation/specparse.y'); + $isolation_tester->AddFile('src/test/isolation/specscanner.l'); + $isolation_tester->AddFile('src/test/isolation/specparse.c'); + $isolation_tester->AddIncludeDir('src/test/isolation'); + $isolation_tester->AddIncludeDir('src/port'); + $isolation_tester->AddIncludeDir('src/test/regress'); + $isolation_tester->AddIncludeDir('src/interfaces/libpq'); + $isolation_tester->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $isolation_tester->AddLibrary('ws2_32.lib'); + $isolation_tester->AddDirResourceFile('src/test/isolation'); + $isolation_tester->AddReference($libpq, $libpgcommon, $libpgport); + + my $pgregress_isolation = + $solution->AddProject('pg_isolation_regress', 'exe', 'misc'); + $pgregress_isolation->AddFile('src/test/isolation/isolation_main.c'); + $pgregress_isolation->AddFile('src/test/regress/pg_regress.c'); + $pgregress_isolation->AddIncludeDir('src/port'); + $pgregress_isolation->AddIncludeDir('src/test/regress'); + $pgregress_isolation->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress_isolation->AddLibrary('ws2_32.lib'); + $pgregress_isolation->AddDirResourceFile('src/test/isolation'); + $pgregress_isolation->AddReference($libpgcommon, $libpgport); + + # src/bin + my $D; + opendir($D, 'src/bin') || croak "Could not opendir on src/bin!\n"; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next unless (-f "src/bin/$d/Makefile"); + next if (grep { /^$d$/ } @frontend_excludes); + AddSimpleFrontend($d); + } + + my $pgbasebackup = AddSimpleFrontend('pg_basebackup', 1); + # This list of files has to match BBOBJS in pg_basebackup's Makefile. + $pgbasebackup->AddFile('src/bin/pg_basebackup/pg_basebackup.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_file.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_gzip.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_inject.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_lz4.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_zstd.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_tar.c'); + $pgbasebackup->AddLibrary('ws2_32.lib'); + + my $pgreceivewal = AddSimpleFrontend('pg_basebackup', 1); + $pgreceivewal->{name} = 'pg_receivewal'; + $pgreceivewal->AddFile('src/bin/pg_basebackup/pg_receivewal.c'); + $pgreceivewal->AddLibrary('ws2_32.lib'); + + my $pgrecvlogical = AddSimpleFrontend('pg_basebackup', 1); + $pgrecvlogical->{name} = 'pg_recvlogical'; + $pgrecvlogical->AddFile('src/bin/pg_basebackup/pg_recvlogical.c'); + $pgrecvlogical->AddLibrary('ws2_32.lib'); + + my $pgrewind = AddSimpleFrontend('pg_rewind', 1); + $pgrewind->{name} = 'pg_rewind'; + $pgrewind->AddFile('src/backend/access/transam/xlogreader.c'); + $pgrewind->AddLibrary('ws2_32.lib'); + $pgrewind->AddDefine('FRONTEND'); + + my $pgevent = $solution->AddProject('pgevent', 'dll', 'bin'); + $pgevent->AddFiles('src/bin/pgevent', 'pgevent.c', 'pgmsgevent.rc'); + $pgevent->AddResourceFile('src/bin/pgevent', 'Eventlog message formatter', + 'win32'); + $pgevent->RemoveFile('src/bin/pgevent/win32ver.rc'); + $pgevent->UseDef('src/bin/pgevent/pgevent.def'); + $pgevent->DisableLinkerWarnings('4104'); + + my $pgdump = AddSimpleFrontend('pg_dump', 1); + $pgdump->AddIncludeDir('src/backend'); + $pgdump->AddFile('src/bin/pg_dump/pg_dump.c'); + $pgdump->AddFile('src/bin/pg_dump/common.c'); + $pgdump->AddFile('src/bin/pg_dump/pg_dump_sort.c'); + $pgdump->AddLibrary('ws2_32.lib'); + + my $pgdumpall = AddSimpleFrontend('pg_dump', 1); + + # pg_dumpall doesn't use the files in the Makefile's $(OBJS), unlike + # pg_dump and pg_restore. + # So remove their sources from the object, keeping the other setup that + # AddSimpleFrontend() has done. + my @nodumpall = grep { m!src/bin/pg_dump/.*\.c$! } + keys %{ $pgdumpall->{files} }; + delete @{ $pgdumpall->{files} }{@nodumpall}; + $pgdumpall->{name} = 'pg_dumpall'; + $pgdumpall->AddIncludeDir('src/backend'); + $pgdumpall->AddFile('src/bin/pg_dump/pg_dumpall.c'); + $pgdumpall->AddFile('src/bin/pg_dump/dumputils.c'); + $pgdumpall->AddLibrary('ws2_32.lib'); + + my $pgrestore = AddSimpleFrontend('pg_dump', 1); + $pgrestore->{name} = 'pg_restore'; + $pgrestore->AddIncludeDir('src/backend'); + $pgrestore->AddFile('src/bin/pg_dump/pg_restore.c'); + $pgrestore->AddLibrary('ws2_32.lib'); + + my $zic = $solution->AddProject('zic', 'exe', 'utils'); + $zic->AddFiles('src/timezone', 'zic.c'); + $zic->AddDirResourceFile('src/timezone'); + $zic->AddReference($libpgcommon, $libpgport); + + if (!$solution->{options}->{xml}) + { + push @contrib_excludes, 'xml2'; + } + + if (!$solution->{options}->{openssl}) + { + push @contrib_excludes, 'sslinfo', 'ssl_passphrase_callback', + 'pgcrypto'; + } + + if (!$solution->{options}->{uuid}) + { + push @contrib_excludes, 'uuid-ossp'; + } + + foreach my $subdir ('contrib', 'src/test/modules') + { + opendir($D, $subdir) || croak "Could not opendir on $subdir!\n"; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next unless (-f "$subdir/$d/Makefile"); + next if (grep { /^$d$/ } @contrib_excludes); + AddContrib($subdir, $d); + } + closedir($D); + } + + # Build Perl and Python modules after contrib/ modules to satisfy some + # dependencies with transform contrib modules, like hstore_plpython + # ltree_plpython and hstore_plperl. + if ($solution->{options}->{python}) + { + + # Attempt to get python version and location. + # Assume python.exe in specified dir. + my $pythonprog = "import sys;print(sys.prefix);" + . "print(str(sys.version_info[0])+str(sys.version_info[1]))"; + my $prefixcmd = + qq("$solution->{options}->{python}\\python" -c "$pythonprog"); + my $pyout = `$prefixcmd`; + die "Could not query for python version!\n" if $?; + my ($pyprefix, $pyver) = split(/\r?\n/, $pyout); + + # Sometimes (always?) if python is not present, the execution + # appears to work, but gives no data... + die "Failed to query python for version information\n" + if (!(defined($pyprefix) && defined($pyver))); + + my $pymajorver = substr($pyver, 0, 1); + + die + "Python version $pyver is too old (version 3 or later is required)" + if int($pymajorver) < 3; + + my $plpython = $solution->AddProject('plpython' . $pymajorver, + 'dll', 'PLs', 'src/pl/plpython'); + $plpython->AddIncludeDir($pyprefix . '/include'); + $plpython->AddLibrary($pyprefix . "/Libs/python$pyver.lib"); + $plpython->AddReference($postgres); + + # Add transform modules dependent on plpython + my $hstore_plpython = AddTransformModule( + 'hstore_plpython' . $pymajorver, 'contrib/hstore_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython', + 'hstore', 'contrib'); + $hstore_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + my $jsonb_plpython = AddTransformModule( + 'jsonb_plpython' . $pymajorver, 'contrib/jsonb_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython'); + $jsonb_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + my $ltree_plpython = AddTransformModule( + 'ltree_plpython' . $pymajorver, 'contrib/ltree_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython', + 'ltree', 'contrib'); + $ltree_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + } + + if ($solution->{options}->{perl}) + { + my $plperlsrc = "src/pl/plperl/"; + my $plperl = + $solution->AddProject('plperl', 'dll', 'PLs', 'src/pl/plperl'); + $plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE'); + $plperl->AddReference($postgres); + + my $perl_path = $solution->{options}->{perl} . '\lib\CORE\*perl*'; + + # ActivePerl 5.16 provided perl516.lib; 5.18 provided libperl518.a + # Starting with ActivePerl 5.24, both perlnn.lib and libperlnn.a are provided. + # In this case, prefer .lib. + my @perl_libs = + reverse sort grep { /perl\d+\.lib$|libperl\d+\.a$/ } + glob($perl_path); + if (@perl_libs > 0) + { + $plperl->AddLibrary($perl_libs[0]); + } + else + { + die + "could not identify perl library version matching pattern $perl_path\n"; + } + + # Add defines from Perl's ccflags; see PGAC_CHECK_PERL_EMBED_CCFLAGS + my @perl_embed_ccflags; + foreach my $f (split(" ", $Config{ccflags})) + { + if ($f =~ /^-D[^_]/) + { + $f =~ s/\-D//; + push(@perl_embed_ccflags, $f); + } + } + + # hack to prevent duplicate definitions of uid_t/gid_t + push(@perl_embed_ccflags, 'PLPERL_HAVE_UID_GID'); + # prevent binary mismatch between MSVC built plperl and + # Strawberry or msys ucrt perl libraries + push(@perl_embed_ccflags, 'NO_THREAD_SAFE_LOCALE'); + + # Windows offers several 32-bit ABIs. Perl is sensitive to + # sizeof(time_t), one of the ABI dimensions. To get 32-bit time_t, + # use "cl -D_USE_32BIT_TIME_T" or plain "gcc". For 64-bit time_t, use + # "gcc -D__MINGW_USE_VC2005_COMPAT" or plain "cl". Before MSVC 2005, + # plain "cl" chose 32-bit time_t. PostgreSQL doesn't support building + # with pre-MSVC-2005 compilers, but it does support linking to Perl + # built with such a compiler. MSVC-built Perl 5.13.4 and later report + # -D_USE_32BIT_TIME_T in $Config{ccflags} if applicable, but + # MinGW-built Perl never reports -D_USE_32BIT_TIME_T despite typically + # needing it. Ignore the $Config{ccflags} opinion about + # -D_USE_32BIT_TIME_T, and use a runtime test to deduce the ABI Perl + # expects. Specifically, test use of PL_modglobal, which maps to a + # PerlInterpreter field whose position depends on sizeof(time_t). + if ($solution->{platform} eq 'Win32') + { + my $source_file = 'conftest.c'; + my $obj = 'conftest.obj'; + my $exe = 'conftest.exe'; + my @conftest = ($source_file, $obj, $exe); + push @unlink_on_exit, @conftest; + unlink $source_file; + open my $o, '>', $source_file + || croak "Could not write to $source_file"; + print $o ' + /* compare to plperl.h */ + #define __inline__ __inline + #define PERL_NO_GET_CONTEXT + #include <EXTERN.h> + #include <perl.h> + + int + main(int argc, char **argv) + { + int dummy_argc = 1; + char *dummy_argv[1] = {""}; + char *dummy_env[1] = {NULL}; + static PerlInterpreter *interp; + + PERL_SYS_INIT3(&dummy_argc, (char ***) &dummy_argv, + (char ***) &dummy_env); + interp = perl_alloc(); + perl_construct(interp); + { + dTHX; + const char key[] = "dummy"; + + PL_exit_flags |= PERL_EXIT_DESTRUCT_END; + hv_store(PL_modglobal, key, sizeof(key) - 1, newSViv(1), 0); + return hv_fetch(PL_modglobal, key, sizeof(key) - 1, 0) == NULL; + } + } +'; + close $o; + + # Build $source_file with a given #define, and return a true value + # if a run of the resulting binary exits successfully. + my $try_define = sub { + my $define = shift; + + unlink $obj, $exe; + my @cmd = ( + 'cl', + '-I' . $solution->{options}->{perl} . '/lib/CORE', + (map { "-D$_" } @perl_embed_ccflags, $define || ()), + $source_file, + '/link', + $perl_libs[0]); + my $compile_output = `@cmd 2>&1`; + -f $exe || die "Failed to build Perl test:\n$compile_output"; + + { + + # Some builds exhibit runtime failure through Perl warning + # 'Can't spawn "conftest.exe"'; suppress that. + no warnings; + + no strict 'subs'; ## no critic (ProhibitNoStrict) + + # Disable error dialog boxes like we do in the postmaster. + # Here, we run code that triggers relevant errors. + use + if ($^O eq "MSWin32"), 'Win32API::File', + qw(SetErrorMode :SEM_); + my $oldmode = SetErrorMode( + SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + system(".\\$exe"); + SetErrorMode($oldmode); + } + + return !($? >> 8); + }; + + my $define_32bit_time = '_USE_32BIT_TIME_T'; + my $ok_now = $try_define->(undef); + my $ok_32bit = $try_define->($define_32bit_time); + unlink @conftest; + if (!$ok_now && !$ok_32bit) + { + + # Unsupported configuration. Since we used %Config from the + # Perl running the build scripts, this is expected if + # attempting to link with some other Perl. + die "Perl test fails with or without -D$define_32bit_time"; + } + elsif ($ok_now && $ok_32bit) + { + + # Resulting build may work, but it's especially important to + # verify with "vcregress plcheck". A refined test may avoid + # this outcome. + warn "Perl test passes with or without -D$define_32bit_time"; + } + elsif ($ok_32bit) + { + push(@perl_embed_ccflags, $define_32bit_time); + } # else $ok_now, hence no flag required + } + + print "CFLAGS recommended by Perl: $Config{ccflags}\n"; + print "CFLAGS to compile embedded Perl: ", + (join ' ', map { "-D$_" } @perl_embed_ccflags), "\n"; + foreach my $f (@perl_embed_ccflags) + { + $plperl->AddDefine($f); + } + + foreach my $xs ('SPI.xs', 'Util.xs') + { + (my $xsc = $xs) =~ s/\.xs/.c/; + if (Solution::IsNewer("$plperlsrc$xsc", "$plperlsrc$xs")) + { + my $xsubppdir = first { -e "$_/ExtUtils/xsubpp" } (@INC); + print "Building $plperlsrc$xsc...\n"; + system( $solution->{options}->{perl} + . '/bin/perl ' + . "$xsubppdir/ExtUtils/xsubpp -typemap " + . $solution->{options}->{perl} + . '/lib/ExtUtils/typemap ' + . "$plperlsrc$xs " + . ">$plperlsrc$xsc"); + if ((!(-f "$plperlsrc$xsc")) || -z "$plperlsrc$xsc") + { + unlink("$plperlsrc$xsc"); # if zero size + die "Failed to create $xsc.\n"; + } + } + } + if (Solution::IsNewer( + 'src/pl/plperl/perlchunks.h', + 'src/pl/plperl/plc_perlboot.pl') + || Solution::IsNewer( + 'src/pl/plperl/perlchunks.h', + 'src/pl/plperl/plc_trusted.pl')) + { + print 'Building src/pl/plperl/perlchunks.h ...' . "\n"; + my $basedir = getcwd; + chdir 'src/pl/plperl'; + system( $solution->{options}->{perl} + . '/bin/perl ' + . 'text2macro.pl ' + . '--strip="^(\#.*|\s*)$$" ' + . 'plc_perlboot.pl plc_trusted.pl ' + . '>perlchunks.h'); + chdir $basedir; + if ((!(-f 'src/pl/plperl/perlchunks.h')) + || -z 'src/pl/plperl/perlchunks.h') + { + unlink('src/pl/plperl/perlchunks.h'); # if zero size + die 'Failed to create perlchunks.h' . "\n"; + } + } + if (Solution::IsNewer( + 'src/pl/plperl/plperl_opmask.h', + 'src/pl/plperl/plperl_opmask.pl')) + { + print 'Building src/pl/plperl/plperl_opmask.h ...' . "\n"; + my $basedir = getcwd; + chdir 'src/pl/plperl'; + system( $solution->{options}->{perl} + . '/bin/perl ' + . 'plperl_opmask.pl ' + . 'plperl_opmask.h'); + chdir $basedir; + if ((!(-f 'src/pl/plperl/plperl_opmask.h')) + || -z 'src/pl/plperl/plperl_opmask.h') + { + unlink('src/pl/plperl/plperl_opmask.h'); # if zero size + die 'Failed to create plperl_opmask.h' . "\n"; + } + } + + # Add transform modules dependent on plperl + my $bool_plperl = AddTransformModule( + 'bool_plperl', 'contrib/bool_plperl', + 'plperl', 'src/pl/plperl'); + my $hstore_plperl = AddTransformModule( + 'hstore_plperl', 'contrib/hstore_plperl', + 'plperl', 'src/pl/plperl', + 'hstore', 'contrib'); + my $jsonb_plperl = AddTransformModule( + 'jsonb_plperl', 'contrib/jsonb_plperl', + 'plperl', 'src/pl/plperl'); + + foreach my $f (@perl_embed_ccflags) + { + $bool_plperl->AddDefine($f); + $hstore_plperl->AddDefine($f); + $jsonb_plperl->AddDefine($f); + } + } + + my $mf = + Project::read_file('src/backend/utils/mb/conversion_procs/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ m{SUBDIRS\s*=\s*(.*)$}m + || die 'Could not match in conversion makefile' . "\n"; + foreach my $sub (split /\s+/, $1) + { + my $dir = 'src/backend/utils/mb/conversion_procs/' . $sub; + my $p = $solution->AddProject($sub, 'dll', 'conversion procs', $dir); + $p->AddFile("$dir/$sub.c"); # implicit source file + $p->AddReference($postgres); + } + + $mf = Project::read_file('src/bin/scripts/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ m{PROGRAMS\s*=\s*(.*)$}m + || die 'Could not match in bin/scripts/Makefile' . "\n"; + foreach my $prg (split /\s+/, $1) + { + my $proj = $solution->AddProject($prg, 'exe', 'bin'); + $mf =~ m{$prg\s*:\s*(.*)$}m + || die 'Could not find script define for $prg' . "\n"; + my @files = split /\s+/, $1; + foreach my $f (@files) + { + $f =~ s/\.o$/\.c/; + if ($f =~ /\.c$/) + { + $proj->AddFile('src/bin/scripts/' . $f); + } + } + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq, $libpgfeutils, $libpgcommon, $libpgport); + $proj->AddDirResourceFile('src/bin/scripts'); + $proj->AddLibrary('ws2_32.lib'); + } + + # Regression DLL and EXE + my $regress = $solution->AddProject('regress', 'dll', 'misc'); + $regress->AddFile('src/test/regress/regress.c'); + $regress->AddDirResourceFile('src/test/regress'); + $regress->AddReference($postgres); + + my $pgregress = $solution->AddProject('pg_regress', 'exe', 'misc'); + $pgregress->AddFile('src/test/regress/pg_regress.c'); + $pgregress->AddFile('src/test/regress/pg_regress_main.c'); + $pgregress->AddIncludeDir('src/port'); + $pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress->AddLibrary('ws2_32.lib'); + $pgregress->AddDirResourceFile('src/test/regress'); + $pgregress->AddReference($libpgcommon, $libpgport); + + # fix up pg_waldump once it's been set up + # files symlinked on Unix are copied on windows + my $pg_waldump = AddSimpleFrontend('pg_waldump'); + $pg_waldump->AddDefine('FRONTEND'); + foreach my $xf (glob('src/backend/access/rmgrdesc/*desc.c')) + { + $pg_waldump->AddFile($xf); + } + $pg_waldump->AddFile('src/backend/access/transam/xlogreader.c'); + + $solution->Save(); + return $solution->{vcver}; +} + +##################### +# Utility functions # +##################### + +# Add a simple frontend project (exe) +sub AddSimpleFrontend +{ + my $n = shift; + my $uselibpq = shift; + + my $p = $solution->AddProject($n, 'exe', 'bin'); + $p->AddDir('src/bin/' . $n); + $p->AddReference($libpgfeutils, $libpgcommon, $libpgport); + if ($uselibpq) + { + $p->AddIncludeDir('src/interfaces/libpq'); + $p->AddReference($libpq); + } + + # Adjust module definition using frontend variables + AdjustFrontendProj($p); + + return $p; +} + +# Add a simple transform module +sub AddTransformModule +{ + my $n = shift; + my $n_src = shift; + my $pl_proj_name = shift; + my $pl_src = shift; + my $type_name = shift; + my $type_src = shift; + + my $type_proj = undef; + if ($type_name) + { + foreach my $proj (@{ $solution->{projects}->{'contrib'} }) + { + if ($proj->{name} eq $type_name) + { + $type_proj = $proj; + last; + } + } + die "could not find base module $type_name for transform module $n" + if (!defined($type_proj)); + } + + my $pl_proj = undef; + foreach my $proj (@{ $solution->{projects}->{'PLs'} }) + { + if ($proj->{name} eq $pl_proj_name) + { + $pl_proj = $proj; + last; + } + } + die "could not find PL $pl_proj_name for transform module $n" + if (!defined($pl_proj)); + + my $p = $solution->AddProject($n, 'dll', 'contrib', $n_src); + for my $file (glob("$n_src/*.c")) + { + $p->AddFile($file); + } + $p->AddReference($postgres); + + # Add PL dependencies + $p->AddIncludeDir($pl_src); + $p->AddReference($pl_proj); + $p->AddIncludeDir($_) for @{ $pl_proj->{includes} }; + foreach my $pl_lib (@{ $pl_proj->{libraries} }) + { + $p->AddLibrary($pl_lib); + } + + # Add base module dependencies + if ($type_proj) + { + $p->AddIncludeDir($type_src); + $p->AddIncludeDir($_) for @{ $type_proj->{includes} }; + foreach my $type_lib (@{ $type_proj->{libraries} }) + { + $p->AddLibrary($type_lib); + } + $p->AddReference($type_proj); + } + + return $p; +} + +# Add a simple contrib project +sub AddContrib +{ + my $subdir = shift; + my $n = shift; + my $mf = Project::read_file("$subdir/$n/Makefile"); + my @projects = (); + + if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg) + { + my $dn = $1; + my $proj = $solution->AddProject($dn, 'dll', 'contrib', "$subdir/$n"); + $proj->AddReference($postgres); + AdjustContribProj($proj); + push @projects, $proj; + } + elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg) + { + foreach my $mod (split /\s+/, $1) + { + my $proj = + $solution->AddProject($mod, 'dll', 'contrib', "$subdir/$n"); + my $filename = $mod . '.c'; + $proj->AddFile("$subdir/$n/$filename"); + $proj->AddReference($postgres); + AdjustContribProj($proj); + push @projects, $proj; + } + } + elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg) + { + my $proj = $solution->AddProject($1, 'exe', 'contrib', "$subdir/$n"); + AdjustContribProj($proj); + push @projects, $proj; + } + else + { + croak "Could not determine contrib module type for $n\n"; + } + + # Process custom compiler flags + if ( $mf =~ /^PG_CPPFLAGS\s*=\s*(.*)$/mg + || $mf =~ /^override\s*CPPFLAGS\s*[+:]?=\s*(.*)$/mg) + { + foreach my $flag (split /\s+/, $1) + { + if ($flag =~ /^-D(.*)$/) + { + foreach my $proj (@projects) + { + $proj->AddDefine($1); + } + } + elsif ($flag =~ /^-I(.*)$/) + { + if ($1 eq '$(libpq_srcdir)') + { + foreach my $proj (@projects) + { + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq); + } + } + } + } + } + + if ($mf =~ /^SHLIB_LINK_INTERNAL\s*[+:]?=\s*(.*)$/mg) + { + foreach my $lib (split /\s+/, $1) + { + if ($lib eq '$(libpq)') + { + foreach my $proj (@projects) + { + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq); + } + } + } + } + + if ($mf =~ /^PG_LIBS_INTERNAL\s*[+:]?=\s*(.*)$/mg) + { + foreach my $lib (split /\s+/, $1) + { + if ($lib eq '$(libpq_pgport)') + { + foreach my $proj (@projects) + { + $proj->AddReference($libpgport); + $proj->AddReference($libpgcommon); + } + } + } + } + + foreach my $line (split /\n/, $mf) + { + if ($line =~ /^[A-Za-z0-9_]*\.o:\s(.*)/) + { + foreach my $file (split /\s+/, $1) + { + foreach my $proj (@projects) + { + $proj->AddDependantFiles("$subdir/$n/$file"); + } + } + } + } + + # Are there any output data files to build? + GenerateContribSqlFiles($n, $mf); + return; +} + +sub GenerateContribSqlFiles +{ + my $n = shift; + my $mf = shift; + $mf =~ s{\\\r?\n}{}g; + if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg) + { + my $l = $1; + + # Strip out $(addsuffix) rules + if (index($l, '$(addsuffix ') >= 0) + { + my $pcount = 0; + my $i; + for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++) + { + $pcount++ if (substr($l, $i, 1) eq '('); + $pcount-- if (substr($l, $i, 1) eq ')'); + last if ($pcount < 0); + } + $l = + substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i + 1); + } + + foreach my $d (split /\s+/, $l) + { + my $in = "$d.in"; + my $out = "$d"; + + if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in")) + { + print "Building $out from $in (contrib/$n)...\n"; + my $cont = Project::read_file("contrib/$n/$in"); + my $dn = $out; + $dn =~ s/\.sql$//; + $cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g; + my $o; + open($o, '>', "contrib/$n/$out") + || croak "Could not write to contrib/$n/$d"; + print $o $cont; + close($o); + } + } + } + return; +} + +sub AdjustContribProj +{ + my $proj = shift; + AdjustModule( + $proj, $contrib_defines, + \@contrib_uselibpq, \@contrib_uselibpgport, + \@contrib_uselibpgcommon, $contrib_extralibs, + $contrib_extrasource, $contrib_extraincludes); + return; +} + +sub AdjustFrontendProj +{ + my $proj = shift; + AdjustModule( + $proj, $frontend_defines, + \@frontend_uselibpq, \@frontend_uselibpgport, + \@frontend_uselibpgcommon, $frontend_extralibs, + $frontend_extrasource, $frontend_extraincludes); + return; +} + +sub AdjustModule +{ + my $proj = shift; + my $module_defines = shift; + my $module_uselibpq = shift; + my $module_uselibpgport = shift; + my $module_uselibpgcommon = shift; + my $module_extralibs = shift; + my $module_extrasource = shift; + my $module_extraincludes = shift; + my $n = $proj->{name}; + + if ($module_defines->{$n}) + { + foreach my $d ($module_defines->{$n}) + { + $proj->AddDefine($d); + } + } + if (grep { /^$n$/ } @{$module_uselibpq}) + { + $proj->AddIncludeDir('src\interfaces\libpq'); + $proj->AddReference($libpq); + } + if (grep { /^$n$/ } @{$module_uselibpgport}) + { + $proj->AddReference($libpgport); + } + if (grep { /^$n$/ } @{$module_uselibpgcommon}) + { + $proj->AddReference($libpgcommon); + } + if ($module_extralibs->{$n}) + { + foreach my $l (@{ $module_extralibs->{$n} }) + { + $proj->AddLibrary($l); + } + } + if ($module_extraincludes->{$n}) + { + foreach my $i (@{ $module_extraincludes->{$n} }) + { + $proj->AddIncludeDir($i); + } + } + if ($module_extrasource->{$n}) + { + foreach my $i (@{ $module_extrasource->{$n} }) + { + print "Files $i\n"; + $proj->AddFile($i); + } + } + return; +} + +END +{ + unlink @unlink_on_exit; +} + +1; diff --git a/src/tools/msvc/Project.pm b/src/tools/msvc/Project.pm new file mode 100644 index 0000000..570bab5 --- /dev/null +++ b/src/tools/msvc/Project.pm @@ -0,0 +1,494 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Project; + +# +# Package that encapsulates a Visual C++ project file generation +# +# src/tools/msvc/Project.pm +# +use Carp; +use strict; +use warnings; +use File::Basename; + +sub _new +{ + my ($classname, $name, $type, $solution) = @_; + my $good_types = { + lib => 1, + exe => 1, + dll => 1, + }; + confess("Bad project type: $type\n") unless exists $good_types->{$type}; + my $self = { + name => $name, + type => $type, + guid => $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE', + files => {}, + references => [], + libraries => [], + suffixlib => [], + includes => [], + prefixincludes => '', + defines => ';', + solution => $solution, + disablewarnings => '4018;4244;4273;4101;4102;4090;4267', + disablelinkerwarnings => '', + platform => $solution->{platform}, + }; + + bless($self, $classname); + return $self; +} + +sub AddFile +{ + my ($self, $filename) = @_; + + $self->FindAndAddAdditionalFiles($filename); + $self->{files}->{$filename} = 1; + return; +} + +sub AddDependantFiles +{ + my ($self, $filename) = @_; + + $self->FindAndAddAdditionalFiles($filename); + return; +} + +sub AddFiles +{ + my $self = shift; + my $dir = shift; + + while (my $f = shift) + { + $self->AddFile($dir . "/" . $f, 1); + } + return; +} + +# Handle Makefile rules by searching for other files which exist with the same +# name but a different file extension and add those files too. +sub FindAndAddAdditionalFiles +{ + my $self = shift; + my $fname = shift; + $fname =~ /(.*)(\.[^.]+)$/; + my $filenoext = $1; + my $fileext = $2; + + # For .c files, check if either a .l or .y file of the same name + # exists and add that too. + if ($fileext eq ".c") + { + my $file = $filenoext . ".l"; + if (-e $file) + { + $self->AddFile($file); + } + + $file = $filenoext . ".y"; + if (-e $file) + { + $self->AddFile($file); + } + } +} + +sub ReplaceFile +{ + my ($self, $filename, $newname) = @_; + my $re = "\\/$filename\$"; + + foreach my $file (keys %{ $self->{files} }) + { + + # Match complete filename + if ($filename =~ m!/!) + { + if ($file eq $filename) + { + delete $self->{files}{$file}; + $self->AddFile($newname); + return; + } + } + elsif ($file =~ m/($re)/) + { + delete $self->{files}{$file}; + $self->AddFile("$newname/$filename"); + return; + } + } + confess("Could not find file $filename to replace\n"); +} + +sub RemoveFile +{ + my ($self, $filename) = @_; + my $orig = scalar keys %{ $self->{files} }; + delete $self->{files}->{$filename}; + if ($orig > scalar keys %{ $self->{files} }) + { + return; + } + confess("Could not find file $filename to remove\n"); +} + +sub RelocateFiles +{ + my ($self, $targetdir, $proc) = @_; + foreach my $f (keys %{ $self->{files} }) + { + my $r = &$proc($f); + if ($r) + { + $self->RemoveFile($f); + $self->AddFile($targetdir . '/' . basename($f)); + } + } + return; +} + +sub AddReference +{ + my $self = shift; + + while (my $ref = shift) + { + if (!grep { $_ eq $ref } @{ $self->{references} }) + { + push @{ $self->{references} }, $ref; + } + $self->AddLibrary( + "__CFGNAME__/" . $ref->{name} . "/" . $ref->{name} . ".lib"); + } + return; +} + +sub AddLibrary +{ + my ($self, $lib, $dbgsuffix) = @_; + + # quote lib name if it has spaces and isn't already quoted + if ($lib =~ m/\s/ && $lib !~ m/^[&]quot;/) + { + $lib = '"' . $lib . """; + } + + if (!grep { $_ eq $lib } @{ $self->{libraries} }) + { + push @{ $self->{libraries} }, $lib; + } + + if ($dbgsuffix) + { + push @{ $self->{suffixlib} }, $lib; + } + return; +} + +sub AddIncludeDir +{ + my ($self, $incstr) = @_; + + foreach my $inc (split(/;/, $incstr)) + { + if (!grep { $_ eq $inc } @{ $self->{includes} }) + { + push @{ $self->{includes} }, $inc; + } + } + return; +} + +sub AddPrefixInclude +{ + my ($self, $inc) = @_; + + $self->{prefixincludes} = $inc . ';' . $self->{prefixincludes}; + return; +} + +sub AddDefine +{ + my ($self, $def) = @_; + + $def =~ s/"/""/g; + $self->{defines} .= $def . ';'; + return; +} + +sub FullExportDLL +{ + my ($self, $libname) = @_; + + $self->{builddef} = 1; + $self->{def} = "./__CFGNAME__/$self->{name}/$self->{name}.def"; + $self->{implib} = "__CFGNAME__/$self->{name}/$libname"; + return; +} + +sub UseDef +{ + my ($self, $def) = @_; + + $self->{def} = $def; + return; +} + +sub AddDir +{ + my ($self, $reldir) = @_; + my $mf = read_makefile($reldir); + + $mf =~ s{\\\r?\n}{}g; + if ($mf =~ m{^(?:SUB)?DIRS[^=]*=\s*(.*)$}mg) + { + foreach my $subdir (split /\s+/, $1) + { + next + if $subdir eq "\$(top_builddir)/src/timezone" + ; #special case for non-standard include + next + if $reldir . "/" . $subdir eq "src/backend/port/darwin"; + + $self->AddDir($reldir . "/" . $subdir); + } + } + while ($mf =~ m{^(?:EXTRA_)?OBJS[^=]*=\s*(.*)$}m) + { + my $s = $1; + my $filter_re = qr{\$\(filter ([^,]+),\s+\$\(([^\)]+)\)\)}; + while ($s =~ /$filter_re/) + { + + # Process $(filter a b c, $(VAR)) expressions + my $list = $1; + my $filter = $2; + $list =~ s/\.o/\.c/g; + my @pieces = split /\s+/, $list; + my $matches = ""; + foreach my $p (@pieces) + { + + if ($filter eq "LIBOBJS") + { + no warnings qw(once); + if (grep(/$p/, @main::pgportfiles) == 1) + { + $p =~ s/\.c/\.o/; + $matches .= $p . " "; + } + } + else + { + confess "Unknown filter $filter\n"; + } + } + $s =~ s/$filter_re/$matches/; + } + foreach my $f (split /\s+/, $s) + { + next if $f =~ /^\s*$/; + next if $f eq "\\"; + next if $f =~ /\/SUBSYS.o$/; + $f =~ s/,$// + ; # Remove trailing comma that can show up from filter stuff + next unless $f =~ /.*\.o$/; + $f =~ s/\.o$/\.c/; + if ($f =~ /^\$\(top_builddir\)\/(.*)/) + { + $f = $1; + $self->AddFile($f); + } + else + { + $self->AddFile("$reldir/$f"); + } + } + $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m; + } + + # Match rules that pull in source files from different directories, eg + # pgstrcasecmp.c rint.c snprintf.c: % : $(top_srcdir)/src/port/% + my $replace_re = + qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+\n}m; + while ($mf =~ m{$replace_re}m) + { + my $match = $1; + my $top = $2; + my $target = $3; + my @pieces = split /\s+/, $match; + foreach my $fn (@pieces) + { + if ($top eq "(top_srcdir)") + { + eval { $self->ReplaceFile($fn, $target) }; + } + elsif ($top eq "(backend_src)") + { + eval { $self->ReplaceFile($fn, "src/backend/$target") }; + } + else + { + confess "Bad replacement top: $top, on line $_\n"; + } + } + $mf =~ s{$replace_re}{}m; + } + + $self->AddDirResourceFile($reldir); + return; +} + +# If the directory's Makefile bears a description string, add a resource file. +sub AddDirResourceFile +{ + my ($self, $reldir) = @_; + my $mf = read_makefile($reldir); + + if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m) + { + my $desc = $1; + my $ico; + if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; } + $self->AddResourceFile($reldir, $desc, $ico); + } + return; +} + +sub AddResourceFile +{ + my ($self, $dir, $desc, $ico) = @_; + + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + localtime(time); + my $d = sprintf("%02d%03d", ($year - 100), $yday); + + if (Solution::IsNewer("$dir/win32ver.rc", 'src/port/win32ver.rc')) + { + print "Generating win32ver.rc for $dir\n"; + open(my $i, '<', 'src/port/win32ver.rc') + || confess "Could not open win32ver.rc"; + open(my $o, '>', "$dir/win32ver.rc") + || confess "Could not write win32ver.rc"; + my $icostr = $ico ? "IDI_ICON ICON \"src/port/$ico.ico\"" : ""; + while (<$i>) + { + s/FILEDESC/"$desc"/gm; + s/_ICO_/$icostr/gm; + s/(VERSION.*),0/$1,$d/; + if ($self->{type} eq "dll") + { + s/VFT_APP/VFT_DLL/gm; + my $name = $self->{name}; + s/_INTERNAL_NAME_/"$name"/; + s/_ORIGINAL_NAME_/"$name.dll"/; + } + else + { + /_INTERNAL_NAME_/ && next; + /_ORIGINAL_NAME_/ && next; + } + print $o $_; + } + close($o); + close($i); + } + $self->AddFile("$dir/win32ver.rc"); + return; +} + +sub DisableLinkerWarnings +{ + my ($self, $warnings) = @_; + + $self->{disablelinkerwarnings} .= ',' + unless ($self->{disablelinkerwarnings} eq ''); + $self->{disablelinkerwarnings} .= $warnings; + return; +} + +sub Save +{ + my ($self) = @_; + + # If doing DLL and haven't specified a DEF file, do a full export of all symbols + # in the project. + if ($self->{type} eq "dll" && !$self->{def}) + { + $self->FullExportDLL($self->{name} . ".lib"); + } + + # Warning 4197 is about double exporting, disable this per + # http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99193 + $self->DisableLinkerWarnings('4197') if ($self->{platform} eq 'x64'); + + # Dump the project + open(my $f, '>', "$self->{name}$self->{filenameExtension}") + || croak( + "Could not write to $self->{name}$self->{filenameExtension}\n"); + $self->WriteHeader($f); + $self->WriteFiles($f); + $self->Footer($f); + close($f); + return; +} + +sub GetAdditionalLinkerDependencies +{ + my ($self, $cfgname, $separator) = @_; + my $libcfg = (uc $cfgname eq "RELEASE") ? "MD" : "MDd"; + my $libs = ''; + foreach my $lib (@{ $self->{libraries} }) + { + my $xlib = $lib; + foreach my $slib (@{ $self->{suffixlib} }) + { + if ($slib eq $lib) + { + $xlib =~ s/\.lib$/$libcfg.lib/; + last; + } + } + $libs .= $xlib . $separator; + } + $libs =~ s/.$//; + $libs =~ s/__CFGNAME__/$cfgname/g; + return $libs; +} + +# Utility function that loads a complete file +sub read_file +{ + my $filename = shift; + my $F; + local $/ = undef; + open($F, '<', $filename) || croak "Could not open file $filename\n"; + my $txt = <$F>; + close($F); + + return $txt; +} + +sub read_makefile +{ + my $reldir = shift; + my $F; + local $/ = undef; + open($F, '<', "$reldir/GNUmakefile") + || open($F, '<', "$reldir/Makefile") + || confess "Could not open $reldir/Makefile\n"; + my $txt = <$F>; + close($F); + + return $txt; +} + +1; diff --git a/src/tools/msvc/README b/src/tools/msvc/README new file mode 100644 index 0000000..473d6f4 --- /dev/null +++ b/src/tools/msvc/README @@ -0,0 +1,99 @@ +src/tools/msvc/README + +MSVC build +========== + +This directory contains the tools required to build PostgreSQL using +Microsoft Visual Studio 2013 - 2022. This builds the whole backend, not just +the libpq frontend library. For more information, see the documentation +chapter "Installation on Windows" and the description below. + + +Notes about Visual Studio Express +--------------------------------- +To build PostgreSQL using Visual Studio Express, the Microsoft Windows SDK +has to be installed. Since this is not included in the product +originally, extra steps are needed to make it work. + +First, download and install a supported version of the Microsoft Windows SDK +from www.microsoft.com (v8.1a or greater). + +Locate the files vcprojectengine.dll.express.config and +vcprojectengine.dll.config in the vc\vcpackages directory of +the Visual C++ Express installation. In these files, add the paths +to the Platform SDK to the Include, Library and Path tags. Be sure +to add them to the beginning of the list. + +This should work for both GUI and commandline builds, but a restart +may be necessary. + +If you are using a recent version of the Microsoft Windows SDK that includes +the compilers and build tools you probably don't even need Visual Studio +Express to build PostgreSQL. + + +Structure of the build tools +---------------------------- +The tools for building PostgreSQL using Microsoft Visual Studio currently +consist of the following files: + +- Configuration files - +config_default.pl default configuration arguments + +A typical build environment has two more files, buildenv.pl and config.pl +that contain the user's build environment settings and configuration +arguments. + + +- User tools - +build.pl tool to build the binaries +clean.bat batch file for cleaning up generated files +install.pl tool to install the generated files +mkvcbuild.pl tool to generate the Visual Studio build files +vcregress.pl tool to run the regression tests + + +- Internal tools - +gendef.pl internal tool to generate .DEF files +pgbison.pl internal tool to process .y files using bison +pgflex.pl internal tool to process .l files using flex + +Many of those .pl files also have a corresponding .bat-wrapper that doesn't +contain any additional logic. + + +- Internal modules - +Install.pm module containing the install logic +Mkvcbuild.pm module containing the code to generate the Visual + Studio build (project/solution) files +MSBuildProject.pm module containing the code to generate MSBuild based + project files (Visual Studio 2013 or greater) +Project.pm module containing the common code to generate the + Visual Studio project files. Also provides the + common interface of all project file generators +Solution.pm module containing the code to generate the Visual + Studio solution files. +VSObjectFactory.pm factory module providing the code to create the + appropriate project/solution files for the current + environment + + +Description of the internals of the Visual Studio build process +--------------------------------------------------------------- +By typing 'build' the user starts the build.bat wrapper which simply passes +its arguments to build.pl. +In build.pl the user's buildenv.pl is used to set up the build environment +(i. e. path to bison and flex). In addition his config.pl file is merged into +config_default.pl to create the configuration arguments. +These configuration arguments are passed over to Mkvcbuild::mkvcbuild +(Mkvcbuild.pm) which creates the Visual Studio project and solution files. +It does this by using VSObjectFactory::CreateSolution to create an object +implementing the Solution interface (this could be either VS2013Solution, +VS2015Solution, VS2017Solution, VS2019Solution or VS2022Solution, all in +Solution.pm, depending on the user's build environment) and adding objects +implementing the corresponding Project interface (VC2013Project, +VC2015Project, VC2017Project, VC2019Project or VC2022Project from +MSBuildProject.pm) to it. When Solution::Save is called, the implementations +of Solution and Project save their content in the appropriate format. +The final step of starting the appropriate build program (msbuild) is +performed in build.pl again. diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm new file mode 100644 index 0000000..790f03b --- /dev/null +++ b/src/tools/msvc/Solution.pm @@ -0,0 +1,1378 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Solution; + +# +# Package that encapsulates a Visual C++ solution file generation +# +# src/tools/msvc/Solution.pm +# +use Carp; +use strict; +use warnings; +use VSObjectFactory; + +no warnings qw(redefine); ## no critic + +sub _new +{ + my $classname = shift; + my $options = shift; + my $self = { + projects => {}, + options => $options, + VisualStudioVersion => undef, + MinimumVisualStudioVersion => undef, + vcver => undef, + platform => undef, + }; + bless($self, $classname); + + $self->DeterminePlatform(); + + if ($options->{xslt} && !$options->{xml}) + { + die "XSLT requires XML\n"; + } + $options->{blocksize} = 8 + unless $options->{blocksize}; # undef or 0 means default + die "Bad blocksize $options->{blocksize}" + unless grep { $_ == $options->{blocksize} } (1, 2, 4, 8, 16, 32); + $options->{segsize} = 1 + unless $options->{segsize}; # undef or 0 means default + # only allow segsize 1 for now, as we can't do large files yet in windows + die "Bad segsize $options->{segsize}" + unless $options->{segsize} == 1; + $options->{wal_blocksize} = 8 + unless $options->{wal_blocksize}; # undef or 0 means default + die "Bad wal_blocksize $options->{wal_blocksize}" + unless grep { $_ == $options->{wal_blocksize} } + (1, 2, 4, 8, 16, 32, 64); + + return $self; +} + +sub GetAdditionalHeaders +{ + return ''; +} + +sub DeterminePlatform +{ + my $self = shift; + + if ($^O eq "MSWin32") + { + # Examine CL help output to determine if we are in 32 or 64-bit mode. + my $output = `cl /help 2>&1`; + $? >> 8 == 0 or die "cl command not found"; + $self->{platform} = + ($output =~ /^\/favor:<.+AMD64/m) ? 'x64' : 'Win32'; + } + else + { + $self->{platform} = 'FAKE'; + } + print "Detected hardware platform: $self->{platform}\n"; + return; +} + +# Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist. +# Special case - if config.pl has changed, always return 1 +sub IsNewer +{ + my ($newfile, $oldfile) = @_; + -e $oldfile or warn "source file \"$oldfile\" does not exist"; + if ( $oldfile ne 'src/tools/msvc/config.pl' + && $oldfile ne 'src/tools/msvc/config_default.pl') + { + return 1 + if (-f 'src/tools/msvc/config.pl') + && IsNewer($newfile, 'src/tools/msvc/config.pl'); + return 1 + if (-f 'src/tools/msvc/config_default.pl') + && IsNewer($newfile, 'src/tools/msvc/config_default.pl'); + } + return 1 if (!(-e $newfile)); + my @nstat = stat($newfile); + my @ostat = stat($oldfile); + return 1 if ($nstat[9] < $ostat[9]); + return 0; +} + +# Copy a file, *not* preserving date. Only works for text files. +sub copyFile +{ + my ($src, $dest) = @_; + open(my $i, '<', $src) || croak "Could not open $src"; + open(my $o, '>', $dest) || croak "Could not open $dest"; + while (<$i>) + { + print $o $_; + } + close($i); + close($o); + return; +} + +# Fetch version of OpenSSL based on a parsing of the command shipped with +# the installer this build is linking to. This returns as result an array +# made of the three first digits of the OpenSSL version, which is enough +# to decide which options to apply depending on the version of OpenSSL +# linking with. +sub GetOpenSSLVersion +{ + my $self = shift; + + # Attempt to get OpenSSL version and location. This assumes that + # openssl.exe is in the specified directory. + # Quote the .exe name in case it has spaces + my $opensslcmd = + qq("$self->{options}->{openssl}\\bin\\openssl.exe" version 2>&1); + my $sslout = `$opensslcmd`; + + $? >> 8 == 0 + or croak + "Unable to determine OpenSSL version: The openssl.exe command wasn't found."; + + if ($sslout =~ /(\d+)\.(\d+)\.(\d+)(\D)/m) + { + return ($1, $2, $3); + } + + croak + "Unable to determine OpenSSL version: The openssl.exe version could not be determined."; +} + +sub GenerateFiles +{ + my $self = shift; + my $bits = $self->{platform} eq 'Win32' ? 32 : 64; + my $ac_init_found = 0; + my $package_name; + my $package_version; + my $package_bugreport; + my $package_url; + my ($majorver, $minorver); + my $ac_define_openssl_api_compat_found = 0; + my $openssl_api_compat; + + # Parse configure.ac to get version numbers + open(my $c, '<', "configure.ac") + || confess("Could not open configure.ac for reading\n"); + while (<$c>) + { + if (/^AC_INIT\(\[([^\]]+)\], \[([^\]]+)\], \[([^\]]+)\], \[([^\]]*)\], \[([^\]]+)\]/ + ) + { + $ac_init_found = 1; + + $package_name = $1; + $package_version = $2; + $package_bugreport = $3; + #$package_tarname = $4; + $package_url = $5; + + if ($package_version !~ /^(\d+)(?:\.(\d+))?/) + { + confess "Bad format of version: $package_version\n"; + } + $majorver = sprintf("%d", $1); + $minorver = sprintf("%d", $2 ? $2 : 0); + } + elsif (/\bAC_DEFINE\(OPENSSL_API_COMPAT, \[([0-9xL]+)\]/) + { + $ac_define_openssl_api_compat_found = 1; + $openssl_api_compat = $1; + } + } + close($c); + confess "Unable to parse configure.ac for all variables!" + unless $ac_init_found && $ac_define_openssl_api_compat_found; + + if (IsNewer("src/include/pg_config_os.h", "src/include/port/win32.h")) + { + print "Copying pg_config_os.h...\n"; + copyFile("src/include/port/win32.h", "src/include/pg_config_os.h"); + } + + print "Generating configuration headers...\n"; + my $extraver = $self->{options}->{extraver}; + $extraver = '' unless defined $extraver; + my $port = $self->{options}->{"--with-pgport"} || 5432; + + # Every symbol in pg_config.h.in must be accounted for here. Set + # to undef if the symbol should not be defined. + my %define = ( + ALIGNOF_DOUBLE => 8, + ALIGNOF_INT => 4, + ALIGNOF_LONG => 4, + ALIGNOF_LONG_LONG_INT => 8, + ALIGNOF_PG_INT128_TYPE => undef, + ALIGNOF_SHORT => 2, + AC_APPLE_UNIVERSAL_BUILD => undef, + BLCKSZ => 1024 * $self->{options}->{blocksize}, + CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"', + DEF_PGPORT => $port, + DEF_PGPORT_STR => qq{"$port"}, + DLSUFFIX => '".dll"', + ENABLE_GSS => $self->{options}->{gss} ? 1 : undef, + ENABLE_NLS => $self->{options}->{nls} ? 1 : undef, + ENABLE_THREAD_SAFETY => 1, + GETTIMEOFDAY_1ARG => undef, + HAVE_APPEND_HISTORY => undef, + HAVE_ASN1_STRING_GET0_DATA => undef, + HAVE_ATOMICS => 1, + HAVE_ATOMIC_H => undef, + HAVE_BACKTRACE_SYMBOLS => undef, + HAVE_BIO_GET_DATA => undef, + HAVE_BIO_METH_NEW => undef, + HAVE_CLOCK_GETTIME => undef, + HAVE_COMPUTED_GOTO => undef, + HAVE_COPYFILE => undef, + HAVE_COPYFILE_H => undef, + HAVE_CRTDEFS_H => undef, + HAVE_CRYPTO_LOCK => undef, + HAVE_DECL_FDATASYNC => 0, + HAVE_DECL_F_FULLFSYNC => 0, + HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER => 0, + HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER => 0, + HAVE_DECL_LLVMGETHOSTCPUNAME => 0, + HAVE_DECL_LLVMGETHOSTCPUFEATURES => 0, + HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN => 0, + HAVE_DECL_POSIX_FADVISE => 0, + HAVE_DECL_PREADV => 0, + HAVE_DECL_PWRITEV => 0, + HAVE_DECL_RTLD_GLOBAL => 0, + HAVE_DECL_RTLD_NOW => 0, + HAVE_DECL_SIGWAIT => 0, + HAVE_DECL_STRLCAT => 0, + HAVE_DECL_STRLCPY => 0, + HAVE_DECL_STRNLEN => 1, + HAVE_DECL_STRTOLL => 1, + HAVE_DECL_STRTOULL => 1, + HAVE_DLOPEN => undef, + HAVE_EDITLINE_HISTORY_H => undef, + HAVE_EDITLINE_READLINE_H => undef, + HAVE_EXECINFO_H => undef, + HAVE_EXPLICIT_BZERO => undef, + HAVE_FDATASYNC => undef, + HAVE_FLS => undef, + HAVE_FSEEKO => 1, + HAVE_FUNCNAME__FUNC => undef, + HAVE_FUNCNAME__FUNCTION => 1, + HAVE_GCC__ATOMIC_INT32_CAS => undef, + HAVE_GCC__ATOMIC_INT64_CAS => undef, + HAVE_GCC__SYNC_CHAR_TAS => undef, + HAVE_GCC__SYNC_INT32_CAS => undef, + HAVE_GCC__SYNC_INT32_TAS => undef, + HAVE_GCC__SYNC_INT64_CAS => undef, + HAVE_GETADDRINFO => undef, + HAVE_GETHOSTBYNAME_R => undef, + HAVE_GETIFADDRS => undef, + HAVE_GETOPT => undef, + HAVE_GETOPT_H => undef, + HAVE_GETOPT_LONG => undef, + HAVE_GETPEEREID => undef, + HAVE_GETPEERUCRED => undef, + HAVE_GETPWUID_R => undef, + HAVE_GETRLIMIT => undef, + HAVE_GETRUSAGE => undef, + HAVE_GETTIMEOFDAY => undef, + HAVE_GSSAPI_GSSAPI_H => undef, + HAVE_GSSAPI_H => undef, + HAVE_HMAC_CTX_FREE => undef, + HAVE_HMAC_CTX_NEW => undef, + HAVE_HISTORY_H => undef, + HAVE_HISTORY_TRUNCATE_FILE => undef, + HAVE_IFADDRS_H => undef, + HAVE_INET_ATON => undef, + HAVE_INET_PTON => 1, + HAVE_INT_TIMEZONE => 1, + HAVE_INT64 => undef, + HAVE_INT8 => undef, + HAVE_INTTYPES_H => undef, + HAVE_INT_OPTERR => undef, + HAVE_INT_OPTRESET => undef, + HAVE_IPV6 => 1, + HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P => undef, + HAVE_KQUEUE => undef, + HAVE_LANGINFO_H => undef, + HAVE_LDAP_H => undef, + HAVE_LDAP_INITIALIZE => undef, + HAVE_LIBCRYPTO => undef, + HAVE_LIBLDAP => undef, + HAVE_LIBLZ4 => undef, + HAVE_LIBM => undef, + HAVE_LIBPAM => undef, + HAVE_LIBREADLINE => undef, + HAVE_LIBSELINUX => undef, + HAVE_LIBSSL => undef, + HAVE_LIBWLDAP32 => undef, + HAVE_LIBXML2 => undef, + HAVE_LIBXSLT => undef, + HAVE_LIBZ => $self->{options}->{zlib} ? 1 : undef, + HAVE_LIBZSTD => undef, + HAVE_LINK => undef, + HAVE_LOCALE_T => 1, + HAVE_LONG_INT_64 => undef, + HAVE_LONG_LONG_INT_64 => 1, + HAVE_MBARRIER_H => undef, + HAVE_MBSTOWCS_L => 1, + HAVE_MEMORY_H => 1, + HAVE_MEMSET_S => undef, + HAVE_MINIDUMP_TYPE => 1, + HAVE_MKDTEMP => undef, + HAVE_NETINET_TCP_H => undef, + HAVE_NET_IF_H => undef, + HAVE_OPENSSL_INIT_SSL => undef, + HAVE_OSSP_UUID_H => undef, + HAVE_PAM_PAM_APPL_H => undef, + HAVE_POLL => undef, + HAVE_POLL_H => undef, + HAVE_POSIX_DECL_SIGWAIT => undef, + HAVE_POSIX_FADVISE => undef, + HAVE_POSIX_FALLOCATE => undef, + HAVE_PPC_LWARX_MUTEX_HINT => undef, + HAVE_PPOLL => undef, + HAVE_PREAD => undef, + HAVE_PSTAT => undef, + HAVE_PS_STRINGS => undef, + HAVE_PTHREAD => undef, + HAVE_PTHREAD_BARRIER_WAIT => undef, + HAVE_PTHREAD_IS_THREADED_NP => undef, + HAVE_PTHREAD_PRIO_INHERIT => undef, + HAVE_PWRITE => undef, + HAVE_READLINE_H => undef, + HAVE_READLINE_HISTORY_H => undef, + HAVE_READLINE_READLINE_H => undef, + HAVE_READLINK => undef, + HAVE_READV => undef, + HAVE_RL_COMPLETION_MATCHES => undef, + HAVE_RL_COMPLETION_SUPPRESS_QUOTE => undef, + HAVE_RL_FILENAME_COMPLETION_FUNCTION => undef, + HAVE_RL_FILENAME_QUOTE_CHARACTERS => undef, + HAVE_RL_FILENAME_QUOTING_FUNCTION => undef, + HAVE_RL_RESET_SCREEN_SIZE => undef, + HAVE_RL_VARIABLE_BIND => undef, + HAVE_SECURITY_PAM_APPL_H => undef, + HAVE_SETENV => undef, + HAVE_SETPROCTITLE => undef, + HAVE_SETPROCTITLE_FAST => undef, + HAVE_SETSID => undef, + HAVE_SHM_OPEN => undef, + HAVE_SOCKLEN_T => 1, + HAVE_SPINLOCKS => 1, + HAVE_STDBOOL_H => 1, + HAVE_STDINT_H => 1, + HAVE_STDLIB_H => 1, + HAVE_STRCHRNUL => undef, + HAVE_STRERROR_R => undef, + HAVE_STRINGS_H => undef, + HAVE_STRING_H => 1, + HAVE_STRLCAT => undef, + HAVE_STRLCPY => undef, + HAVE_STRNLEN => 1, + HAVE_STRSIGNAL => undef, + HAVE_STRTOF => 1, + HAVE_STRTOLL => 1, + HAVE_STRTOQ => undef, + HAVE_STRTOULL => 1, + HAVE_STRTOUQ => undef, + HAVE_STRUCT_ADDRINFO => 1, + HAVE_STRUCT_CMSGCRED => undef, + HAVE_STRUCT_OPTION => undef, + HAVE_STRUCT_SOCKADDR_SA_LEN => undef, + HAVE_STRUCT_SOCKADDR_STORAGE => 1, + HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY => 1, + HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN => undef, + HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY => undef, + HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN => undef, + HAVE_STRUCT_SOCKADDR_UN => undef, + HAVE_STRUCT_TM_TM_ZONE => undef, + HAVE_SYNC_FILE_RANGE => undef, + HAVE_SYMLINK => 1, + HAVE_SYNCFS => undef, + HAVE_SYSLOG => undef, + HAVE_SYS_EPOLL_H => undef, + HAVE_SYS_EVENT_H => undef, + HAVE_SYS_IPC_H => undef, + HAVE_SYS_PERSONALITY_H => undef, + HAVE_SYS_PRCTL_H => undef, + HAVE_SYS_PROCCTL_H => undef, + HAVE_SYS_PSTAT_H => undef, + HAVE_SYS_RESOURCE_H => undef, + HAVE_SYS_SELECT_H => undef, + HAVE_SYS_SEM_H => undef, + HAVE_SYS_SHM_H => undef, + HAVE_SYS_SIGNALFD_H => undef, + HAVE_SYS_SOCKIO_H => undef, + HAVE_SYS_STAT_H => 1, + HAVE_SYS_TAS_H => undef, + HAVE_SYS_TYPES_H => 1, + HAVE_SYS_UCRED_H => undef, + HAVE_SYS_UIO_H => undef, + HAVE_SYS_UN_H => undef, + HAVE_TERMIOS_H => undef, + HAVE_TYPEOF => undef, + HAVE_UCRED_H => undef, + HAVE_UINT64 => undef, + HAVE_UINT8 => undef, + HAVE_UNION_SEMUN => undef, + HAVE_UNISTD_H => 1, + HAVE_UNSETENV => undef, + HAVE_USELOCALE => undef, + HAVE_UUID_BSD => undef, + HAVE_UUID_E2FS => undef, + HAVE_UUID_OSSP => undef, + HAVE_UUID_H => undef, + HAVE_UUID_UUID_H => undef, + HAVE_WINLDAP_H => undef, + HAVE_WCSTOMBS_L => 1, + HAVE_WCTYPE_H => 1, + HAVE_WRITEV => undef, + HAVE_X509_GET_SIGNATURE_NID => 1, + HAVE_X509_GET_SIGNATURE_INFO => undef, + HAVE_X86_64_POPCNTQ => undef, + HAVE__BOOL => undef, + HAVE__BUILTIN_BSWAP16 => undef, + HAVE__BUILTIN_BSWAP32 => undef, + HAVE__BUILTIN_BSWAP64 => undef, + HAVE__BUILTIN_CLZ => undef, + HAVE__BUILTIN_CONSTANT_P => undef, + HAVE__BUILTIN_CTZ => undef, + HAVE__BUILTIN_FRAME_ADDRESS => undef, + HAVE__BUILTIN_OP_OVERFLOW => undef, + HAVE__BUILTIN_POPCOUNT => undef, + HAVE__BUILTIN_TYPES_COMPATIBLE_P => undef, + HAVE__BUILTIN_UNREACHABLE => undef, + HAVE__CONFIGTHREADLOCALE => 1, + HAVE__CPUID => 1, + HAVE__GET_CPUID => undef, + HAVE__STATIC_ASSERT => undef, + HAVE___STRTOLL => undef, + HAVE___STRTOULL => undef, + INT64_MODIFIER => qq{"ll"}, + LOCALE_T_IN_XLOCALE => undef, + MAXIMUM_ALIGNOF => 8, + MEMSET_LOOP_LIMIT => 1024, + OPENSSL_API_COMPAT => $openssl_api_compat, + PACKAGE_BUGREPORT => qq{"$package_bugreport"}, + PACKAGE_NAME => qq{"$package_name"}, + PACKAGE_STRING => qq{"$package_name $package_version"}, + PACKAGE_TARNAME => lc qq{"$package_name"}, + PACKAGE_URL => qq{"$package_url"}, + PACKAGE_VERSION => qq{"$package_version"}, + PG_INT128_TYPE => undef, + PG_INT64_TYPE => 'long long int', + PG_KRB_SRVNAM => qq{"postgres"}, + PG_MAJORVERSION => qq{"$majorver"}, + PG_MAJORVERSION_NUM => $majorver, + PG_MINORVERSION_NUM => $minorver, + PG_PRINTF_ATTRIBUTE => undef, + PG_USE_STDBOOL => 1, + PG_VERSION => qq{"$package_version$extraver"}, + PG_VERSION_NUM => sprintf("%d%04d", $majorver, $minorver), + PG_VERSION_STR => + qq{"PostgreSQL $package_version$extraver, compiled by Visual C++ build " CppAsString2(_MSC_VER) ", $bits-bit"}, + PROFILE_PID_DIR => undef, + PTHREAD_CREATE_JOINABLE => undef, + RELSEG_SIZE => (1024 / $self->{options}->{blocksize}) * + $self->{options}->{segsize} * 1024, + SIZEOF_BOOL => 1, + SIZEOF_LONG => 4, + SIZEOF_OFF_T => undef, + SIZEOF_SIZE_T => $bits / 8, + SIZEOF_VOID_P => $bits / 8, + STDC_HEADERS => 1, + STRERROR_R_INT => undef, + USE_ARMV8_CRC32C => undef, + USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK => undef, + USE_ASSERT_CHECKING => $self->{options}->{asserts} ? 1 : undef, + USE_BONJOUR => undef, + USE_BSD_AUTH => undef, + USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBXML => undef, + USE_LIBXSLT => undef, + USE_LZ4 => undef, + USE_LDAP => $self->{options}->{ldap} ? 1 : undef, + USE_LLVM => undef, + USE_NAMED_POSIX_SEMAPHORES => undef, + USE_OPENSSL => undef, + USE_PAM => undef, + USE_SLICING_BY_8_CRC32C => undef, + USE_SSE42_CRC32C => undef, + USE_SSE42_CRC32C_WITH_RUNTIME_CHECK => 1, + USE_SYSTEMD => undef, + USE_SYSV_SEMAPHORES => undef, + USE_SYSV_SHARED_MEMORY => undef, + USE_UNNAMED_POSIX_SEMAPHORES => undef, + USE_WIN32_SEMAPHORES => 1, + USE_WIN32_SHARED_MEMORY => 1, + USE_ZSTD => undef, + WCSTOMBS_L_IN_XLOCALE => undef, + WORDS_BIGENDIAN => undef, + XLOG_BLCKSZ => 1024 * $self->{options}->{wal_blocksize}, + _FILE_OFFSET_BITS => undef, + _LARGEFILE_SOURCE => undef, + _LARGE_FILES => undef, + inline => '__inline', + pg_restrict => '__restrict', + # not defined, because it'd conflict with __declspec(restrict) + restrict => undef, + typeof => undef,); + + if ($self->{options}->{uuid}) + { + $define{HAVE_UUID_OSSP} = 1; + $define{HAVE_UUID_H} = 1; + } + if ($self->{options}->{xml}) + { + $define{HAVE_LIBXML2} = 1; + $define{USE_LIBXML} = 1; + } + if ($self->{options}->{xslt}) + { + $define{HAVE_LIBXSLT} = 1; + $define{USE_LIBXSLT} = 1; + } + if ($self->{options}->{lz4}) + { + $define{HAVE_LIBLZ4} = 1; + $define{USE_LZ4} = 1; + } + if ($self->{options}->{zstd}) + { + $define{HAVE_LIBZSTD} = 1; + $define{USE_ZSTD} = 1; + } + if ($self->{options}->{openssl}) + { + $define{USE_OPENSSL} = 1; + + my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion(); + + # Symbols needed with OpenSSL 1.1.1 and above. + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '1')) + { + $define{HAVE_X509_GET_SIGNATURE_INFO} = 1; + } + + # Symbols needed with OpenSSL 1.1.0 and above. + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0')) + { + $define{HAVE_ASN1_STRING_GET0_DATA} = 1; + $define{HAVE_BIO_GET_DATA} = 1; + $define{HAVE_BIO_METH_NEW} = 1; + $define{HAVE_HMAC_CTX_FREE} = 1; + $define{HAVE_HMAC_CTX_NEW} = 1; + $define{HAVE_OPENSSL_INIT_SSL} = 1; + } + } + + $self->GenerateConfigHeader('src/include/pg_config.h', \%define, 1); + $self->GenerateConfigHeader('src/include/pg_config_ext.h', \%define, 0); + $self->GenerateConfigHeader('src/interfaces/ecpg/include/ecpg_config.h', + \%define, 0); + + $self->GenerateDefFile( + "src/interfaces/libpq/libpqdll.def", + "src/interfaces/libpq/exports.txt", + "LIBPQ"); + $self->GenerateDefFile( + "src/interfaces/ecpg/ecpglib/ecpglib.def", + "src/interfaces/ecpg/ecpglib/exports.txt", + "LIBECPG"); + $self->GenerateDefFile( + "src/interfaces/ecpg/compatlib/compatlib.def", + "src/interfaces/ecpg/compatlib/exports.txt", + "LIBECPG_COMPAT"); + $self->GenerateDefFile( + "src/interfaces/ecpg/pgtypeslib/pgtypeslib.def", + "src/interfaces/ecpg/pgtypeslib/exports.txt", + "LIBPGTYPES"); + + chdir('src/backend/utils'); + my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat'; + if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl') + || IsNewer('fmgr-stamp', '../catalog/Catalog.pm') + || IsNewer('fmgr-stamp', $pg_proc_dat) + || IsNewer('fmgr-stamp', '../../../src/include/access/transam.h')) + { + system( + "perl -I ../catalog Gen_fmgrtab.pl --include-path ../../../src/include/ $pg_proc_dat" + ); + open(my $f, '>', 'fmgr-stamp') + || confess "Could not touch fmgr-stamp"; + close($f); + } + chdir('../../..'); + + if (IsNewer( + 'src/include/utils/fmgroids.h', + 'src/backend/utils/fmgroids.h')) + { + copyFile('src/backend/utils/fmgroids.h', + 'src/include/utils/fmgroids.h'); + } + + if (IsNewer( + 'src/include/utils/fmgrprotos.h', + 'src/backend/utils/fmgrprotos.h')) + { + copyFile( + 'src/backend/utils/fmgrprotos.h', + 'src/include/utils/fmgrprotos.h'); + } + + if (IsNewer( + 'src/include/storage/lwlocknames.h', + 'src/backend/storage/lmgr/lwlocknames.txt')) + { + print "Generating lwlocknames.c and lwlocknames.h...\n"; + chdir('src/backend/storage/lmgr'); + system('perl generate-lwlocknames.pl lwlocknames.txt'); + chdir('../../../..'); + } + if (IsNewer( + 'src/include/storage/lwlocknames.h', + 'src/backend/storage/lmgr/lwlocknames.h')) + { + copyFile( + 'src/backend/storage/lmgr/lwlocknames.h', + 'src/include/storage/lwlocknames.h'); + } + + if (IsNewer('src/include/utils/probes.h', 'src/backend/utils/probes.d')) + { + print "Generating probes.h...\n"; + system( + 'perl src/backend/utils/Gen_dummy_probes.pl src/backend/utils/probes.d > src/include/utils/probes.h' + ); + } + + if ($self->{options}->{python} + && IsNewer( + 'src/pl/plpython/spiexceptions.h', + 'src/backend/utils/errcodes.txt')) + { + print "Generating spiexceptions.h...\n"; + system( + 'perl src/pl/plpython/generate-spiexceptions.pl src/backend/utils/errcodes.txt > src/pl/plpython/spiexceptions.h' + ); + } + + if (IsNewer( + 'src/include/utils/errcodes.h', + 'src/backend/utils/errcodes.txt')) + { + print "Generating errcodes.h...\n"; + system( + 'perl src/backend/utils/generate-errcodes.pl src/backend/utils/errcodes.txt > src/backend/utils/errcodes.h' + ); + copyFile('src/backend/utils/errcodes.h', + 'src/include/utils/errcodes.h'); + } + + if (IsNewer( + 'src/pl/plpgsql/src/plerrcodes.h', + 'src/backend/utils/errcodes.txt')) + { + print "Generating plerrcodes.h...\n"; + system( + 'perl src/pl/plpgsql/src/generate-plerrcodes.pl src/backend/utils/errcodes.txt > src/pl/plpgsql/src/plerrcodes.h' + ); + } + + if ($self->{options}->{tcl} + && IsNewer( + 'src/pl/tcl/pltclerrcodes.h', 'src/backend/utils/errcodes.txt')) + { + print "Generating pltclerrcodes.h...\n"; + system( + 'perl src/pl/tcl/generate-pltclerrcodes.pl src/backend/utils/errcodes.txt > src/pl/tcl/pltclerrcodes.h' + ); + } + + if (IsNewer('src/bin/psql/sql_help.h', 'src/bin/psql/create_help.pl')) + { + print "Generating sql_help.h...\n"; + chdir('src/bin/psql'); + system("perl create_help.pl ../../../doc/src/sgml/ref sql_help"); + chdir('../../..'); + } + + if (IsNewer('src/common/kwlist_d.h', 'src/include/parser/kwlist.h')) + { + print "Generating kwlist_d.h...\n"; + system( + 'perl -I src/tools src/tools/gen_keywordlist.pl --extern -o src/common src/include/parser/kwlist.h' + ); + } + + if (IsNewer( + 'src/pl/plpgsql/src/pl_reserved_kwlist_d.h', + 'src/pl/plpgsql/src/pl_reserved_kwlist.h') + || IsNewer( + 'src/pl/plpgsql/src/pl_unreserved_kwlist_d.h', + 'src/pl/plpgsql/src/pl_unreserved_kwlist.h')) + { + print + "Generating pl_reserved_kwlist_d.h and pl_unreserved_kwlist_d.h...\n"; + chdir('src/pl/plpgsql/src'); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ReservedPLKeywords pl_reserved_kwlist.h' + ); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname UnreservedPLKeywords pl_unreserved_kwlist.h' + ); + chdir('../../../..'); + } + + if (IsNewer( + 'src/interfaces/ecpg/preproc/c_kwlist_d.h', + 'src/interfaces/ecpg/preproc/c_kwlist.h') + || IsNewer( + 'src/interfaces/ecpg/preproc/ecpg_kwlist_d.h', + 'src/interfaces/ecpg/preproc/ecpg_kwlist.h')) + { + print "Generating c_kwlist_d.h and ecpg_kwlist_d.h...\n"; + chdir('src/interfaces/ecpg/preproc'); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanCKeywords --no-case-fold c_kwlist.h' + ); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanECPGKeywords ecpg_kwlist.h' + ); + chdir('../../../..'); + } + + if (IsNewer( + 'src/interfaces/ecpg/preproc/preproc.y', + 'src/backend/parser/gram.y')) + { + print "Generating preproc.y...\n"; + chdir('src/interfaces/ecpg/preproc'); + system('perl parse.pl < ../../../backend/parser/gram.y > preproc.y'); + chdir('../../../..'); + } + + unless (-f "src/port/pg_config_paths.h") + { + print "Generating pg_config_paths.h...\n"; + open(my $o, '>', 'src/port/pg_config_paths.h') + || confess "Could not open pg_config_paths.h"; + print $o <<EOF; +#define PGBINDIR "/bin" +#define PGSHAREDIR "/share" +#define SYSCONFDIR "/etc" +#define INCLUDEDIR "/include" +#define PKGINCLUDEDIR "/include" +#define INCLUDEDIRSERVER "/include/server" +#define LIBDIR "/lib" +#define PKGLIBDIR "/lib" +#define LOCALEDIR "/share/locale" +#define DOCDIR "/doc" +#define HTMLDIR "/doc" +#define MANDIR "/man" +EOF + close($o); + } + + my $mf = Project::read_file('src/backend/catalog/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ /^CATALOG_HEADERS\s*:?=(.*)$/gm + || croak "Could not find CATALOG_HEADERS in Makefile\n"; + my @bki_srcs = split /\s+/, $1; + $mf =~ /^POSTGRES_BKI_DATA\s*:?=[^,]+,(.*)\)$/gm + || croak "Could not find POSTGRES_BKI_DATA in Makefile\n"; + my @bki_data = split /\s+/, $1; + + my $need_genbki = 0; + foreach my $bki (@bki_srcs, @bki_data) + { + next if $bki eq ""; + if (IsNewer( + 'src/backend/catalog/bki-stamp', + "src/include/catalog/$bki")) + { + $need_genbki = 1; + last; + } + } + $need_genbki = 1 + if IsNewer('src/backend/catalog/bki-stamp', + 'src/backend/catalog/genbki.pl'); + $need_genbki = 1 + if IsNewer('src/backend/catalog/bki-stamp', + 'src/backend/catalog/Catalog.pm'); + if ($need_genbki) + { + chdir('src/backend/catalog'); + my $bki_srcs = join(' ../../../src/include/catalog/', @bki_srcs); + system( + "perl genbki.pl --include-path ../../../src/include/ --set-version=$majorver $bki_srcs" + ); + open(my $f, '>', 'bki-stamp') + || confess "Could not touch bki-stamp"; + close($f); + chdir('../../..'); + } + + if (IsNewer( + 'src/include/catalog/header-stamp', + 'src/backend/catalog/bki-stamp')) + { + # Copy generated headers to include directory. + opendir(my $dh, 'src/backend/catalog/') + || die "Can't opendir src/backend/catalog/ $!"; + my @def_headers = grep { /pg_\w+_d\.h$/ } readdir($dh); + closedir $dh; + foreach my $def_header (@def_headers) + { + copyFile( + "src/backend/catalog/$def_header", + "src/include/catalog/$def_header"); + } + copyFile( + 'src/backend/catalog/schemapg.h', + 'src/include/catalog/schemapg.h'); + copyFile( + 'src/backend/catalog/system_fk_info.h', + 'src/include/catalog/system_fk_info.h'); + open(my $chs, '>', 'src/include/catalog/header-stamp') + || confess "Could not touch header-stamp"; + close($chs); + } + + open(my $o, '>', "doc/src/sgml/version.sgml") + || croak "Could not write to version.sgml\n"; + print $o <<EOF; +<!ENTITY version "$package_version"> +<!ENTITY majorversion "$majorver"> +EOF + close($o); + return; +} + +# Read lines from input file and substitute symbols using the same +# logic that config.status uses. There should be one call of this for +# each AC_CONFIG_HEADERS call in configure.ac. +# +# If the "required" argument is true, we also keep track which of our +# defines have been found and error out if any are left unused at the +# end. That way we avoid accumulating defines in this file that are +# no longer used by configure. +sub GenerateConfigHeader +{ + my ($self, $config_header, $defines, $required) = @_; + + my $config_header_in = $config_header . '.in'; + + if ( IsNewer($config_header, $config_header_in) + || IsNewer($config_header, __FILE__)) + { + my %defines_copy = %$defines; + + open(my $i, '<', $config_header_in) + || confess "Could not open $config_header_in\n"; + open(my $o, '>', $config_header) + || confess "Could not write to $config_header\n"; + + print $o + "/* $config_header. Generated from $config_header_in by src/tools/msvc/Solution.pm. */\n"; + + while (<$i>) + { + if (m/^#(\s*)undef\s+(\w+)/) + { + my $ws = $1; + my $macro = $2; + if (exists $defines->{$macro}) + { + if (defined $defines->{$macro}) + { + print $o "#${ws}define $macro ", $defines->{$macro}, + "\n"; + } + else + { + print $o "/* #${ws}undef $macro */\n"; + } + delete $defines_copy{$macro}; + } + else + { + croak + "undefined symbol: $macro at $config_header line $."; + } + } + else + { + print $o $_; + } + } + close($o); + close($i); + + if ($required && scalar(keys %defines_copy) > 0) + { + croak "unused defines: " . join(' ', keys %defines_copy); + } + } +} + +sub GenerateDefFile +{ + my ($self, $deffile, $txtfile, $libname) = @_; + + if (IsNewer($deffile, $txtfile)) + { + print "Generating $deffile...\n"; + open(my $if, '<', $txtfile) || confess("Could not open $txtfile\n"); + open(my $of, '>', $deffile) || confess("Could not open $deffile\n"); + print $of "LIBRARY $libname\nEXPORTS\n"; + while (<$if>) + { + next if (/^#/); + next if (/^\s*$/); + my ($f, $o) = split; + print $of " $f @ $o\n"; + } + close($of); + close($if); + } + return; +} + +sub AddProject +{ + my ($self, $name, $type, $folder, $initialdir) = @_; + + my $proj = + VSObjectFactory::CreateProject($self->{vcver}, $name, $type, $self); + push @{ $self->{projects}->{$folder} }, $proj; + $proj->AddDir($initialdir) if ($initialdir); + if ($self->{options}->{zlib}) + { + $proj->AddIncludeDir($self->{options}->{zlib} . '\include'); + $proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib'); + } + if ($self->{options}->{openssl}) + { + $proj->AddIncludeDir($self->{options}->{openssl} . '\include'); + my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion(); + + # Starting at version 1.1.0 the OpenSSL installers have + # changed their library names from: + # - libeay to libcrypto + # - ssleay to libssl + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0')) + { + my $dbgsuffix; + my $libsslpath; + my $libcryptopath; + + # The format name of the libraries is slightly + # different between the Win32 and Win64 platform, so + # adapt. + if (-e "$self->{options}->{openssl}/lib/VC/sslcrypto32MD.lib") + { + # Win32 here, with a debugging library set. + $dbgsuffix = 1; + $libsslpath = '\lib\VC\libssl32.lib'; + $libcryptopath = '\lib\VC\libcrypto32.lib'; + } + elsif (-e "$self->{options}->{openssl}/lib/VC/sslcrypto64MD.lib") + { + # Win64 here, with a debugging library set. + $dbgsuffix = 1; + $libsslpath = '\lib\VC\libssl64.lib'; + $libcryptopath = '\lib\VC\libcrypto64.lib'; + } + else + { + # On both Win32 and Win64 the same library + # names are used without a debugging context. + $dbgsuffix = 0; + $libsslpath = '\lib\libssl.lib'; + $libcryptopath = '\lib\libcrypto.lib'; + } + + $proj->AddLibrary($self->{options}->{openssl} . $libsslpath, + $dbgsuffix); + $proj->AddLibrary($self->{options}->{openssl} . $libcryptopath, + $dbgsuffix); + } + else + { + # Choose which set of libraries to use depending on if + # debugging libraries are in place in the installer. + if (-e "$self->{options}->{openssl}/lib/VC/ssleay32MD.lib") + { + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\VC\ssleay32.lib', 1); + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\VC\libeay32.lib', 1); + } + else + { + # We don't expect the config-specific library + # to be here, so don't ask for it in last + # parameter. + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\ssleay32.lib', 0); + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\libeay32.lib', 0); + } + } + } + if ($self->{options}->{nls}) + { + $proj->AddIncludeDir($self->{options}->{nls} . '\include'); + $proj->AddLibrary($self->{options}->{nls} . '\lib\libintl.lib'); + } + if ($self->{options}->{gss}) + { + $proj->AddIncludeDir($self->{options}->{gss} . '\include'); + $proj->AddIncludeDir($self->{options}->{gss} . '\include\krb5'); + if ($self->{platform} eq 'Win32') + { + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\i386\krb5_32.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\i386\comerr32.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\i386\gssapi32.lib'); + } + else + { + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\amd64\krb5_64.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\amd64\comerr64.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\amd64\gssapi64.lib'); + } + } + if ($self->{options}->{iconv}) + { + $proj->AddIncludeDir($self->{options}->{iconv} . '\include'); + $proj->AddLibrary($self->{options}->{iconv} . '\lib\iconv.lib'); + } + if ($self->{options}->{icu}) + { + $proj->AddIncludeDir($self->{options}->{icu} . '\include'); + if ($self->{platform} eq 'Win32') + { + $proj->AddLibrary($self->{options}->{icu} . '\lib\icuin.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib\icuuc.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib\icudt.lib'); + } + else + { + $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuin.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuuc.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib64\icudt.lib'); + } + } + if ($self->{options}->{xml}) + { + $proj->AddIncludeDir($self->{options}->{xml} . '\include'); + $proj->AddIncludeDir($self->{options}->{xml} . '\include\libxml2'); + $proj->AddLibrary($self->{options}->{xml} . '\lib\libxml2.lib'); + } + if ($self->{options}->{xslt}) + { + $proj->AddIncludeDir($self->{options}->{xslt} . '\include'); + $proj->AddLibrary($self->{options}->{xslt} . '\lib\libxslt.lib'); + } + if ($self->{options}->{lz4}) + { + $proj->AddIncludeDir($self->{options}->{lz4} . '\include'); + $proj->AddLibrary($self->{options}->{lz4} . '\lib\liblz4.lib'); + } + if ($self->{options}->{zstd}) + { + $proj->AddIncludeDir($self->{options}->{zstd} . '\include'); + $proj->AddLibrary($self->{options}->{zstd} . '\lib\libzstd.lib'); + } + if ($self->{options}->{uuid}) + { + $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); + $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); + } + return $proj; +} + +sub Save +{ + my ($self) = @_; + my %flduid; + + $self->GenerateFiles(); + foreach my $fld (keys %{ $self->{projects} }) + { + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + $proj->Save(); + } + } + + open(my $sln, '>', "pgsql.sln") || croak "Could not write to pgsql.sln\n"; + print $sln <<EOF; +Microsoft Visual Studio Solution File, Format Version $self->{solutionFileVersion} +# $self->{visualStudioName} +EOF + + print $sln $self->GetAdditionalHeaders(); + + foreach my $fld (keys %{ $self->{projects} }) + { + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + print $sln <<EOF; +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "$proj->{name}", "$proj->{name}$proj->{filenameExtension}", "$proj->{guid}" +EndProject +EOF + } + if ($fld ne "") + { + $flduid{$fld} = $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE'; + print $sln <<EOF; +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "$fld", "$fld", "$flduid{$fld}" +EndProject +EOF + } + } + + print $sln <<EOF; +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|$self->{platform}= Debug|$self->{platform} + Release|$self->{platform} = Release|$self->{platform} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution +EOF + + foreach my $fld (keys %{ $self->{projects} }) + { + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + print $sln <<EOF; + $proj->{guid}.Debug|$self->{platform}.ActiveCfg = Debug|$self->{platform} + $proj->{guid}.Debug|$self->{platform}.Build.0 = Debug|$self->{platform} + $proj->{guid}.Release|$self->{platform}.ActiveCfg = Release|$self->{platform} + $proj->{guid}.Release|$self->{platform}.Build.0 = Release|$self->{platform} +EOF + } + } + + print $sln <<EOF; + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution +EOF + + foreach my $fld (keys %{ $self->{projects} }) + { + next if ($fld eq ""); + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + print $sln "\t\t$proj->{guid} = $flduid{$fld}\n"; + } + } + + print $sln <<EOF; + EndGlobalSection +EndGlobal +EOF + close($sln); + return; +} + +sub GetFakeConfigure +{ + my $self = shift; + + my $cfg = '--enable-thread-safety'; + $cfg .= ' --enable-cassert' if ($self->{options}->{asserts}); + $cfg .= ' --enable-nls' if ($self->{options}->{nls}); + $cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests}); + $cfg .= ' --with-ldap' if ($self->{options}->{ldap}); + $cfg .= ' --without-zlib' unless ($self->{options}->{zlib}); + $cfg .= ' --with-extra-version' if ($self->{options}->{extraver}); + $cfg .= ' --with-ssl=openssl' if ($self->{options}->{openssl}); + $cfg .= ' --with-uuid' if ($self->{options}->{uuid}); + $cfg .= ' --with-libxml' if ($self->{options}->{xml}); + $cfg .= ' --with-libxslt' if ($self->{options}->{xslt}); + $cfg .= ' --with-lz4' if ($self->{options}->{lz4}); + $cfg .= ' --with-zstd' if ($self->{options}->{zstd}); + $cfg .= ' --with-gssapi' if ($self->{options}->{gss}); + $cfg .= ' --with-icu' if ($self->{options}->{icu}); + $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); + $cfg .= ' --with-perl' if ($self->{options}->{perl}); + $cfg .= ' --with-python' if ($self->{options}->{python}); + my $port = $self->{options}->{'--with-pgport'}; + $cfg .= " --with-pgport=$port" if defined($port); + + return $cfg; +} + +package VS2013Solution; + +# +# Package that encapsulates a Visual Studio 2013 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '12.00'; + $self->{visualStudioName} = 'Visual Studio 2013'; + $self->{VisualStudioVersion} = '12.0.21005.1'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2015Solution; + +# +# Package that encapsulates a Visual Studio 2015 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '14.00'; + $self->{visualStudioName} = 'Visual Studio 2015'; + $self->{VisualStudioVersion} = '14.0.24730.2'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2017Solution; + +# +# Package that encapsulates a Visual Studio 2017 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '15.00'; + $self->{visualStudioName} = 'Visual Studio 2017'; + $self->{VisualStudioVersion} = '15.0.26730.3'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2019Solution; + +# +# Package that encapsulates a Visual Studio 2019 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '16.00'; + $self->{visualStudioName} = 'Visual Studio 2019'; + $self->{VisualStudioVersion} = '16.0.28729.10'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2022Solution; + +# +# Package that encapsulates a Visual Studio 2022 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '17.00'; + $self->{visualStudioName} = 'Visual Studio 2022'; + $self->{VisualStudioVersion} = '17.0.31903.59'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +sub GetAdditionalHeaders +{ + my ($self, $f) = @_; + + return qq|VisualStudioVersion = $self->{VisualStudioVersion} +MinimumVisualStudioVersion = $self->{MinimumVisualStudioVersion} +|; +} + +1; diff --git a/src/tools/msvc/VSObjectFactory.pm b/src/tools/msvc/VSObjectFactory.pm new file mode 100644 index 0000000..9f9712b --- /dev/null +++ b/src/tools/msvc/VSObjectFactory.pm @@ -0,0 +1,182 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package VSObjectFactory; + +# +# Package that creates Visual Studio wrapper objects for msvc build +# +# src/tools/msvc/VSObjectFactory.pm +# + +use Carp; +use strict; +use warnings; + +use Exporter; +use Project; +use Solution; +use MSBuildProject; + +our (@ISA, @EXPORT); +@ISA = qw(Exporter); +@EXPORT = qw(CreateSolution CreateProject DetermineVisualStudioVersion); + +no warnings qw(redefine); ## no critic + +sub CreateSolution +{ + my $visualStudioVersion = shift; + + if (!defined($visualStudioVersion)) + { + $visualStudioVersion = DetermineVisualStudioVersion(); + } + + if ($visualStudioVersion eq '12.00') + { + return new VS2013Solution(@_); + } + elsif ($visualStudioVersion eq '14.00') + { + return new VS2015Solution(@_); + } + + # The version of nmake bundled in Visual Studio 2017 is greater + # than 14.10 and less than 14.20. And the version number is + # actually 15.00. + elsif ( + ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20') + || $visualStudioVersion eq '15.00') + { + return new VS2017Solution(@_); + } + + # The version of nmake bundled in Visual Studio 2019 is greater + # than 14.20 and less than 14.30. And the version number is + # actually 16.00. + elsif ( + ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30') + || $visualStudioVersion eq '16.00') + { + return new VS2019Solution(@_); + } + + # The version of nmake bundled in Visual Studio 2022 is greater + # than 14.30 and less than 14.40. And the version number is + # actually 17.00. + elsif ( + ($visualStudioVersion ge '14.30' && $visualStudioVersion lt '14.40') + || $visualStudioVersion eq '17.00') + { + return new VS2022Solution(@_); + } + else + { + croak + "The requested Visual Studio version $visualStudioVersion is not supported."; + } +} + +sub CreateProject +{ + my $visualStudioVersion = shift; + + if (!defined($visualStudioVersion)) + { + $visualStudioVersion = DetermineVisualStudioVersion(); + } + + if ($visualStudioVersion eq '12.00') + { + return new VC2013Project(@_); + } + elsif ($visualStudioVersion eq '14.00') + { + return new VC2015Project(@_); + } + + # The version of nmake bundled in Visual Studio 2017 is greater + # than 14.10 and less than 14.20. And the version number is + # actually 15.00. + elsif ( + ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20') + || $visualStudioVersion eq '15.00') + { + return new VC2017Project(@_); + } + + # The version of nmake bundled in Visual Studio 2019 is greater + # than 14.20 and less than 14.30. And the version number is + # actually 16.00. + elsif ( + ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30') + || $visualStudioVersion eq '16.00') + { + return new VC2019Project(@_); + } + + # The version of nmake bundled in Visual Studio 2022 is greater + # than 14.30 and less than 14.40. And the version number is + # actually 17.00. + elsif ( + ($visualStudioVersion ge '14.30' && $visualStudioVersion lt '14.40') + || $visualStudioVersion eq '17.00') + { + return new VC2022Project(@_); + } + else + { + croak + "The requested Visual Studio version $visualStudioVersion is not supported."; + } +} + +sub DetermineVisualStudioVersion +{ + if ($^O eq "MSWin32") + { + # To determine version of Visual Studio we use nmake as it has + # existed for a long time and still exists in current Visual + # Studio versions. + my $output = `nmake /? 2>&1`; + $? >> 8 == 0 + or croak + "Unable to determine Visual Studio version: The nmake command wasn't found."; + if ($output =~ /(\d+)\.(\d+)\.\d+(\.\d+)?/) + { + return _GetVisualStudioVersion($1, $2); + } + + croak + "Unable to determine Visual Studio version: The nmake version could not be determined."; + } + else + { + # fake version + return '17.00'; + } +} + +sub _GetVisualStudioVersion +{ + my ($major, $minor) = @_; + + # The major visual studio that is supported has nmake + # version <= 14.40, so stick with it as the latest version + # if bumping on something even newer. + if ($major >= 14 && $minor >= 40) + { + carp + "The determined version of Visual Studio is newer than the latest supported version. Returning the latest supported version instead."; + return '14.30'; + } + elsif ($major < 12) + { + croak + "Unable to determine Visual Studio version: Visual Studio versions before 12.0 aren't supported."; + } + return "$major.$minor"; +} + +1; diff --git a/src/tools/msvc/build.bat b/src/tools/msvc/build.bat new file mode 100755 index 0000000..171f749 --- /dev/null +++ b/src/tools/msvc/build.bat @@ -0,0 +1,6 @@ +@echo off +REM src/tools/msvc/build.bat +REM all the logic for this now belongs in build.pl. This file really +REM only exists so you don't have to type "perl build.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/build.pl %* diff --git a/src/tools/msvc/build.pl b/src/tools/msvc/build.pl new file mode 100644 index 0000000..e5ce14d --- /dev/null +++ b/src/tools/msvc/build.pl @@ -0,0 +1,92 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# +# Script that provides 'make' functionality for msvc builds. +# +# src/tools/msvc/build.pl +# +use strict; +use warnings; + +use FindBin; +use lib $FindBin::RealBin; + +use Cwd; + +use Mkvcbuild; + +sub usage +{ + die( "Usage: build.pl [ [ <configuration> ] <component> ]\n" + . "Options are case-insensitive.\n" + . " configuration: Release | Debug. This sets the configuration\n" + . " to build. Default is Release.\n" + . " component: name of component to build. An empty value means\n" + . " to build all components.\n"); +} + +chdir('../../..') if (-d '../msvc' && -d '../../../src'); +die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + +usage() unless scalar(@ARGV) <= 2; + +# buildenv.pl is for specifying the build environment settings +# it should contain lines like: +# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}"; + +if (-e "src/tools/msvc/buildenv.pl") +{ + do "./src/tools/msvc/buildenv.pl"; +} +elsif (-e "./buildenv.pl") +{ + do "./buildenv.pl"; +} + +# set up the project +our $config; +do "./src/tools/msvc/config_default.pl"; +do "./src/tools/msvc/config.pl" if (-f "src/tools/msvc/config.pl"); + +my $vcver = Mkvcbuild::mkvcbuild($config); + +# check what sort of build we are doing +my $bconf = $ENV{CONFIG} || "Release"; +my $msbflags = $ENV{MSBFLAGS} || ""; +my $buildwhat = $ARGV[1] || ""; + +if (defined($ARGV[0])) +{ + if (uc($ARGV[0]) eq 'DEBUG') + { + $bconf = "Debug"; + } + elsif (uc($ARGV[0]) ne "RELEASE") + { + $buildwhat = $ARGV[0] || ""; + } +} + +# ... and do it + +if ($buildwhat) +{ + system( + "msbuild $buildwhat.vcxproj /verbosity:normal $msbflags /p:Configuration=$bconf" + ); +} +else +{ + system( + "msbuild pgsql.sln /verbosity:normal $msbflags /p:Configuration=$bconf" + ); +} + +# report status + +my $status = $? >> 8; + +exit $status; diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat new file mode 100755 index 0000000..42b3d1b --- /dev/null +++ b/src/tools/msvc/clean.bat @@ -0,0 +1,142 @@ +@echo off +REM src/tools/msvc/clean.bat + +set DIST=0 +if "%1"=="dist" set DIST=1 + +setlocal + +cd "%~dp0\..\..\.." + +if exist debug rd /s /q debug +if exist release rd /s /q release +for %%f in (*.vcproj) do del %%f +for %%f in (*.vcxproj) do del %%f +for %%f in (*.vcxproj.user) do del %%f +if exist pgsql.sln del /q pgsql.sln +if exist pgsql.sln.cache del /q pgsql.sln.cache +if exist pgsql.sdf del /q pgsql.sdf +if exist pgsql.suo del /q /a:H pgsql.suo +del /s /q src\bin\win32ver.rc 2> NUL +del /s /q src\interfaces\win32ver.rc 2> NUL +if exist src\backend\win32ver.rc del /q src\backend\win32ver.rc +if exist src\backend\replication\libpqwalreceiver\win32ver.rc del /q src\backend\replication\libpqwalreceiver\win32ver.rc +if exist src\backend\replication\pgoutput\win32ver.rc del /q src\backend\replication\pgoutput\win32ver.rc +if exist src\backend\snowball\win32ver.rc del /q src\backend\snowball\win32ver.rc +if exist src\interfaces\ecpg\test\win32ver.rc del /q src\interfaces\ecpg\test\win32ver.rc +if exist src\pl\plperl\win32ver.rc del /q src\pl\plperl\win32ver.rc +if exist src\pl\plpgsql\src\win32ver.rc del /q src\pl\plpgsql\src\win32ver.rc +if exist src\pl\plpython\win32ver.rc del /q src\pl\plpython\win32ver.rc +if exist src\pl\tcl\win32ver.rc del /q src\pl\tcl\win32ver.rc +if exist src\test\isolation\win32ver.rc del /q src\test\isolation\win32ver.rc +if exist src\test\regress\win32ver.rc del /q src\test\regress\win32ver.rc +if exist src\timezone\win32ver.rc del /q src\timezone\win32ver.rc + +for /d %%f in (src\interfaces\ecpg\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc +for /d %%f in (contrib\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc +for /d %%f in (src\backend\utils\mb\conversion_procs\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc +for /d %%f in (src\test\modules\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc + +REM Delete files created with GenerateFiles() in Solution.pm +if exist src\include\pg_config.h del /q src\include\pg_config.h +if exist src\include\pg_config_ext.h del /q src\include\pg_config_ext.h +if exist src\include\pg_config_os.h del /q src\include\pg_config_os.h +if %DIST%==1 if exist src\backend\parser\gram.h del /q src\backend\parser\gram.h +if exist src\include\utils\errcodes.h del /q src\include\utils\errcodes.h +if exist src\include\utils\fmgroids.h del /q src\include\utils\fmgroids.h +if exist src\include\utils\fmgrprotos.h del /q src\include\utils\fmgrprotos.h +if exist src\include\storage\lwlocknames.h del /q src\include\storage\lwlocknames.h +if exist src\include\utils\probes.h del /q src\include\utils\probes.h +if exist src\include\catalog\schemapg.h del /q src\include\catalog\schemapg.h +if exist src\include\catalog\system_fk_info.h del /q src\include\catalog\system_fk_info.h +if exist src\include\catalog\pg_*_d.h del /q src\include\catalog\pg_*_d.h +if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stamp +if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml + +if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h +if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h +if %DIST%==1 if exist src\backend\utils\fmgrtab.c del /q src\backend\utils\fmgrtab.c +if %DIST%==1 if exist src\backend\utils\fmgr-stamp del /q src\backend\utils\fmgr-stamp +if %DIST%==1 if exist src\backend\utils\errcodes.h del /q src\backend\utils\errcodes.h +if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.c del /q src\backend\storage\lmgr\lwlocknames.c +if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.h del /q src\backend\storage\lmgr\lwlocknames.h +if %DIST%==1 if exist src\pl\plpython\spiexceptions.h del /q src\pl\plpython\spiexceptions.h +if %DIST%==1 if exist src\pl\plpgsql\src\plerrcodes.h del /q src\pl\plpgsql\src\plerrcodes.h +if %DIST%==1 if exist src\pl\tcl\pltclerrcodes.h del /q src\pl\tcl\pltclerrcodes.h +if %DIST%==1 if exist src\bin\psql\sql_help.c del /q src\bin\psql\sql_help.c +if %DIST%==1 if exist src\bin\psql\sql_help.h del /q src\bin\psql\sql_help.h +if %DIST%==1 if exist src\common\kwlist_d.h del /q src\common\kwlist_d.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_reserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_reserved_kwlist_d.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_unreserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_unreserved_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\c_kwlist_d.h del /q src\interfaces\ecpg\preproc\c_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\ecpg_kwlist_d.h del /q src\interfaces\ecpg\preproc\ecpg_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.y del /q src\interfaces\ecpg\preproc\preproc.y +if %DIST%==1 if exist src\backend\catalog\postgres.bki del /q src\backend\catalog\postgres.bki +if %DIST%==1 if exist src\backend\catalog\system_constraints.sql del /q src\backend\catalog\system_constraints.sql +if %DIST%==1 if exist src\backend\catalog\schemapg.h del /q src\backend\catalog\schemapg.h +if %DIST%==1 if exist src\backend\catalog\system_fk_info.h del /q src\backend\catalog\system_fk_info.h +if %DIST%==1 if exist src\backend\catalog\pg_*_d.h del /q src\backend\catalog\pg_*_d.h +if %DIST%==1 if exist src\backend\catalog\bki-stamp del /q src\backend\catalog\bki-stamp +if %DIST%==1 if exist src\backend\parser\scan.c del /q src\backend\parser\scan.c +if %DIST%==1 if exist src\backend\parser\gram.c del /q src\backend\parser\gram.c +if %DIST%==1 if exist src\backend\bootstrap\bootscanner.c del /q src\backend\bootstrap\bootscanner.c +if %DIST%==1 if exist src\backend\bootstrap\bootparse.c del /q src\backend\bootstrap\bootparse.c +if %DIST%==1 if exist src\backend\utils\adt\jsonpath_gram.c del /q src\backend\utils\adt\jsonpath_gram.c +if %DIST%==1 if exist src\backend\utils\adt\jsonpath_scan.c del /q src\backend\utils\adt\jsonpath_scan.c +if %DIST%==1 if exist src\backend\utils\misc\guc-file.c del /q src\backend\utils\misc\guc-file.c +if %DIST%==1 if exist src\backend\replication\repl_scanner.c del /q src\backend\replication\repl_scanner.c +if %DIST%==1 if exist src\backend\replication\repl_gram.c del /q src\backend\replication\repl_gram.c +if %DIST%==1 if exist src\backend\replication\syncrep_scanner.c del /q src\backend\replication\syncrep_scanner.c +if %DIST%==1 if exist src\backend\replication\syncrep_gram.c del /q src\backend\replication\syncrep_gram.c + + +if exist src\interfaces\libpq\libpqdll.def del /q src\interfaces\libpq\libpqdll.def +if exist src\interfaces\ecpg\compatlib\compatlib.def del /q src\interfaces\ecpg\compatlib\compatlib.def +if exist src\interfaces\ecpg\ecpglib\ecpglib.def del /q src\interfaces\ecpg\ecpglib\ecpglib.def +if exist src\interfaces\ecpg\include\ecpg_config.h del /q src\interfaces\ecpg\include\ecpg_config.h +if exist src\interfaces\ecpg\pgtypeslib\pgtypeslib.def del /q src\interfaces\ecpg\pgtypeslib\pgtypeslib.def +if %DIST%==1 if exist src\interfaces\ecpg\preproc\pgc.c del /q src\interfaces\ecpg\preproc\pgc.c +if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.c del /q src\interfaces\ecpg\preproc\preproc.c +if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.h del /q src\interfaces\ecpg\preproc\preproc.h + +if exist src\port\pg_config_paths.h del /q src\port\pg_config_paths.h + +if exist src\pl\plperl\SPI.c del /q src\pl\plperl\SPI.c +if exist src\pl\plperl\Util.c del /q src\pl\plperl\Util.c +if exist src\pl\plperl\perlchunks.h del /q src\pl\plperl\perlchunks.h +if exist src\pl\plperl\plperl_opmask.h del /q src\pl\plperl\plperl_opmask.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.c del /q src\pl\plpgsql\src\pl_gram.c +if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.h del /q src\pl\plpgsql\src\pl_gram.h + +if %DIST%==1 if exist src\fe_utils\psqlscan.c del /q src\fe_utils\psqlscan.c +if %DIST%==1 if exist src\bin\psql\psqlscanslash.c del /q src\bin\psql\psqlscanslash.c +if %DIST%==1 if exist src\bin\pgbench\exprscan.c del /q src\bin\pgbench\exprscan.c +if %DIST%==1 if exist src\bin\pgbench\exprparse.c del /q src\bin\pgbench\exprparse.c + +if %DIST%==1 if exist contrib\cube\cubescan.c del /q contrib\cube\cubescan.c +if %DIST%==1 if exist contrib\cube\cubeparse.c del /q contrib\cube\cubeparse.c +if %DIST%==1 if exist contrib\seg\segscan.c del /q contrib\seg\segscan.c +if %DIST%==1 if exist contrib\seg\segparse.c del /q contrib\seg\segparse.c + +if exist src\test\regress\tmp_check rd /s /q src\test\regress\tmp_check +if exist contrib\spi\refint.dll del /q contrib\spi\refint.dll +if exist contrib\spi\autoinc.dll del /q contrib\spi\autoinc.dll +if exist src\test\regress\regress.dll del /q src\test\regress\regress.dll +if exist src\test\regress\refint.dll del /q src\test\regress\refint.dll +if exist src\test\regress\autoinc.dll del /q src\test\regress\autoinc.dll +if %DIST%==1 if exist src\test\isolation\specscanner.c del /q src\test\isolation\specscanner.c +if %DIST%==1 if exist src\test\isolation\specparse.c del /q src\test\isolation\specparse.c + +for /d %%f in (contrib\* src\bin\* src\test\* src\test\modules\* + ) do if exist %%f\tmp_check rd /s /q %%f\tmp_check + +REM Clean up datafiles built with contrib +REM cd contrib +REM for /r %%f in (*.sql) do if exist %%f.in del %%f + +cd "%~dp0" + +REM Clean up ecpg regression test files +msbuild ecpg_regression.proj /NoLogo /v:q %MSBFLAGS% /t:clean + +goto :eof diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl new file mode 100644 index 0000000..186849a --- /dev/null +++ b/src/tools/msvc/config_default.pl @@ -0,0 +1,32 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Configuration arguments for vcbuild. +use strict; +use warnings; + +our $config = { + asserts => 0, # --enable-cassert + + # blocksize => 8, # --with-blocksize, 8kB by default + # wal_blocksize => 8, # --with-wal-blocksize, 8kB by default + ldap => 1, # --with-ldap + extraver => undef, # --with-extra-version=<string> + gss => undef, # --with-gssapi=<path> + icu => undef, # --with-icu=<path> + lz4 => undef, # --with-lz4=<path> + zstd => undef, # --with-zstd=<path> + nls => undef, # --enable-nls=<path> + tap_tests => undef, # --enable-tap-tests + tcl => undef, # --with-tcl=<path> + perl => undef, # --with-perl=<path> + python => undef, # --with-python=<path> + openssl => undef, # --with-ssl=openssl with <path> + uuid => undef, # --with-uuid=<path> + xml => undef, # --with-libxml=<path> + xslt => undef, # --with-libxslt=<path> + iconv => undef, # (not in configure, path to iconv) + zlib => undef # --with-zlib=<path> +}; + +1; diff --git a/src/tools/msvc/dummylib/README b/src/tools/msvc/dummylib/README new file mode 100644 index 0000000..7b63d0e --- /dev/null +++ b/src/tools/msvc/dummylib/README @@ -0,0 +1,13 @@ + +src/tools/msvc/dummylib + +This directory contains just enough of a dummy library to allow checking of +the programs in src/tools/msvc and src/tools/win32tzlist.pl with +perl -cw, even on machines that lack the Win32 perl infrastructure. + +invoke via: + +PERL5LIB=src/tools/msvc/dummylib perl -cw $file + +This is the only use that should be made of this directory. Attempting actually +running of any programs using this library will result in a lot of grief. diff --git a/src/tools/msvc/dummylib/Win32.pm b/src/tools/msvc/dummylib/Win32.pm new file mode 100644 index 0000000..1c7e3d3 --- /dev/null +++ b/src/tools/msvc/dummylib/Win32.pm @@ -0,0 +1,7 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Win32; +use strict; +use warnings; +1; diff --git a/src/tools/msvc/dummylib/Win32/Registry.pm b/src/tools/msvc/dummylib/Win32/Registry.pm new file mode 100644 index 0000000..d995432 --- /dev/null +++ b/src/tools/msvc/dummylib/Win32/Registry.pm @@ -0,0 +1,16 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Win32::Registry; + +use strict; +use warnings; + +use vars qw($HKEY_LOCAL_MACHINE); + +use Exporter (); +our (@EXPORT, @ISA); +@ISA = qw(Exporter); +@EXPORT = qw($HKEY_LOCAL_MACHINE); + +1; diff --git a/src/tools/msvc/dummylib/Win32API/File.pm b/src/tools/msvc/dummylib/Win32API/File.pm new file mode 100644 index 0000000..d77e48b --- /dev/null +++ b/src/tools/msvc/dummylib/Win32API/File.pm @@ -0,0 +1,17 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +package Win32API::File; + +use strict; +use warnings; + +use constant { SEM_FAILCRITICALERRORS => 1, SEM_NOGPFAULTERRORBOX => 2 }; +sub SetErrormode { } +use Exporter; +our (@ISA, @EXPORT_OK, %EXPORT_TAGS); +@ISA = qw(Exporter); +@EXPORT_OK = qw(SetErrorMode SEM_FAILCRITICALERRORS SEM_NOGPFAULTERRORBOX); +%EXPORT_TAGS = (SEM_ => [qw(SEM_FAILCRITICALERRORS SEM_NOGPFAULTERRORBOX)]); + +1; diff --git a/src/tools/msvc/ecpg_regression.proj b/src/tools/msvc/ecpg_regression.proj new file mode 100644 index 0000000..ec2760b --- /dev/null +++ b/src/tools/msvc/ecpg_regression.proj @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="all"> +<!-- + MSBuild project file to build ecpg regression tests +--> + + <PropertyGroup> + <TESTDIR>..\..\interfaces\ecpg\test</TESTDIR> + <CONFIG>Debug</CONFIG> + <OUTDIR>..\..\..\..\..\$(CONFIG)\</OUTDIR> + </PropertyGroup> + <PropertyGroup Condition="'$(CONFIG)'=='DEBUG'"> + <!-- set debug runtime library if necessary to be compatible with the LIB files generated --> + <DEBUGLIB>d</DEBUGLIB> + </PropertyGroup> + + <ItemGroup> + <Pgc Include="$(TESTDIR)\**\*.pgc" Exclude="$(TESTDIR)\performance\perftest.pgc"/> + </ItemGroup> + + <ItemGroup> + <OutputToDelete Include="$(TESTDIR)\**\*.exe" /> + <OutputToDelete Include="$(TESTDIR)\**\*.exe.manifest" /> + <OutputToDelete Include="$(TESTDIR)\**\*.obj" /> + <OutputToDelete Include="$(TESTDIR)\**\*.c" Exclude="$(TESTDIR)\pg_regress_ecpg.c;$(TESTDIR)\expected\*.c" /> + </ItemGroup> + + <Target Name="all" Inputs="@(Pgc)" Outputs="%(RelativeDir)%(Filename).exe"> + <!-- Set special parameters for some tests --> + <CreateProperty Value="-C INFORMIX" Condition="'%(Pgc.RelativeDir)'=='$(TESTDIR)\compat_informix\'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-C INFORMIX -r no_indicator" Condition="'%(Pgc.FileName)'=='rnull'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-C ORACLE" Condition="'%(Pgc.RelativeDir)'=='$(TESTDIR)\compat_oracle\'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-c" Condition="'%(Pgc.FileName)'=='array_of_struct'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-c" Condition="'%(Pgc.FileName)'=='pointer_to_struct'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-r questionmarks" Condition="'%(Pgc.FileName)'=='oldexec'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-r prepare" Condition="'%(Pgc.FileName)'=='autoprep'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-i" Condition="'%(Pgc.FileName)'=='strings'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + + <!-- Run ECPG and the Visual C++ compiler on the files. Don't bother with dependency check between the steps --> + <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="$(OUTDIR)ecpg\ecpg -I ../../include --regression $(ECPGPARAM) -o %(Pgc.Filename).c %(Pgc.Filename).pgc" /> + <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="cl /nologo %(Pgc.FileName).c /TC /MD$(DEBUGLIB) /DENABLE_THREAD_SAFETY /DWIN32 /I. /I..\..\include /I..\..\..\libpq /I..\..\..\..\include /link /defaultlib:$(OUTDIR)libecpg\libecpg.lib /defaultlib:$(OUTDIR)libecpg_compat\libecpg_compat.lib /defaultlib:$(OUTDIR)libpgtypes\libpgtypes.lib" /> + </Target> + + <!-- Clean up all output files --> + <Target Name="clean"> + <Delete Files="@(OutputToDelete)" /> + </Target> +</Project> diff --git a/src/tools/msvc/gendef.pl b/src/tools/msvc/gendef.pl new file mode 100644 index 0000000..b8c514a --- /dev/null +++ b/src/tools/msvc/gendef.pl @@ -0,0 +1,181 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +use strict; +use warnings; +use 5.8.0; +use List::Util qw(max); + +my @def; + +# +# Script that generates a .DEF file for all objects in a directory +# +# src/tools/msvc/gendef.pl +# + +# Given a symbol file path, loops over its contents +# and returns a list of symbols of interest as a dictionary +# of 'symbolname' -> symtype, where symtype is: +# +# 0 a CODE symbol, left undecorated in the .DEF +# 1 A DATA symbol, i.e. global var export +# +sub extract_syms +{ + my ($symfile, $def) = @_; + open(my $f, '<', $symfile) || die "Could not open $symfile for $_: $!\n"; + while (<$f>) + { + + # Expected symbol lines look like: + # + # 0 1 2 3 4 5 6 + # IDX SYMBOL SECT SYMTYPE SYMSTATIC SYMNAME + # ------------------------------------------------------------------------ + # 02E 00000130 SECTA notype External | _standbyState + # 02F 00000009 SECT9 notype Static | _LocalRecoveryInProgress + # 064 00000020 SECTC notype () Static | _XLogCheckBuffer + # 065 00000000 UNDEF notype () External | _BufferGetTag + # + # See http://msdn.microsoft.com/en-us/library/b842y285.aspx + # + # We're not interested in the symbol index or offset. + # + # SECT[ION] is only examined to see whether the symbol is defined in a + # COFF section of the local object file; if UNDEF, it's a symbol to be + # resolved at link time from another object so we can't export it. + # + # SYMTYPE is always notype for C symbols as there's no typeinfo and no + # way to get the symbol type from name (de)mangling. However, we care + # if "notype" is suffixed by "()" or not. The presence of () means the + # symbol is a function, the absence means it isn't. + # + # SYMSTATIC indicates whether it's a compilation-unit local "static" + # symbol ("Static"), or whether it's available for use from other + # compilation units ("External"). We export all symbols that aren't + # static as part of the whole program DLL interface to produce UNIX-like + # default linkage. + # + # SYMNAME is, obviously, the symbol name. The leading underscore + # indicates that the _cdecl calling convention is used. See + # http://www.unixwiz.net/techtips/win32-callconv.html + # http://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified + # + s/notype \(\)/func/g; + s/notype/data/g; + + my @pieces = split; + + # Skip file and section headers and other non-symbol entries + next unless defined($pieces[0]) and $pieces[0] =~ /^[A-F0-9]{3,}$/; + + # Skip blank symbol names + next unless $pieces[6]; + + # Skip externs used from another compilation unit + next if ($pieces[2] eq "UNDEF"); + + # Skip static symbols + next unless ($pieces[4] eq "External"); + + # Skip some more MSVC-generated crud + next if $pieces[6] =~ /^@/; + next if $pieces[6] =~ /^\(/; + + # __real and __xmm are out-of-line floating point literals and + # (for __xmm) their SIMD equivalents. They shouldn't be part + # of the DLL interface. + next if $pieces[6] =~ /^__real/; + next if $pieces[6] =~ /^__xmm/; + + # __imp entries are imports from other DLLs, eg __imp__malloc . + # (We should never have one of these that hasn't already been skipped + # by the UNDEF test above, though). + next if $pieces[6] =~ /^__imp/; + + # More under-documented internal crud + next if $pieces[6] =~ /NULL_THUNK_DATA$/; + next if $pieces[6] =~ /^__IMPORT_DESCRIPTOR/; + next if $pieces[6] =~ /^__NULL_IMPORT/; + + # Skip string literals + next if $pieces[6] =~ /^\?\?_C/; + + # We assume that if a symbol is defined as data, then as a function, + # the linker will reject the binary anyway. So it's OK to just pick + # whatever came last. + $def->{ $pieces[6] } = $pieces[3]; + } + close($f); + return; +} + +sub writedef +{ + my ($deffile, $platform, $def) = @_; + open(my $fh, '>', $deffile) || die "Could not write to $deffile\n"; + print $fh "EXPORTS\n"; + foreach my $f (sort keys %{$def}) + { + my $isdata = $def->{$f} eq 'data'; + + # Strip the leading underscore for win32, but not x64 + $f =~ s/^_// + unless ($platform eq "x64"); + + # Emit just the name if it's a function symbol, or emit the name + # decorated with the DATA option for variables. + if ($isdata) + { + print $fh " $f DATA\n"; + } + else + { + print $fh " $f\n"; + } + } + close($fh); + return; +} + + +sub usage +{ + die( "Usage: gendef.pl <modulepath> <platform>\n" + . " modulepath: path to dir with obj files, no trailing slash" + . " platform: Win32 | x64"); +} + +usage() + unless scalar(@ARGV) == 2 + && ( ($ARGV[0] =~ /\\([^\\]+$)/) + && ($ARGV[1] eq 'Win32' || $ARGV[1] eq 'x64')); +my $defname = uc $1; +my $deffile = "$ARGV[0]/$defname.def"; +my $platform = $ARGV[1]; + +# if the def file exists and is newer than all input object files, skip +# its creation +if (-f $deffile + && (-M $deffile > max(map { -M } <$ARGV[0]/*.obj>))) +{ + print "Not re-generating $defname.DEF, file already exists.\n"; + exit(0); +} + +print "Generating $defname.DEF from directory $ARGV[0], platform $platform\n"; + +my %def = (); + +my $symfile = "$ARGV[0]/all.sym"; +my $tmpfile = "$ARGV[0]/tmp.sym"; +system("dumpbin /symbols /out:$tmpfile $ARGV[0]/*.obj >NUL") + && die "Could not call dumpbin"; +rename($tmpfile, $symfile); +extract_syms($symfile, \%def); +print "\n"; + +writedef($deffile, $platform, \%def); + +print "Generated " . scalar(keys(%def)) . " symbols\n"; diff --git a/src/tools/msvc/install.bat b/src/tools/msvc/install.bat new file mode 100644 index 0000000..d02b808 --- /dev/null +++ b/src/tools/msvc/install.bat @@ -0,0 +1,6 @@ +@echo off +REM src/tools/msvc/install.bat +REM all the logic for this now belongs in install.pl. This file really +REM only exists so you don't have to type "perl install.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/install.pl %* diff --git a/src/tools/msvc/install.pl b/src/tools/msvc/install.pl new file mode 100755 index 0000000..142d0dd --- /dev/null +++ b/src/tools/msvc/install.pl @@ -0,0 +1,39 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# +# Script that provides 'make install' functionality for msvc builds +# +# src/tools/msvc/install.pl +# +use strict; +use warnings; + +use FindBin; +use lib $FindBin::RealBin; + +use Install qw(Install); + +# buildenv.pl is for specifying the build environment settings +# it should contain lines like: +# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}"; + +if (-e "src/tools/msvc/buildenv.pl") +{ + do "./src/tools/msvc/buildenv.pl"; +} +elsif (-e "./buildenv.pl") +{ + do "./buildenv.pl"; +} + +my $target = shift || Usage(); +my $insttype = shift; +Install($target, $insttype); + +sub Usage +{ + print "Usage: install.pl <targetdir> [installtype]\n"; + print "installtype: client\n"; + exit(1); +} diff --git a/src/tools/msvc/mkvcbuild.pl b/src/tools/msvc/mkvcbuild.pl new file mode 100644 index 0000000..7c9f7ce --- /dev/null +++ b/src/tools/msvc/mkvcbuild.pl @@ -0,0 +1,31 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# +# Script that parses Unix style build environment and generates build files +# for building with Visual Studio. +# +# src/tools/msvc/mkvcbuild.pl +# +use strict; +use warnings; + +use FindBin; +use lib $FindBin::RealBin; + +use Mkvcbuild; + +chdir('../../..') if (-d '../msvc' && -d '../../../src'); +die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + +die 'Could not find config_default.pl' + unless (-f 'src/tools/msvc/config_default.pl'); +print "Warning: no config.pl found, using default.\n" + unless (-f 'src/tools/msvc/config.pl'); + +our $config; +do './src/tools/msvc/config_default.pl'; +do './src/tools/msvc/config.pl' if (-f 'src/tools/msvc/config.pl'); + +Mkvcbuild::mkvcbuild($config); diff --git a/src/tools/msvc/pgbison.bat b/src/tools/msvc/pgbison.bat new file mode 100755 index 0000000..dc8ac4a --- /dev/null +++ b/src/tools/msvc/pgbison.bat @@ -0,0 +1,7 @@ +@echo off + +REM src/tools/msvc/pgbison.bat +REM all the logic for this now belongs in pgbison.pl. This file really +REM only exists so you don't have to type "perl src/tools/msvc/pgbison.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/pgbison.pl %* diff --git a/src/tools/msvc/pgbison.pl b/src/tools/msvc/pgbison.pl new file mode 100644 index 0000000..3974c30 --- /dev/null +++ b/src/tools/msvc/pgbison.pl @@ -0,0 +1,55 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# src/tools/msvc/pgbison.pl + +use strict; +use warnings; + +use File::Basename; + +# assume we are in the postgres source root + +do './src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl'; + +my ($bisonver) = `bison -V`; # grab first line +$bisonver = (split(/\s+/, $bisonver))[3]; # grab version number + +unless ($bisonver eq '1.875' || $bisonver ge '2.2') +{ + print "WARNING! Bison install not found, or unsupported Bison version.\n"; + print "echo Attempting to build without.\n"; + exit 0; +} + +my $input = shift; +if ($input !~ /\.y$/) +{ + print "Input must be a .y file\n"; + exit 1; +} +elsif (!-e $input) +{ + print "Input file $input not found\n"; + exit 1; +} + +(my $output = $input) =~ s/\.y$/.c/; + +# plpgsql just has to be different +$output =~ s/gram\.c$/pl_gram.c/ if $input =~ /src.pl.plpgsql.src.gram\.y$/; + +my $makefile = dirname($input) . "/Makefile"; +my ($mf, $make); +open($mf, '<', $makefile); +local $/ = undef; +$make = <$mf>; +close($mf); +my $basetarg = basename($output); +my $headerflag = ($make =~ /^$basetarg:\s+BISONFLAGS\b.*-d/m ? '-d' : ''); + +my $nodep = $bisonver ge '3.0' ? "-Wno-deprecated" : ""; + +system("bison $nodep $headerflag $input -o $output"); +exit $? >> 8; diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat new file mode 100755 index 0000000..f20834b --- /dev/null +++ b/src/tools/msvc/pgflex.bat @@ -0,0 +1,7 @@ +@echo off + +REM src/tools/msvc/pgflex.bat +REM all the logic for this now belongs in pgflex.pl. This file really +REM only exists so you don't have to type "perl src/tools/msvc/pgflex.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/pgflex.pl %* diff --git a/src/tools/msvc/pgflex.pl b/src/tools/msvc/pgflex.pl new file mode 100644 index 0000000..eb31983 --- /dev/null +++ b/src/tools/msvc/pgflex.pl @@ -0,0 +1,108 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# src/tools/msvc/pgflex.pl + +use strict; +use warnings; + +use File::Basename; + +# silence flex bleatings about file path style +$ENV{CYGWIN} = 'nodosfilewarning'; + +# assume we are in the postgres source root + +do './src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl'; + +my ($flexver) = `flex -V`; # grab first line +$flexver = (split(/\s+/, $flexver))[1]; +$flexver =~ s/[^0-9.]//g; +my @verparts = split(/\./, $flexver); +unless ($verparts[0] == 2 + && ($verparts[1] > 5 || ($verparts[1] == 5 && $verparts[2] >= 31))) +{ + print "WARNING! Flex install not found, or unsupported Flex version.\n"; + print "echo Attempting to build without.\n"; + exit 0; +} + +my $input = shift; +if ($input !~ /\.l$/) +{ + print "Input must be a .l file\n"; + exit 1; +} +elsif (!-e $input) +{ + print "Input file $input not found\n"; + exit 1; +} + +(my $output = $input) =~ s/\.l$/.c/; + +# get flex flags from make file +my $makefile = dirname($input) . "/Makefile"; +my ($mf, $make); +open($mf, '<', $makefile); +local $/ = undef; +$make = <$mf>; +close($mf); +my $basetarg = basename($output); +my $flexflags = ($make =~ /^$basetarg:\s*FLEXFLAGS\s*=\s*(\S.*)/m ? $1 : ''); + +system("flex $flexflags -o$output $input"); +if ($? == 0) +{ + + # Check for "%option reentrant" in .l file. + my $lfile; + open($lfile, '<', $input) || die "opening $input for reading: $!"; + my $lcode = <$lfile>; + close($lfile); + if ($lcode =~ /\%option\sreentrant/) + { + + # Reentrant scanners usually need a fix to prevent + # "unused variable" warnings with older flex versions. + system("perl src\\tools\\fix-old-flex-code.pl $output"); + } + else + { + + # For non-reentrant scanners we need to fix up the yywrap + # macro definition to keep the MS compiler happy. + # For reentrant scanners (like the core scanner) we do not + # need to (and must not) change the yywrap definition. + my $cfile; + open($cfile, '<', $output) || die "opening $output for reading: $!"; + my $ccode = <$cfile>; + close($cfile); + $ccode =~ s/yywrap\(n\)/yywrap()/; + open($cfile, '>', $output) || die "opening $output for writing: $!"; + print $cfile $ccode; + close($cfile); + } + if ($flexflags =~ /\s-b\s/) + { + my $lexback = "lex.backup"; + open($lfile, '<', $lexback) || die "opening $lexback for reading: $!"; + my $lexbacklines = <$lfile>; + close($lfile); + my $linecount = $lexbacklines =~ tr /\n/\n/; + if ($linecount != 1) + { + print "Scanner requires backup, see lex.backup.\n"; + exit 1; + } + unlink $lexback; + } + + exit 0; + +} +else +{ + exit $? >> 8; +} diff --git a/src/tools/msvc/vcregress.bat b/src/tools/msvc/vcregress.bat new file mode 100644 index 0000000..7fba45c --- /dev/null +++ b/src/tools/msvc/vcregress.bat @@ -0,0 +1,6 @@ +@echo off +REM src/tools/msvc/vcregress.bat +REM all the logic for this now belongs in vcregress.pl. This file really +REM only exists so you don't have to type "perl vcregress.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/vcregress.pl %* diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl new file mode 100644 index 0000000..74fb735 --- /dev/null +++ b/src/tools/msvc/vcregress.pl @@ -0,0 +1,660 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# src/tools/msvc/vcregress.pl + +use strict; +use warnings; + +our $config; + +use Cwd; +use File::Basename; +use File::Copy; +use File::Find (); +use File::Path qw(rmtree); +use File::Spec qw(devnull); + +use FindBin; +use lib $FindBin::RealBin; + +use Install qw(Install); + +my $startdir = getcwd(); + +chdir "../../.." if (-d "../../../src/tools/msvc"); + +my $topdir = getcwd(); +my $tmp_installdir = "$topdir/tmp_install"; + +do './src/tools/msvc/config_default.pl'; +do './src/tools/msvc/config.pl' if (-f 'src/tools/msvc/config.pl'); + +my $devnull = File::Spec->devnull; + +# These values are defaults that can be overridden by the calling environment +# (see buildenv.pl processing below). We assume that the ones listed here +# always exist by default. Other values may optionally be set for bincheck +# or taptest, see set_command_env() below. +# c.f. src/Makefile.global.in and configure.ac +$ENV{TAR} ||= 'tar'; + +# buildenv.pl is for specifying the build environment settings +# it should contain lines like: +# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}"; + +if (-e "src/tools/msvc/buildenv.pl") +{ + do "./src/tools/msvc/buildenv.pl"; +} + +my $what = shift || ""; +if ($what =~ + /^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck|bincheck|recoverycheck|taptest)$/i + ) +{ + $what = uc $what; +} +else +{ + usage(); +} + +# use a capital C here because config.pl has $config +my $Config = -e "release/postgres/postgres.exe" ? "Release" : "Debug"; + +copy("$Config/refint/refint.dll", "src/test/regress"); +copy("$Config/autoinc/autoinc.dll", "src/test/regress"); +copy("$Config/regress/regress.dll", "src/test/regress"); +copy("$Config/dummy_seclabel/dummy_seclabel.dll", "src/test/regress"); + +# Configuration settings used by TAP tests +$ENV{with_ssl} = $config->{openssl} ? 'openssl' : 'no'; +$ENV{with_ldap} = $config->{ldap} ? 'yes' : 'no'; +$ENV{with_icu} = $config->{icu} ? 'yes' : 'no'; +$ENV{with_gssapi} = $config->{gss} ? 'yes' : 'no'; +$ENV{with_krb_srvnam} = $config->{krb_srvnam} || 'postgres'; +$ENV{with_readline} = 'no'; + +$ENV{PATH} = "$topdir/$Config/libpq;$ENV{PATH}"; + +if ($ENV{PERL5LIB}) +{ + $ENV{PERL5LIB} = "$topdir/src/tools/msvc;$ENV{PERL5LIB}"; +} +else +{ + $ENV{PERL5LIB} = "$topdir/src/tools/msvc"; +} + +my $maxconn = ""; +$maxconn = "--max-connections=$ENV{MAX_CONNECTIONS}" + if $ENV{MAX_CONNECTIONS}; + +my $temp_config = ""; +$temp_config = "--temp-config=\"$ENV{TEMP_CONFIG}\"" + if $ENV{TEMP_CONFIG}; + +chdir "src/test/regress"; + +my %command = ( + CHECK => \&check, + PLCHECK => \&plcheck, + INSTALLCHECK => \&installcheck, + ECPGCHECK => \&ecpgcheck, + CONTRIBCHECK => \&contribcheck, + MODULESCHECK => \&modulescheck, + ISOLATIONCHECK => \&isolationcheck, + BINCHECK => \&bincheck, + RECOVERYCHECK => \&recoverycheck, + UPGRADECHECK => \&upgradecheck, # no-op + TAPTEST => \&taptest,); + +my $proc = $command{$what}; + +exit 3 unless $proc; + +&$proc(@ARGV); + +exit 0; + +######################################################################## + +# Helper function for set_command_env, to set one environment command. +sub set_single_env +{ + my $envname = shift; + my $envdefault = shift; + + # If a command is defined by the environment, just use it. + return if (defined($ENV{$envname})); + + # Nothing is defined, so attempt to assign a default. The command + # may not be in the current environment, hence check if it can be + # executed. + my $rc = system("$envdefault --version >$devnull 2>&1"); + + # Set the environment to the default if it exists, else leave it. + $ENV{$envname} = $envdefault if $rc == 0; + return; +} + +# Set environment values for various command types. These can be used +# in the TAP tests. +sub set_command_env +{ + set_single_env('GZIP_PROGRAM', 'gzip'); + set_single_env('LZ4', 'lz4'); + set_single_env('ZSTD', 'zstd'); +} + +sub installcheck_internal +{ + my ($schedule, @EXTRA_REGRESS_OPTS) = @_; + # for backwards compatibility, "serial" runs the tests in + # parallel_schedule one by one. + my $maxconn = $maxconn; + $maxconn = "--max-connections=1" if $schedule eq 'serial'; + $schedule = 'parallel' if $schedule eq 'serial'; + + my @args = ( + "../../../$Config/pg_regress/pg_regress", + "--dlpath=.", + "--bindir=../../../$Config/psql", + "--schedule=${schedule}_schedule", + "--max-concurrent-tests=20"); + push(@args, $maxconn) if $maxconn; + push(@args, @EXTRA_REGRESS_OPTS); + system(@args); + my $status = $? >> 8; + exit $status if $status; + return; +} + +sub installcheck +{ + my $schedule = shift || 'serial'; + installcheck_internal($schedule); + return; +} + +sub check +{ + my $schedule = shift || 'parallel'; + # for backwards compatibility, "serial" runs the tests in + # parallel_schedule one by one. + my $maxconn = $maxconn; + $maxconn = "--max-connections=1" if $schedule eq 'serial'; + $schedule = 'parallel' if $schedule eq 'serial'; + + InstallTemp(); + chdir "${topdir}/src/test/regress"; + my @args = ( + "../../../$Config/pg_regress/pg_regress", + "--dlpath=.", + "--bindir=", + "--schedule=${schedule}_schedule", + "--max-concurrent-tests=20", + "--encoding=SQL_ASCII", + "--no-locale", + "--temp-instance=./tmp_check"); + push(@args, $maxconn) if $maxconn; + push(@args, $temp_config) if $temp_config; + system(@args); + my $status = $? >> 8; + exit $status if $status; + return; +} + +sub ecpgcheck +{ + my $msbflags = $ENV{MSBFLAGS} || ""; + chdir $startdir; + system("msbuild ecpg_regression.proj $msbflags /p:config=$Config"); + my $status = $? >> 8; + exit $status if $status; + InstallTemp(); + chdir "$topdir/src/interfaces/ecpg/test"; + my $schedule = "ecpg"; + my @args = ( + "../../../../$Config/pg_regress_ecpg/pg_regress_ecpg", + "--bindir=", + "--dbname=ecpg1_regression,ecpg2_regression", + "--create-role=regress_ecpg_user1,regress_ecpg_user2", + "--schedule=${schedule}_schedule", + "--encoding=SQL_ASCII", + "--no-locale", + "--temp-instance=./tmp_chk"); + push(@args, $maxconn) if $maxconn; + system(@args); + $status = $? >> 8; + exit $status if $status; + return; +} + +sub isolationcheck +{ + chdir "../isolation"; + copy("../../../$Config/isolationtester/isolationtester.exe", + "../../../$Config/pg_isolation_regress"); + my @args = ( + "../../../$Config/pg_isolation_regress/pg_isolation_regress", + "--bindir=../../../$Config/psql", + "--inputdir=.", + "--schedule=./isolation_schedule"); + push(@args, $maxconn) if $maxconn; + system(@args); + my $status = $? >> 8; + exit $status if $status; + return; +} + +sub tap_check +{ + die "Tap tests not enabled in configuration" + unless $config->{tap_tests}; + + my @flags; + foreach my $arg (0 .. scalar(@_) - 1) + { + next unless $_[$arg] =~ /^PROVE_FLAGS=(.*)/; + @flags = split(/\s+/, $1); + splice(@_, $arg, 1); + last; + } + + my $dir = shift; + chdir $dir; + + # Fetch and adjust PROVE_TESTS, applying glob() to each element + # defined to build a list of all the tests matching patterns. + my $prove_tests_val = $ENV{PROVE_TESTS} || "t/*.pl"; + my @prove_tests_array = split(/\s+/, $prove_tests_val); + my @prove_tests = (); + foreach (@prove_tests_array) + { + push(@prove_tests, glob($_)); + } + + # Fetch and adjust PROVE_FLAGS, handling multiple arguments. + my $prove_flags_val = $ENV{PROVE_FLAGS} || ""; + my @prove_flags = split(/\s+/, $prove_flags_val); + + my @args = ("prove", @flags, @prove_tests, @prove_flags); + + # adjust the environment for just this test + local %ENV = %ENV; + $ENV{PERL5LIB} = "$topdir/src/test/perl;$ENV{PERL5LIB}"; + $ENV{PG_REGRESS} = "$topdir/$Config/pg_regress/pg_regress"; + $ENV{REGRESS_SHLIB} = "$topdir/src/test/regress/regress.dll"; + + $ENV{TESTDIR} = "$dir"; + my $module = basename $dir; + # add the module build dir as the second element in the PATH + $ENV{PATH} =~ s!;!;$topdir/$Config/$module;!; + + rmtree('tmp_check'); + system(@args); + my $status = $? >> 8; + return $status; +} + +sub bincheck +{ + InstallTemp(); + + set_command_env(); + + my $mstat = 0; + + # Find out all the existing TAP tests by looking for t/ directories + # in the tree. + my @bin_dirs = glob("$topdir/src/bin/*"); + + # Process each test + foreach my $dir (@bin_dirs) + { + next unless -d "$dir/t"; + + my $status = tap_check($dir); + $mstat ||= $status; + } + exit $mstat if $mstat; + return; +} + +sub taptest +{ + my $dir = shift; + my @args; + + if ($dir =~ /^PROVE_FLAGS=/) + { + push(@args, $dir); + $dir = shift; + } + + die "no tests found!" unless -d "$topdir/$dir/t"; + + push(@args, "$topdir/$dir"); + + InstallTemp(); + + set_command_env(); + + my $status = tap_check(@args); + exit $status if $status; + return; +} + +sub plcheck +{ + chdir "$topdir/src/pl"; + + foreach my $dir (glob("*/src *")) + { + next unless -d "$dir/sql" && -d "$dir/expected"; + my $lang; + if ($dir eq 'plpgsql/src') + { + $lang = 'plpgsql'; + } + elsif ($dir eq 'tcl') + { + $lang = 'pltcl'; + } + else + { + $lang = $dir; + } + if ($lang eq 'plpython') + { + next + unless -d "$topdir/$Config/plpython3"; + $lang = 'plpythonu'; + } + else + { + next unless -d "$topdir/$Config/$lang"; + } + my @lang_args = ("--load-extension=$lang"); + chdir $dir; + my @tests = fetchTests(); + if ($lang eq 'plperl') + { + + # plperl tests will install the extensions themselves + @lang_args = (); + + # assume we're using this perl to built postgres + # test if we can run two interpreters in one backend, and if so + # run the trusted/untrusted interaction tests + use Config; + if ($Config{usemultiplicity} eq 'define') + { + push(@tests, 'plperl_plperlu'); + } + } + elsif ($lang eq 'plpythonu' && -d "$topdir/$Config/plpython3") + { + @lang_args = (); + } + + # Move on if no tests are listed. + next if (scalar @tests == 0); + + print + "============================================================\n"; + print "Checking $lang\n"; + my @args = ( + "$topdir/$Config/pg_regress/pg_regress", + "--bindir=$topdir/$Config/psql", + "--dbname=pl_regression", @lang_args, @tests); + system(@args); + my $status = $? >> 8; + exit $status if $status; + chdir "$topdir/src/pl"; + } + + chdir "$topdir"; + return; +} + +sub subdircheck +{ + my $module = shift; + + if ( !-d "$module/sql" + || !-d "$module/expected" + || (!-f "$module/GNUmakefile" && !-f "$module/Makefile")) + { + return; + } + + chdir $module; + my @tests = fetchTests(); + + # Leave if no tests are listed in the module. + if (scalar @tests == 0) + { + chdir ".."; + return; + } + + my @opts = fetchRegressOpts(); + + print "============================================================\n"; + print "Checking $module\n"; + my @args = ( + "$topdir/$Config/pg_regress/pg_regress", + "--bindir=${topdir}/${Config}/psql", + "--dbname=contrib_regression", @opts, @tests); + print join(' ', @args), "\n"; + system(@args); + chdir ".."; + return; +} + +sub contribcheck +{ + chdir "../../../contrib"; + my $mstat = 0; + foreach my $module (glob("*")) + { + # these configuration-based exclusions must match Install.pm + next if ($module eq "uuid-ossp" && !defined($config->{uuid})); + next if ($module eq "sslinfo" && !defined($config->{openssl})); + next if ($module eq "pgcrypto" && !defined($config->{openssl})); + next if ($module eq "xml2" && !defined($config->{xml})); + next if ($module =~ /_plperl$/ && !defined($config->{perl})); + next if ($module =~ /_plpython$/ && !defined($config->{python})); + next if ($module eq "sepgsql"); + + subdircheck($module); + my $status = $? >> 8; + $mstat ||= $status; + } + exit $mstat if $mstat; + return; +} + +sub modulescheck +{ + chdir "../../../src/test/modules"; + my $mstat = 0; + foreach my $module (glob("*")) + { + subdircheck($module); + my $status = $? >> 8; + $mstat ||= $status; + } + exit $mstat if $mstat; + return; +} + +sub recoverycheck +{ + InstallTemp(); + + my $dir = "$topdir/src/test/recovery"; + my $status = tap_check($dir); + exit $status if $status; + return; +} + +# Run "initdb", then reconfigure authentication. +sub standard_initdb +{ + return ( + system('initdb', '-N') == 0 and system( + "$topdir/$Config/pg_regress/pg_regress", '--config-auth', + $ENV{PGDATA}) == 0); +} + +# This is similar to appendShellString(). Perl system(@args) bypasses +# cmd.exe, so omit the caret escape layer. +sub quote_system_arg +{ + my $arg = shift; + + # Change N >= 0 backslashes before a double quote to 2N+1 backslashes. + $arg =~ s/(\\*)"/${\($1 . $1)}\\"/gs; + + # Change N >= 1 backslashes at end of argument to 2N backslashes. + $arg =~ s/(\\+)$/${\($1 . $1)}/gs; + + # Wrap the whole thing in unescaped double quotes. + return "\"$arg\""; +} + +sub upgradecheck +{ + # pg_upgrade is now handled by bincheck, but keep this target for + # backward compatibility. + print "upgradecheck is a no-op, use bincheck instead.\n"; + return; +} + +sub fetchRegressOpts +{ + my $handle; + open($handle, '<', "GNUmakefile") + || open($handle, '<', "Makefile") + || die "Could not open Makefile"; + local ($/) = undef; + my $m = <$handle>; + close($handle); + my @opts; + + $m =~ s{\\\r?\n}{}g; + if ($m =~ /^\s*REGRESS_OPTS\s*\+?=(.*)/m) + { + + # Substitute known Makefile variables, then ignore options that retain + # an unhandled variable reference. Ignore anything that isn't an + # option starting with "--". + @opts = grep { !/\$\(/ && /^--/ } + map { (my $x = $_) =~ s/\Q$(top_builddir)\E/\"$topdir\"/; $x; } + split(/\s+/, $1); + } + if ($m =~ /^\s*ENCODING\s*=\s*(\S+)/m) + { + push @opts, "--encoding=$1"; + } + if ($m =~ /^\s*NO_LOCALE\s*=\s*\S+/m) + { + push @opts, "--no-locale"; + } + return @opts; +} + +# Fetch the list of tests by parsing a module's Makefile. An empty +# list is returned if the module does not need to run anything. +sub fetchTests +{ + my $handle; + open($handle, '<', "GNUmakefile") + || open($handle, '<', "Makefile") + || die "Could not open Makefile"; + local ($/) = undef; + my $m = <$handle>; + close($handle); + my $t = ""; + + $m =~ s{\\\r?\n}{}g; + + # A module specifying NO_INSTALLCHECK does not support installcheck, + # so bypass its run by returning an empty set of tests. + if ($m =~ /^\s*NO_INSTALLCHECK\s*=\s*\S+/m) + { + return (); + } + + if ($m =~ /^REGRESS\s*=\s*(.*)$/gm) + { + $t = $1; + $t =~ s/\s+/ /g; + + if ($m =~ /contrib\/pgcrypto/) + { + + # pgcrypto is special since some tests depend on the + # configuration of the build + + my $pgptests = + $config->{zlib} + ? GetTests("ZLIB_TST", $m) + : GetTests("ZLIB_OFF_TST", $m); + $t =~ s/\$\(CF_PGP_TESTS\)/$pgptests/; + } + } + + return split(/\s+/, $t); +} + +sub GetTests +{ + my $testname = shift; + my $m = shift; + if ($m =~ /^$testname\s*=\s*(.*)$/gm) + { + return $1; + } + return ""; +} + +sub InstallTemp +{ + unless ($ENV{NO_TEMP_INSTALL}) + { + print "Setting up temp install\n\n"; + Install("$tmp_installdir", "all", $config); + } + $ENV{PATH} = "$tmp_installdir/bin;$ENV{PATH}"; + return; +} + +sub usage +{ + print STDERR + "Usage: vcregress.pl <mode> [<arg>]\n\n", + "Options for <mode>:\n", + " bincheck run tests of utilities in src/bin/\n", + " check deploy instance and run regression tests on it\n", + " contribcheck run tests of modules in contrib/\n", + " ecpgcheck run regression tests of ECPG\n", + " installcheck run regression tests on existing instance\n", + " isolationcheck run isolation tests\n", + " modulescheck run tests of modules in src/test/modules/\n", + " plcheck run tests of PL languages\n", + " recoverycheck run recovery test suite\n", + " taptest run an arbitrary TAP test set\n", + " upgradecheck run tests of pg_upgrade (no-op)\n", + "\nOptions for <arg>: (used by check and installcheck)\n", + " serial serial mode\n", + " parallel parallel mode\n", + "\nOption for <arg>: for taptest\n", + " TEST_DIR (required) directory where tests reside\n"; + exit(1); +} diff --git a/src/tools/perlcheck/find_perl_files b/src/tools/perlcheck/find_perl_files new file mode 100644 index 0000000..fd99dab --- /dev/null +++ b/src/tools/perlcheck/find_perl_files @@ -0,0 +1,14 @@ +# src/tools/perlcheck/find_perl_files + +# shell function to find all perl files in the source tree + +find_perl_files () { + { + # take all .pl and .pm files + find . -type f -name '*.p[lm]' -print + # take executable files that file(1) thinks are perl files + find . -type f -perm -100 -exec file {} \; -print | + egrep -i ':.*perl[0-9]*\>' | + cut -d: -f1 + } | sort -u | grep -v '^\./\.git/' +} diff --git a/src/tools/perlcheck/perlcriticrc b/src/tools/perlcheck/perlcriticrc new file mode 100644 index 0000000..9267fb4 --- /dev/null +++ b/src/tools/perlcheck/perlcriticrc @@ -0,0 +1,31 @@ +###################################################################### +# +# src/tools/perlcheck/perlcriticrc +# +# config file for perlcritic for Postgres project +# +##################################################################### + +severity = 5 + +theme = core + +# print the policy name as well as the normal output +verbose = %f: %m at line %l, column %c. %e. ([%p] Severity: %s)\n + +# Note: for policy descriptions see https://metacpan.org/release/Perl-Critic + + +# allow octal constants with leading zeros +[-ValuesAndExpressions::ProhibitLeadingZeros] + +# insist on use of the warnings pragma +[TestingAndDebugging::RequireUseWarnings] +severity = 5 + +# forbid grep and map in void context +[BuiltinFunctions::ProhibitVoidGrep] +severity = 5 + +[BuiltinFunctions::ProhibitVoidMap] +severity = 5 diff --git a/src/tools/perlcheck/pgperlcritic b/src/tools/perlcheck/pgperlcritic new file mode 100755 index 0000000..1c2f787 --- /dev/null +++ b/src/tools/perlcheck/pgperlcritic @@ -0,0 +1,20 @@ +#!/bin/sh + +# src/tools/perlcheck/pgperlcritic + +test -f src/tools/perlcheck/perlcriticrc || { + echo could not find src/tools/perlcheck/perlcriticrc + exit 1 + } + +set -e + +# set this to override default perlcritic program: +PERLCRITIC=${PERLCRITIC:-perlcritic} + +. src/tools/perlcheck/find_perl_files + +find_perl_files | xargs $PERLCRITIC \ + --quiet \ + --program-extensions .pl \ + --profile=src/tools/perlcheck/perlcriticrc diff --git a/src/tools/perlcheck/pgperlsyncheck b/src/tools/perlcheck/pgperlsyncheck new file mode 100755 index 0000000..730f592 --- /dev/null +++ b/src/tools/perlcheck/pgperlsyncheck @@ -0,0 +1,16 @@ +#!/bin/sh + +# script to detect compile time errors and warnings in all perl files + +INCLUDES="-I src/tools/msvc -I src/tools/msvc/dummylib -I src/backend/catalog" +INCLUDES="-I src/test/perl -I src/backend/utils/mb/Unicode $INCLUDES" +INCLUDES="-I src/bin/pg_rewind -I src/test/ssl/t $INCLUDES" + +set -e + +. src/tools/perlcheck/find_perl_files + +# for zsh +setopt shwordsplit 2>/dev/null || true + +find_perl_files | xargs -L 1 perl $INCLUDES -cw 2>&1 | grep -v OK diff --git a/src/tools/pginclude/README b/src/tools/pginclude/README new file mode 100644 index 0000000..49eb4b6 --- /dev/null +++ b/src/tools/pginclude/README @@ -0,0 +1,103 @@ +src/tools/pginclude/README + +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 +============ + +This script can be run to verify that all Postgres include files meet +the project convention that they will compile "standalone", that is +with no prerequisite headers other than postgres.h (or postgres_fe.h +or c.h, as appropriate). + +A small number of header files are exempted from this requirement, +and are skipped by the headerscheck script. + +The easy way to run the script is to say "make -s headerscheck" in +the top-level build directory after completing a build. You should +have included "--with-perl --with-python" in your configure options, +else you're likely to get errors about related headers not being found. + +A limitation of the current script is that it doesn't know which headers +are for frontend or backend, so it tests everything with postgres.h +as prerequisite, even if postgres_fe.h would be more appropriate. Also +note that the contents of macros are not checked; this is intentional. + + +cpluspluscheck +============== + +This script can be run to verify that all Postgres include files meet +the project convention that they will compile as C++ code. Although +the project's coding language is C, some people write extensions in C++, +so it's helpful for include files to be C++-clean. + +A small number of header files are exempted from this requirement, +and are skipped by the cpluspluscheck script. + +The easy way to run the script is to say "make -s cpluspluscheck" in +the top-level build directory after completing a build. You should +have included "--with-perl --with-python" in your configure options, +else you're likely to get errors about related headers not being found. + +If you are using a non-g++-compatible C++ compiler, you may need to +override the script's CXXFLAGS setting by setting a suitable environment +value. + +A limitation of the current script is that it doesn't know which headers +are for frontend or backend, so it tests everything with postgres.h +as prerequisite, even if postgres_fe.h would be more appropriate. Also +note that the contents of macros are not checked; this is intentional. diff --git a/src/tools/pginclude/cpluspluscheck b/src/tools/pginclude/cpluspluscheck new file mode 100755 index 0000000..42688ff --- /dev/null +++ b/src/tools/pginclude/cpluspluscheck @@ -0,0 +1,175 @@ +#!/bin/sh + +# Check (almost) all PostgreSQL include files for C++ compatibility. +# +# 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/cpluspluscheck +# Copyright (c) 2009-2022, PostgreSQL Global Development Group + +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"` +CXX=`sed -n 's/^CXX[ ]*=[ ]*//p' "$MGLOB"` +perl_includespec=`sed -n 's/^perl_includespec[ ]*=[ ]*//p' "$MGLOB"` +python_includespec=`sed -n 's/^python_includespec[ ]*=[ ]*//p' "$MGLOB"` + +# Extract any -I and -D switches from CPPFLAGS. +# (If necessary, user can pass more switches by presetting EXTRAFLAGS.) +for flag in $CPPFLAGS; do + case $flag in + -I*|-D*) EXTRAFLAGS="$EXTRAFLAGS $flag";; + esac +done + +# Create temp directory. +tmp=`mktemp -d /tmp/$me.XXXXXX` + +trap 'rm -rf $tmp' 0 1 2 3 15 + +# 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/aix.h && continue + 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/hpux.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/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-hppa.h && continue + test "$f" = src/include/port/atomics/arch-ia64.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-acc.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 + + # rusagestub.h is also platform-specific, and will be included + # by utils/pg_rusage.h if necessary. + test "$f" = src/include/rusagestub.h && continue + + # sepgsql.h depends on headers that aren't there on most platforms. + test "$f" = contrib/sepgsql/sepgsql.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/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" = src/include/common/unicode_combining_table.h && continue + test "$f" = src/include/common/unicode_east_asian_fw_table.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" = src/include/parser/gram.h && continue + test "$f" = src/backend/parser/gram.h && continue + test "$f" = src/pl/plpgsql/src/pl_gram.h && continue + test "$f" = src/interfaces/ecpg/preproc/preproc.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 + + # 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_io.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 + + # OK, create .c file to include this .h file. + { + echo 'extern "C" {' + test "$f" != src/include/postgres_fe.h && echo '#include "postgres.h"' + echo "#include \"$f\"" + echo '};' + } >$tmp/test.cpp + + # 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" ;; + *) + EXTRAINCLUDES="" ;; + esac + + # Run the test. + ${CXX:-g++} -I $builddir -I $srcdir \ + -I $builddir/src/include -I $srcdir/src/include \ + -I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \ + $EXTRAINCLUDES $EXTRAFLAGS $CXXFLAGS -c $tmp/test.cpp + +done diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck new file mode 100755 index 0000000..f8b0674 --- /dev/null +++ b/src/tools/pginclude/headerscheck @@ -0,0 +1,159 @@ +#!/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-2022, PostgreSQL Global Development Group + +if [ -z "$1" ]; then + srcdir="." +else + srcdir="$1" +fi + +if [ -z "$2" ]; then + builddir="." +else + builddir="$2" +fi + +me=`basename $0` + +# 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"` +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.) + +# Create temp directory. +tmp=`mktemp -d /tmp/$me.XXXXXX` + +trap 'rm -rf $tmp' 0 1 2 3 15 + +# 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/aix.h && continue + 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/hpux.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/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-hppa.h && continue + test "$f" = src/include/port/atomics/arch-ia64.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-acc.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 + + # rusagestub.h is also platform-specific, and will be included + # by utils/pg_rusage.h if necessary. + test "$f" = src/include/rusagestub.h && continue + + # sepgsql.h depends on headers that aren't there on most platforms. + test "$f" = contrib/sepgsql/sepgsql.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/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" = src/include/common/unicode_combining_table.h && continue + test "$f" = src/include/common/unicode_east_asian_fw_table.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" = src/include/parser/gram.h && continue + test "$f" = src/backend/parser/gram.h && continue + test "$f" = src/pl/plpgsql/src/pl_gram.h && continue + test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue + + # This produces a "no previous prototype" warning. + test "$f" = src/include/storage/checksum_impl.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 + + # OK, create .c file to include this .h file. + { + test "$f" != src/include/postgres_fe.h && echo '#include "postgres.h"' + echo "#include \"$f\"" + } >$tmp/test.c + + # 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" ;; + *) + EXTRAINCLUDES="" ;; + esac + + # Run the test. + ${CC:-gcc} $CPPFLAGS $CFLAGS -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.c -o $tmp/test.o + +done diff --git a/src/tools/pginclude/pgcheckdefines b/src/tools/pginclude/pgcheckdefines new file mode 100755 index 0000000..e46a756 --- /dev/null +++ b/src/tools/pginclude/pgcheckdefines @@ -0,0 +1,305 @@ +#! /usr/bin/perl + +# Copyright (c) 2021-2022, 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; + +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; +} diff --git a/src/tools/pginclude/pgcompinclude b/src/tools/pginclude/pgcompinclude new file mode 100755 index 0000000..12169db --- /dev/null +++ b/src/tools/pginclude/pgcompinclude @@ -0,0 +1,47 @@ +: +# 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-prototypes \ + -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 diff --git a/src/tools/pginclude/pgdefine b/src/tools/pginclude/pgdefine new file mode 100755 index 0000000..242d035 --- /dev/null +++ b/src/tools/pginclude/pgdefine @@ -0,0 +1,25 @@ +: +# 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 diff --git a/src/tools/pginclude/pgfixinclude b/src/tools/pginclude/pgfixinclude new file mode 100755 index 0000000..6721566 --- /dev/null +++ b/src/tools/pginclude/pgfixinclude @@ -0,0 +1,21 @@ +: +# 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 diff --git a/src/tools/pginclude/pgrminclude b/src/tools/pginclude/pgrminclude new file mode 100755 index 0000000..7cbd2e7 --- /dev/null +++ b/src/tools/pginclude/pgrminclude @@ -0,0 +1,149 @@ +: +# 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 leak 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-prototypes \ + -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 diff --git a/src/tools/pgindent/README b/src/tools/pgindent/README new file mode 100644 index 0000000..103970c --- /dev/null +++ b/src/tools/pgindent/README @@ -0,0 +1,159 @@ +pgindent'ing the PostgreSQL source tree +======================================= + +We run this process at least once in each development cycle, +to maintain uniform layout style in our C and Perl code. + +You might find this blog post interesting: +http://adpgtech.blogspot.com/2015/05/running-pgindent-on-non-core-code-or.html + + +PREREQUISITES: + +1) Install pg_bsd_indent in your PATH. Fetch its source code with + git clone https://git.postgresql.org/git/pg_bsd_indent.git + then follow the directions in README.pg_bsd_indent therein. + +2) Install perltidy. Please be sure it is version 20170521 (older and newer + versions make different formatting choices, and we want consistency). + You can get the correct version from + https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/ + To install, follow the usual install process for a Perl module + ("man perlmodinstall" explains it). Or, if you have cpan installed, + this should work: + cpan SHANCOCK/Perl-Tidy-20170521.tar.gz + +DOING THE INDENT RUN: + +1) Change directory to the top of the source tree. + +2) Download the latest typedef file from the buildfarm: + + wget -O src/tools/pgindent/typedefs.list https://buildfarm.postgresql.org/cgi-bin/typedefs.pl + + (See https://buildfarm.postgresql.org/cgi-bin/typedefs.pl?show_list for a full + list of typedef files, if you want to indent some back branch.) + +3) Run pgindent on the C files: + + src/tools/pgindent/pgindent + + If any files generate errors, restore their original versions with + "git checkout", and see below for cleanup ideas. + +4) Indent the Perl code using perltidy: + + src/tools/pgindent/pgperltidy + + If you want to use some perltidy version that's not in your PATH, + first set the PERLTIDY environment variable to point to it. + +5) Reformat the bootstrap catalog data files: + + ./configure # "make" will not work in an unconfigured tree + cd src/include/catalog + make reformat-dat-files + cd ../../.. + +VALIDATION: + +1) Check for any newly-created files using "git status"; there shouldn't + be any. (pgindent leaves *.BAK files behind if it has trouble, while + perltidy leaves *.LOG files behind.) + +2) Do a full test build: + + make -s clean + make -s all # look for unexpected warnings, and errors of course + make check-world + + Your configure switches should include at least --enable-tap-tests + or else much of the Perl code won't get exercised. + The ecpg regression tests may well fail due to pgindent's updates of + header files that get copied into ecpg output; if so, adjust the + expected-files to match. + +3) If you have the patience, it's worth eyeballing the "git diff" output + for any egregiously ugly changes. See below for cleanup ideas. + + +When you're done, "git commit" everything including the typedefs.list file +you used. + +4) Add the newly created commits to the .git-blame-ignore-revs file so + that "git blame" ignores the commits (for anybody that has opted-in + to using the ignore file). Follow the instructions that appear at + the top of the .git-blame-ignore-revs file. + +Another "git commit" will be required for your ignore file changes. + +--------------------------------------------------------------------------- + +Cleaning up in case of failure or ugly output +--------------------------------------------- + +If you don't like the results for any particular file, "git checkout" +that file to undo the changes, patch the file as needed, then repeat +the indent process. + +pgindent will reflow any comment block that's not at the left margin. +If this messes up manual formatting that ought to be preserved, protect +the comment block with some dashes: + + /*---------- + * Text here will not be touched by pgindent. + *---------- + */ + +Odd spacing around typedef names might indicate an incomplete typedefs list. + +pgindent will mangle both declaration and definition of a C function whose +name matches a typedef. Currently the best workaround is to choose +non-conflicting names. + +pgindent can get confused by #if sequences that look correct to the compiler +but have mismatched braces/parentheses when considered as a whole. Usually +that looks pretty unreadable to humans too, so best practice is to rearrange +the #if tests to avoid it. + +Sometimes, if pgindent or perltidy produces odd-looking output, it's because +of minor bugs like extra commas. Don't hesitate to clean that up while +you're at it. + +--------------------------------------------------------------------------- + +BSD indent +---------- + +We have standardized on FreeBSD's indent, and renamed it pg_bsd_indent. +pg_bsd_indent does differ slightly from FreeBSD's version, mostly in +being more easily portable to non-BSD platforms. You can obtain it from +https://git.postgresql.org/git/pg_bsd_indent.git + +GNU indent, version 2.2.6, has several problems, and is not recommended. +These bugs become pretty major when you are doing >500k lines of code. +If you don't believe me, take a directory and make a copy. Run pgindent +on the copy using GNU indent, and do a diff -r. You will see what I +mean. GNU indent does some things better, but mangles too. For details, +see: + + http://archives.postgresql.org/pgsql-hackers/2003-10/msg00374.php + http://archives.postgresql.org/pgsql-hackers/2011-04/msg01436.php + +--------------------------------------------------------------------------- + +Which files are processed +------------------------- + +The pgindent run processes (nearly) all PostgreSQL *.c and *.h files, +but we currently exclude *.y and *.l files, as well as *.c and *.h files +derived from *.y and *.l files. Additional exceptions are listed +in exclude_file_patterns; see the notes therein for rationale. + +Note that we do not exclude ecpg's header files from the run. Some of them +get copied verbatim into ecpg's output, meaning that ecpg's expected files +may need to be updated to match. + +The perltidy run processes all *.pl and *.pm files, plus a few +executable Perl scripts that are not named that way. See the "find" +rules in pgperltidy for details. diff --git a/src/tools/pgindent/exclude_file_patterns b/src/tools/pgindent/exclude_file_patterns new file mode 100644 index 0000000..f08180b --- /dev/null +++ b/src/tools/pgindent/exclude_file_patterns @@ -0,0 +1,49 @@ +# List of filename patterns to exclude from pgindent runs +# +# These contain assembly code that pgindent tends to mess up. +src/include/storage/s_lock\.h$ +src/include/port/atomics/ +# +# This contains C++ constructs that confuse pgindent. +src/include/jit/llvmjit\.h$ +# +# This confuses pgindent, and it's a derived file anyway. +src/backend/utils/fmgrtab\.c$ +# +# pgindent might mangle entries in this that match typedef names. +# Since it's a derived file anyway, just exclude it. +src/backend/utils/fmgrprotos\.h$ +# +# kwlist_d files are made by gen_keywordlist.pl. While we could insist that +# they match pgindent style, they'd look worse not better, so exclude them. +kwlist_d\.h$ +# +# These are generated by the scripts from src/common/unicode/. They use +# hash functions generated by PerfectHash.pm whose format looks worse with +# pgindent. +src/include/common/unicode_norm_hashfunc\.h$ +src/include/common/unicode_normprops_table\.h$ +# +# Exclude ecpg test files to avoid breaking the ecpg regression tests +# (but include files at the top level of the ecpg/test/ directory). +src/interfaces/ecpg/test/.*/ +# +# src/include/snowball/libstemmer/ and src/backend/snowball/libstemmer/ +# are excluded because those files are imported from an external project, +# rather than maintained locally, and they are machine-generated anyway. +/snowball/libstemmer/ +# +# These files are machine-generated by code not under our control, +# so we shouldn't expect them to conform to our style. +# (Some versions of dtrace build probes.h files that confuse pgindent, too.) +src/backend/utils/probes\.h$ +src/include/pg_config\.h$ +src/pl/plperl/ppport\.h$ +src/pl/plperl/SPI\.c$ +src/pl/plperl/Util\.c$ +# +# Exclude any temporary installations that may be in the tree. +/tmp_check/ +/tmp_install/ +# ... and for paranoia's sake, don't touch git stuff. +/\.git/ diff --git a/src/tools/pgindent/perltidyrc b/src/tools/pgindent/perltidyrc new file mode 100644 index 0000000..9f09f0a --- /dev/null +++ b/src/tools/pgindent/perltidyrc @@ -0,0 +1,16 @@ +--add-whitespace +--backup-and-modify-in-place +--backup-file-extension=/ +--delete-old-whitespace +--entab-leading-whitespace=4 +--keep-old-blank-lines=2 +--maximum-line-length=78 +--nooutdent-long-comments +--nooutdent-long-quotes +--nospace-for-semicolon +--opening-brace-on-new-line +--output-line-ending=unix +--paren-tightness=2 +--paren-vertical-tightness=2 +--paren-vertical-tightness-closing=2 +--noblanks-before-comments diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent new file mode 100755 index 0000000..2ef07bb --- /dev/null +++ b/src/tools/pgindent/pgindent @@ -0,0 +1,436 @@ +#!/usr/bin/perl + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +use strict; +use warnings; +use 5.008001; + +use Cwd qw(abs_path getcwd); +use File::Find; +use File::Spec qw(devnull); +use File::Temp; +use IO::Handle; +use Getopt::Long; + +# Update for pg_bsd_indent version +my $INDENT_VERSION = "2.1.1"; + +# Our standard indent settings +my $indent_opts = + "-bad -bap -bbb -bc -bl -cli1 -cp33 -cdb -nce -d0 -di12 -nfc1 -i4 -l79 -lp -lpl -nip -npro -sac -tpg -ts4"; + +my $devnull = File::Spec->devnull; + +my ($typedefs_file, $typedef_str, $code_base, $excludes, $indent, $build); + +my %options = ( + "typedefs=s" => \$typedefs_file, + "list-of-typedefs=s" => \$typedef_str, + "code-base=s" => \$code_base, + "excludes=s" => \$excludes, + "indent=s" => \$indent, + "build" => \$build,); +GetOptions(%options) || die "bad command line argument\n"; + +run_build($code_base) if ($build); + +# command line option wins, then first non-option arg, +# then environment (which is how --build sets it) , +# then locations. based on current dir, then default location +$typedefs_file ||= shift if @ARGV && $ARGV[0] !~ /\.[ch]$/; +$typedefs_file ||= $ENV{PGTYPEDEFS}; + +# build mode sets PGINDENT +$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent"; + +# no non-option arguments given. so do everything in the current directory +$code_base ||= '.' unless @ARGV; + +# if it's the base of a postgres tree, we will exclude the files +# postgres wants excluded +$excludes ||= "$code_base/src/tools/pgindent/exclude_file_patterns" + if $code_base && -f "$code_base/src/tools/pgindent/exclude_file_patterns"; + +# The typedef list that's mechanically extracted by the buildfarm may omit +# some names we want to treat like typedefs, e.g. "bool" (which is a macro +# according to <stdbool.h>), and may include some names we don't want +# treated as typedefs, although various headers that some builds include +# might make them so. For the moment we just hardwire a list of names +# to add and a list of names to exclude; eventually this may need to be +# easier to configure. Note that the typedefs need trailing newlines. +my @additional = ("bool\n"); + +my %excluded = map { +"$_\n" => 1 } qw( + ANY FD_SET U abs allocfunc boolean date digit ilist interval iterator other + pointer printfunc reference string timestamp type wrap +); + +# globals +my @files; +my $filtered_typedefs_fh; + + +sub check_indent +{ + system("$indent -? < $devnull > $devnull 2>&1"); + if ($? >> 8 != 1) + { + print STDERR + "You do not appear to have $indent installed on your system.\n"; + exit 1; + } + + if (`$indent --version` !~ m/ $INDENT_VERSION /) + { + print STDERR + "You do not appear to have $indent version $INDENT_VERSION installed on your system.\n"; + exit 1; + } + + system("$indent -gnu < $devnull > $devnull 2>&1"); + if ($? == 0) + { + print STDERR + "You appear to have GNU indent rather than BSD indent.\n"; + exit 1; + } + + return; +} + + +sub load_typedefs +{ + + # try fairly hard to find the typedefs file if it's not set + + foreach my $try ('.', 'src/tools/pgindent', '/usr/local/etc') + { + $typedefs_file ||= "$try/typedefs.list" + if (-f "$try/typedefs.list"); + } + + # try to find typedefs by moving up directory levels + my $tdtry = ".."; + foreach (1 .. 5) + { + $typedefs_file ||= "$tdtry/src/tools/pgindent/typedefs.list" + if (-f "$tdtry/src/tools/pgindent/typedefs.list"); + $tdtry = "$tdtry/.."; + } + die "cannot locate typedefs file \"$typedefs_file\"\n" + unless $typedefs_file && -f $typedefs_file; + + open(my $typedefs_fh, '<', $typedefs_file) + || die "cannot open typedefs file \"$typedefs_file\": $!\n"; + my @typedefs = <$typedefs_fh>; + close($typedefs_fh); + + # add command-line-supplied typedefs? + if (defined($typedef_str)) + { + foreach my $typedef (split(m/[, \t\n]+/, $typedef_str)) + { + push(@typedefs, $typedef . "\n"); + } + } + + # add additional entries + push(@typedefs, @additional); + + # remove excluded entries + @typedefs = grep { !$excluded{$_} } @typedefs; + + # write filtered typedefs + my $filter_typedefs_fh = new File::Temp(TEMPLATE => "pgtypedefXXXXX"); + print $filter_typedefs_fh @typedefs; + $filter_typedefs_fh->close(); + + # temp file remains because we return a file handle reference + return $filter_typedefs_fh; +} + + +sub process_exclude +{ + if ($excludes && @files) + { + open(my $eh, '<', $excludes) + || die "cannot open exclude file \"$excludes\"\n"; + while (my $line = <$eh>) + { + chomp $line; + next if $line =~ m/^#/; + my $rgx = qr!$line!; + @files = grep { $_ !~ /$rgx/ } @files if $rgx; + } + close($eh); + } + return; +} + + +sub read_source +{ + my $source_filename = shift; + my $source; + + open(my $src_fd, '<', $source_filename) + || die "cannot open file \"$source_filename\": $!\n"; + local ($/) = undef; + $source = <$src_fd>; + close($src_fd); + + return $source; +} + + +sub write_source +{ + my $source = shift; + my $source_filename = shift; + + open(my $src_fh, '>', $source_filename) + || die "cannot open file \"$source_filename\": $!\n"; + print $src_fh $source; + close($src_fh); + return; +} + + +sub pre_indent +{ + my $source = shift; + + ## Comments + + # Convert // comments to /* */ + $source =~ s!^([ \t]*)//(.*)$!$1/* $2 */!gm; + + # Adjust dash-protected block comments so indent won't change them + $source =~ s!/\* +---!/*---X_X!g; + + ## Other + + # Prevent indenting of code in 'extern "C"' blocks. + # we replace the braces with comments which we'll reverse later + my $extern_c_start = '/* Open extern "C" */'; + my $extern_c_stop = '/* Close extern "C" */'; + $source =~ + s!(^#ifdef[ \t]+__cplusplus.*\nextern[ \t]+"C"[ \t]*\n)\{[ \t]*$!$1$extern_c_start!gm; + $source =~ s!(^#ifdef[ \t]+__cplusplus.*\n)\}[ \t]*$!$1$extern_c_stop!gm; + + # Protect wrapping in CATALOG() + $source =~ s!^(CATALOG\(.*)$!/*$1*/!gm; + + return $source; +} + + +sub post_indent +{ + my $source = shift; + my $source_filename = shift; + + # Restore CATALOG lines + $source =~ s!^/\*(CATALOG\(.*)\*/$!$1!gm; + + # Put back braces for extern "C" + $source =~ s!^/\* Open extern "C" \*/$!{!gm; + $source =~ s!^/\* Close extern "C" \*/$!}!gm; + + ## Comments + + # Undo change of dash-protected block comments + $source =~ s!/\*---X_X!/* ---!g; + + # Fix run-together comments to have a tab between them + $source =~ s!\*/(/\*.*\*/)$!*/\t$1!gm; + + ## Functions + + # Use a single space before '*' in function return types + $source =~ s!^([A-Za-z_]\S*)[ \t]+\*$!$1 *!gm; + + return $source; +} + + +sub run_indent +{ + my $source = shift; + my $error_message = shift; + + my $cmd = "$indent $indent_opts -U" . $filtered_typedefs_fh->filename; + + my $tmp_fh = new File::Temp(TEMPLATE => "pgsrcXXXXX"); + my $filename = $tmp_fh->filename; + print $tmp_fh $source; + $tmp_fh->close(); + + $$error_message = `$cmd $filename 2>&1`; + + return "" if ($? || length($$error_message) > 0); + + unlink "$filename.BAK"; + + open(my $src_out, '<', $filename); + local ($/) = undef; + $source = <$src_out>; + close($src_out); + + return $source; + +} + + +# for development diagnostics +sub diff +{ + my $pre = shift; + my $post = shift; + my $flags = shift || ""; + + print STDERR "running diff\n"; + + my $pre_fh = new File::Temp(TEMPLATE => "pgdiffbXXXXX"); + my $post_fh = new File::Temp(TEMPLATE => "pgdiffaXXXXX"); + + print $pre_fh $pre; + print $post_fh $post; + + $pre_fh->close(); + $post_fh->close(); + + system( "diff $flags " + . $pre_fh->filename . " " + . $post_fh->filename + . " >&2"); + return; +} + + +sub run_build +{ + eval "use LWP::Simple;"; ## no critic (ProhibitStringyEval); + + my $code_base = shift || '.'; + my $save_dir = getcwd(); + + # look for the code root + foreach (1 .. 5) + { + last if -d "$code_base/src/tools/pgindent"; + $code_base = "$code_base/.."; + } + + die "cannot locate src/tools/pgindent directory in \"$code_base\"\n" + unless -d "$code_base/src/tools/pgindent"; + + chdir "$code_base/src/tools/pgindent"; + + my $typedefs_list_url = + "https://buildfarm.postgresql.org/cgi-bin/typedefs.pl"; + + my $rv = getstore($typedefs_list_url, "tmp_typedefs.list"); + + die "cannot fetch typedefs list from $typedefs_list_url\n" + unless is_success($rv); + + $ENV{PGTYPEDEFS} = abs_path('tmp_typedefs.list'); + + my $indentrepo = "https://git.postgresql.org/git/pg_bsd_indent.git"; + system("git clone $indentrepo >$devnull 2>&1"); + die "could not fetch pg_bsd_indent sources from $indentrepo\n" + unless $? == 0; + + chdir "pg_bsd_indent" || die; + system("make all check >$devnull"); + die "could not build pg_bsd_indent from source\n" + unless $? == 0; + + $ENV{PGINDENT} = abs_path('pg_bsd_indent'); + + chdir $save_dir; + return; +} + + +sub build_clean +{ + my $code_base = shift || '.'; + + # look for the code root + foreach (1 .. 5) + { + last if -d "$code_base/src/tools/pgindent"; + $code_base = "$code_base/.."; + } + + die "cannot locate src/tools/pgindent directory in \"$code_base\"\n" + unless -d "$code_base/src/tools/pgindent"; + + chdir "$code_base"; + + system("rm -rf src/tools/pgindent/pg_bsd_indent"); + system("rm -f src/tools/pgindent/tmp_typedefs.list"); + return; +} + + +# main + +# get the list of files under code base, if it's set +File::Find::find( + { + wanted => sub { + my ($dev, $ino, $mode, $nlink, $uid, $gid); + (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) + && -f _ + && /^.*\.[ch]\z/s + && push(@files, $File::Find::name); + } + }, + $code_base) if $code_base; + +process_exclude(); + +$filtered_typedefs_fh = load_typedefs(); + +check_indent(); + +# any non-option arguments are files to be processed +push(@files, @ARGV); + +foreach my $source_filename (@files) +{ + + # Automatically ignore .c and .h files that correspond to a .y or .l + # file. indent tends to get badly confused by Bison/flex output, + # and there's no value in indenting derived files anyway. + my $otherfile = $source_filename; + $otherfile =~ s/\.[ch]$/.y/; + next if $otherfile ne $source_filename && -f $otherfile; + $otherfile =~ s/\.y$/.l/; + next if $otherfile ne $source_filename && -f $otherfile; + + my $source = read_source($source_filename); + my $orig_source = $source; + my $error_message = ''; + + $source = pre_indent($source); + + $source = run_indent($source, \$error_message); + if ($source eq "") + { + print STDERR "Failure in $source_filename: " . $error_message . "\n"; + next; + } + + $source = post_indent($source, $source_filename); + + write_source($source, $source_filename) if $source ne $orig_source; +} + +build_clean($code_base) if $build; diff --git a/src/tools/pgindent/pgindent.man b/src/tools/pgindent/pgindent.man new file mode 100644 index 0000000..1c5aedd --- /dev/null +++ b/src/tools/pgindent/pgindent.man @@ -0,0 +1,40 @@ +pgindent will indent .c and .h files according to the coding standards of +the PostgreSQL project. It needs several things to run, and tries to locate +or build them if possible. They can also be specified via command line switches +or the environment. + +In its simplest form, if all the required objects are installed, simply run +it without any parameters at the top of the source tree you want to process. + + pgindent + +If you don't have all the requirements installed, pgindent will fetch and build +them for you, if you're in a PostgreSQL source tree: + + pgindent --build + +If your pg_bsd_indent program is not installed in your path, you can specify +it by setting the environment variable INDENT, or PGINDENT, or by giving the +command line option --indent: + + pgindent --indent=/opt/extras/bsdindent + +pgindent also needs a file containing a list of typedefs. This can be +specified using the PGTYPEDEFS environment variable, or via the command line +--typedefs option. If neither is used, it will look for it within the +current source tree, or in /usr/local/etc/typedefs.list. + +If you want to indent a source tree other than the current working directory, +you can specify it via the --code-base command line option. + +We don't want to indent certain files in the PostgreSQL source. pgindent +will honor a file containing a list of patterns of files to avoid. This +file can be specified using the --excludes command line option. If indenting +a PostgreSQL source tree, this option isn't necessary, as it will find the file +src/tools/pgindent/exclude_file_patterns. + +Any non-option arguments are taken as the names of files to be indented. In this +case only these files will be changed, and nothing else will be touched. If the +first non-option argument is not a .c or .h file, it is treated as the name +of a typedefs file for legacy reasons, but this use is deprecated - use the +--typedefs option instead. diff --git a/src/tools/pgindent/pgperltidy b/src/tools/pgindent/pgperltidy new file mode 100755 index 0000000..5e70411 --- /dev/null +++ b/src/tools/pgindent/pgperltidy @@ -0,0 +1,12 @@ +#!/bin/sh + +# src/tools/pgindent/pgperltidy + +set -e + +# set this to override default perltidy program: +PERLTIDY=${PERLTIDY:-perltidy} + +. src/tools/perlcheck/find_perl_files + +find_perl_files | xargs $PERLTIDY --profile=src/tools/pgindent/perltidyrc diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list new file mode 100644 index 0000000..826d5d6 --- /dev/null +++ b/src/tools/pgindent/typedefs.list @@ -0,0 +1,3900 @@ +ACCESS_ALLOWED_ACE +ACL +ACL_SIZE_INFORMATION +AFFIX +ASN1_INTEGER +ASN1_OBJECT +ASN1_OCTET_STRING +ASN1_STRING +AV +A_ArrayExpr +A_Const +A_Expr +A_Expr_Kind +A_Indices +A_Indirection +A_Star +AbsoluteTime +AccessMethodInfo +AccessPriv +Acl +AclItem +AclMaskHow +AclMode +AclResult +AcquireSampleRowsFunc +ActionList +ActiveSnapshotElt +AddForeignUpdateTargets_function +AffixNode +AffixNodeData +AfterTriggerEvent +AfterTriggerEventChunk +AfterTriggerEventData +AfterTriggerEventList +AfterTriggerShared +AfterTriggerSharedData +AfterTriggersData +AfterTriggersQueryData +AfterTriggersTableData +AfterTriggersTransData +Agg +AggClauseCosts +AggInfo +AggPath +AggSplit +AggState +AggStatePerAgg +AggStatePerGroup +AggStatePerHash +AggStatePerPhase +AggStatePerTrans +AggStrategy +AggTransInfo +Aggref +AggregateInstrumentation +AlenState +Alias +AllocBlock +AllocChunk +AllocPointer +AllocSet +AllocSetContext +AllocSetFreeList +AllocateDesc +AllocateDescKind +AlterCollationStmt +AlterDatabaseRefreshCollStmt +AlterDatabaseSetStmt +AlterDatabaseStmt +AlterDefaultPrivilegesStmt +AlterDomainStmt +AlterEnumStmt +AlterEventTrigStmt +AlterExtensionContentsStmt +AlterExtensionStmt +AlterFdwStmt +AlterForeignServerStmt +AlterFunctionStmt +AlterObjectDependsStmt +AlterObjectSchemaStmt +AlterOpFamilyStmt +AlterOperatorStmt +AlterOwnerStmt +AlterPolicyStmt +AlterPublicationAction +AlterPublicationStmt +AlterRoleSetStmt +AlterRoleStmt +AlterSeqStmt +AlterStatsStmt +AlterSubscriptionStmt +AlterSubscriptionType +AlterSystemStmt +AlterTSConfigType +AlterTSConfigurationStmt +AlterTSDictionaryStmt +AlterTableCmd +AlterTableMoveAllStmt +AlterTableSpaceOptionsStmt +AlterTableStmt +AlterTableType +AlterTableUtilityContext +AlterTypeRecurseParams +AlterTypeStmt +AlterUserMappingStmt +AlteredTableInfo +AlternativeSubPlan +AmcheckOptions +AnalyzeAttrComputeStatsFunc +AnalyzeAttrFetchFunc +AnalyzeForeignTable_function +AnlExprData +AnlIndexData +AnyArrayType +Append +AppendPath +AppendRelInfo +AppendState +ApplyErrorCallbackArg +ApplyExecutionData +ApplySubXactData +Archive +ArchiveCheckConfiguredCB +ArchiveEntryPtrType +ArchiveFileCB +ArchiveFormat +ArchiveHandle +ArchiveMode +ArchiveModuleCallbacks +ArchiveModuleInit +ArchiveOpts +ArchiveShutdownCB +ArchiveStreamState +ArchiverOutput +ArchiverStage +ArrayAnalyzeExtraData +ArrayBuildState +ArrayBuildStateAny +ArrayBuildStateArr +ArrayCoerceExpr +ArrayConstIterState +ArrayExpr +ArrayExprIterState +ArrayIOData +ArrayIterator +ArrayMapState +ArrayMetaState +ArrayParseState +ArraySubWorkspace +ArrayType +AsyncQueueControl +AsyncQueueEntry +AsyncRequest +AttInMetadata +AttStatsSlot +AttoptCacheEntry +AttoptCacheKey +AttrDefInfo +AttrDefault +AttrMap +AttrMissing +AttrNumber +AttributeOpts +AuthRequest +AuthToken +AutoPrewarmSharedState +AutoVacOpts +AutoVacuumShmemStruct +AutoVacuumWorkItem +AutoVacuumWorkItemType +AuxProcType +BF_ctx +BF_key +BF_word +BF_word_signed +BIGNUM +BIO +BIO_METHOD +BITVECP +BMS_Comparison +BMS_Membership +BN_CTX +BOOL +BOOLEAN +BOX +BTArrayKeyInfo +BTBuildState +BTCycleId +BTDedupInterval +BTDedupState +BTDedupStateData +BTDeletedPageData +BTIndexStat +BTInsertState +BTInsertStateData +BTLeader +BTMetaPageData +BTOneVacInfo +BTOptions +BTPS_State +BTPageOpaque +BTPageOpaqueData +BTPageStat +BTPageState +BTParallelScanDesc +BTPendingFSM +BTScanInsert +BTScanInsertData +BTScanOpaque +BTScanOpaqueData +BTScanPos +BTScanPosData +BTScanPosItem +BTShared +BTSortArrayContext +BTSpool +BTStack +BTStackData +BTVacInfo +BTVacState +BTVacuumPosting +BTVacuumPostingData +BTWriteState +BUF_MEM +BYTE +BY_HANDLE_FILE_INFORMATION +Backend +BackendId +BackendParameters +BackendState +BackendType +BackgroundWorker +BackgroundWorkerArray +BackgroundWorkerHandle +BackgroundWorkerSlot +Barrier +BaseBackupCmd +BaseBackupTargetHandle +BaseBackupTargetType +BeginDirectModify_function +BeginForeignInsert_function +BeginForeignModify_function +BeginForeignScan_function +BeginSampleScan_function +BernoulliSamplerData +BgWorkerStartTime +BgwHandleStatus +BinaryArithmFunc +BindParamCbData +BipartiteMatchState +BitString +BitmapAnd +BitmapAndPath +BitmapAndState +BitmapHeapPath +BitmapHeapScan +BitmapHeapScanState +BitmapIndexScan +BitmapIndexScanState +BitmapOr +BitmapOrPath +BitmapOrState +Bitmapset +BlobInfo +Block +BlockId +BlockIdData +BlockInfoRecord +BlockNumber +BlockSampler +BlockSamplerData +BlockedProcData +BlockedProcsData +BloomBuildState +BloomFilter +BloomMetaPageData +BloomOpaque +BloomOptions +BloomPageOpaque +BloomPageOpaqueData +BloomScanOpaque +BloomScanOpaqueData +BloomSignatureWord +BloomState +BloomTuple +BoolAggState +BoolExpr +BoolExprType +BoolTestType +Boolean +BooleanTest +BpChar +BrinBuildState +BrinDesc +BrinMemTuple +BrinMetaPageData +BrinOpaque +BrinOpcInfo +BrinOptions +BrinRevmap +BrinSpecialSpace +BrinStatsData +BrinTuple +BrinValues +BtreeCheckState +BtreeLevel +Bucket +BufFile +Buffer +BufferAccessStrategy +BufferAccessStrategyType +BufferCachePagesContext +BufferCachePagesRec +BufferDesc +BufferDescPadded +BufferHeapTupleTableSlot +BufferLookupEnt +BufferStrategyControl +BufferTag +BufferUsage +BuildAccumulator +BuiltinScript +BulkInsertState +BulkInsertStateData +CACHESIGN +CAC_state +CCFastEqualFN +CCHashFN +CEOUC_WAIT_MODE +CFuncHashTabEntry +CHAR +CHECKPOINT +CHKVAL +CIRCLE +CMPDAffix +CONTEXT +COP +CRITICAL_SECTION +CRSSnapshotAction +CState +CTECycleClause +CTEMaterialize +CTESearchClause +CV +CachedExpression +CachedPlan +CachedPlanSource +CallContext +CallStmt +CancelRequestPacket +Cardinality +CaseExpr +CaseTestExpr +CaseWhen +Cash +CastInfo +CatCList +CatCTup +CatCache +CatCacheHeader +CatalogId +CatalogIdMapEntry +CatalogIndexState +ChangeVarNodes_context +CheckPoint +CheckPointStmt +CheckpointStatsData +CheckpointerRequest +CheckpointerShmemStruct +Chromosome +CkptSortItem +CkptTsStatus +ClientAuthentication_hook_type +ClientCertMode +ClientCertName +ClientData +ClonePtrType +ClosePortalStmt +ClosePtrType +Clump +ClusterInfo +ClusterParams +ClusterStmt +CmdType +CoalesceExpr +CoerceParamHook +CoerceToDomain +CoerceToDomainValue +CoerceViaIO +CoercionContext +CoercionForm +CoercionPathType +CollAliasData +CollInfo +CollateClause +CollateExpr +CollateStrength +CollectedATSubcmd +CollectedCommand +CollectedCommandType +ColorTrgm +ColorTrgmInfo +ColumnCompareData +ColumnDef +ColumnIOData +ColumnRef +ColumnsHashData +CombinationGenerator +ComboCidEntry +ComboCidEntryData +ComboCidKey +ComboCidKeyData +Command +CommandDest +CommandId +CommandTag +CommandTagBehavior +CommentItem +CommentStmt +CommitTimestampEntry +CommitTimestampShared +CommonEntry +CommonTableExpr +CompareScalarsContext +CompiledExprState +CompositeIOData +CompositeTypeStmt +CompoundAffixFlag +CompressionAlgorithm +CompressionLocation +CompressorState +ComputeXidHorizonsResult +ConditionVariable +ConditionVariableMinimallyPadded +ConditionalStack +ConfigData +ConfigVariable +ConnCacheEntry +ConnCacheKey +ConnParams +ConnStatusType +ConnType +ConnectionStateEnum +ConsiderSplitContext +Const +ConstrCheck +ConstrType +Constraint +ConstraintCategory +ConstraintInfo +ConstraintsSetStmt +ControlData +ControlFileData +ConvInfo +ConvProcInfo +ConversionLocation +ConvertRowtypeExpr +CookedConstraint +CopyDest +CopyFormatOptions +CopyFromState +CopyFromStateData +CopyHeaderChoice +CopyInsertMethod +CopyMultiInsertBuffer +CopyMultiInsertInfo +CopySource +CopyStmt +CopyToState +CopyToStateData +Cost +CostSelector +Counters +CoverExt +CoverPos +CreateAmStmt +CreateCastStmt +CreateConversionStmt +CreateDBRelInfo +CreateDBStrategy +CreateDomainStmt +CreateEnumStmt +CreateEventTrigStmt +CreateExtensionStmt +CreateFdwStmt +CreateForeignServerStmt +CreateForeignTableStmt +CreateFunctionStmt +CreateOpClassItem +CreateOpClassStmt +CreateOpFamilyStmt +CreatePLangStmt +CreatePolicyStmt +CreatePublicationStmt +CreateRangeStmt +CreateReplicationSlotCmd +CreateRoleStmt +CreateSchemaStmt +CreateSchemaStmtContext +CreateSeqStmt +CreateStatsStmt +CreateStmt +CreateStmtContext +CreateSubscriptionStmt +CreateTableAsStmt +CreateTableSpaceStmt +CreateTransformStmt +CreateTrigStmt +CreateUserMappingStmt +CreatedbStmt +CredHandle +CteItem +CteScan +CteScanState +CteState +CtlCommand +CtxtHandle +CurrentOfExpr +CustomExecMethods +CustomOutPtrType +CustomPath +CustomScan +CustomScanMethods +CustomScanState +CycleCtr +DBState +DCHCacheEntry +DEADLOCK_INFO +DECountItem +DH +DIR +DNSServiceErrorType +DNSServiceRef +DR_copy +DR_intorel +DR_printtup +DR_sqlfunction +DR_transientrel +DSA +DWORD +DataDumperPtr +DataPageDeleteStack +DatabaseInfo +DateADT +Datum +DatumTupleFields +DbInfo +DbInfoArr +DeClonePtrType +DeadLockState +DeallocateStmt +DeclareCursorStmt +DecodedBkpBlock +DecodedXLogRecord +DecodingOutputState +DefElem +DefElemAction +DefaultACLInfo +DefineStmt +DeleteStmt +DependencyGenerator +DependencyGeneratorData +DependencyType +DestReceiver +DictISpell +DictInt +DictSimple +DictSnowball +DictSubState +DictSyn +DictThesaurus +DimensionInfo +DirectoryMethodData +DirectoryMethodFile +DisableTimeoutParams +DiscardMode +DiscardStmt +DistanceValue +DistinctExpr +DoStmt +DocRepresentation +DomainConstraintCache +DomainConstraintRef +DomainConstraintState +DomainConstraintType +DomainIOData +DropBehavior +DropOwnedStmt +DropReplicationSlotCmd +DropRoleStmt +DropStmt +DropSubscriptionStmt +DropTableSpaceStmt +DropUserMappingStmt +DropdbStmt +DumpComponents +DumpId +DumpOptions +DumpSignalInformation +DumpableAcl +DumpableObject +DumpableObjectType +DumpableObjectWithAcl +DynamicFileList +DynamicZoneAbbrev +EC_KEY +EDGE +ENGINE +EOM_flatten_into_method +EOM_get_flat_size_method +EPQState +EPlan +EState +EStatus +EVP_CIPHER +EVP_CIPHER_CTX +EVP_MD +EVP_MD_CTX +EVP_PKEY +EachState +Edge +EditableObjectType +ElementsState +EnableTimeoutParams +EndBlobPtrType +EndBlobsPtrType +EndDataPtrType +EndDirectModify_function +EndForeignInsert_function +EndForeignModify_function +EndForeignScan_function +EndOfWalRecoveryInfo +EndSampleScan_function +EnumItem +EolType +EphemeralNameRelationType +EphemeralNamedRelation +EphemeralNamedRelationData +EphemeralNamedRelationMetadata +EphemeralNamedRelationMetadataData +EquivalenceClass +EquivalenceMember +ErrorContextCallback +ErrorData +EstimateDSMForeignScan_function +EstimationInfo +EventTriggerCacheEntry +EventTriggerCacheItem +EventTriggerCacheStateType +EventTriggerData +EventTriggerEvent +EventTriggerInfo +EventTriggerQueryState +ExceptionLabelMap +ExceptionMap +ExecAuxRowMark +ExecEvalBoolSubroutine +ExecEvalJsonExprContext +ExecEvalSubroutine +ExecForeignBatchInsert_function +ExecForeignDelete_function +ExecForeignInsert_function +ExecForeignTruncate_function +ExecForeignUpdate_function +ExecParallelEstimateContext +ExecParallelInitializeDSMContext +ExecPhraseData +ExecProcNodeMtd +ExecRowMark +ExecScanAccessMtd +ExecScanRecheckMtd +ExecStatus +ExecStatusType +ExecuteStmt +ExecutorCheckPerms_hook_type +ExecutorEnd_hook_type +ExecutorFinish_hook_type +ExecutorRun_hook_type +ExecutorStart_hook_type +ExpandedArrayHeader +ExpandedObjectHeader +ExpandedObjectMethods +ExpandedRange +ExpandedRecordFieldInfo +ExpandedRecordHeader +ExplainDirectModify_function +ExplainForeignModify_function +ExplainForeignScan_function +ExplainFormat +ExplainOneQuery_hook_type +ExplainState +ExplainStmt +ExplainWorkersState +ExportedSnapshot +Expr +ExprContext +ExprContextCallbackFunction +ExprContext_CB +ExprDoneCond +ExprEvalOp +ExprEvalOpLookup +ExprEvalRowtypeCache +ExprEvalStep +ExprState +ExprStateEvalFunc +ExtensibleNode +ExtensibleNodeEntry +ExtensibleNodeMethods +ExtensionControlFile +ExtensionInfo +ExtensionVersionInfo +FDWCollateState +FD_SET +FILE +FILETIME +FPI +FSMAddress +FSMPage +FSMPageData +FakeRelCacheEntry +FakeRelCacheEntryData +FastPathStrongRelationLockData +FdwInfo +FdwRoutine +FetchDirection +FetchStmt +FieldSelect +FieldStore +File +FileFdwExecutionState +FileFdwPlanState +FileNameMap +FileSet +FileTag +FinalPathExtraData +FindColsContext +FindSplitData +FindSplitStrat +FixedParallelExecutorState +FixedParallelState +FixedParamState +FlagMode +Float +FlushPosition +FmgrBuiltin +FmgrHookEventType +FmgrInfo +ForBothCellState +ForBothState +ForEachState +ForFiveState +ForFourState +ForThreeState +ForeignAsyncConfigureWait_function +ForeignAsyncNotify_function +ForeignAsyncRequest_function +ForeignDataWrapper +ForeignKeyCacheInfo +ForeignKeyOptInfo +ForeignPath +ForeignScan +ForeignScanState +ForeignServer +ForeignServerInfo +ForeignTable +ForeignTruncateInfo +ForkNumber +FormData_pg_aggregate +FormData_pg_am +FormData_pg_amop +FormData_pg_amproc +FormData_pg_attrdef +FormData_pg_attribute +FormData_pg_auth_members +FormData_pg_authid +FormData_pg_cast +FormData_pg_class +FormData_pg_collation +FormData_pg_constraint +FormData_pg_conversion +FormData_pg_database +FormData_pg_default_acl +FormData_pg_depend +FormData_pg_enum +FormData_pg_event_trigger +FormData_pg_extension +FormData_pg_foreign_data_wrapper +FormData_pg_foreign_server +FormData_pg_foreign_table +FormData_pg_index +FormData_pg_inherits +FormData_pg_language +FormData_pg_largeobject +FormData_pg_largeobject_metadata +FormData_pg_namespace +FormData_pg_opclass +FormData_pg_operator +FormData_pg_opfamily +FormData_pg_partitioned_table +FormData_pg_policy +FormData_pg_proc +FormData_pg_publication +FormData_pg_publication_namespace +FormData_pg_publication_rel +FormData_pg_range +FormData_pg_replication_origin +FormData_pg_rewrite +FormData_pg_sequence +FormData_pg_sequence_data +FormData_pg_shdepend +FormData_pg_statistic +FormData_pg_statistic_ext +FormData_pg_statistic_ext_data +FormData_pg_subscription +FormData_pg_subscription_rel +FormData_pg_tablespace +FormData_pg_transform +FormData_pg_trigger +FormData_pg_ts_config +FormData_pg_ts_config_map +FormData_pg_ts_dict +FormData_pg_ts_parser +FormData_pg_ts_template +FormData_pg_type +FormData_pg_user_mapping +Form_pg_aggregate +Form_pg_am +Form_pg_amop +Form_pg_amproc +Form_pg_attrdef +Form_pg_attribute +Form_pg_auth_members +Form_pg_authid +Form_pg_cast +Form_pg_class +Form_pg_collation +Form_pg_constraint +Form_pg_conversion +Form_pg_database +Form_pg_default_acl +Form_pg_depend +Form_pg_enum +Form_pg_event_trigger +Form_pg_extension +Form_pg_foreign_data_wrapper +Form_pg_foreign_server +Form_pg_foreign_table +Form_pg_index +Form_pg_inherits +Form_pg_language +Form_pg_largeobject +Form_pg_largeobject_metadata +Form_pg_namespace +Form_pg_opclass +Form_pg_operator +Form_pg_opfamily +Form_pg_partitioned_table +Form_pg_policy +Form_pg_proc +Form_pg_publication +Form_pg_publication_namespace +Form_pg_publication_rel +Form_pg_range +Form_pg_replication_origin +Form_pg_rewrite +Form_pg_sequence +Form_pg_sequence_data +Form_pg_shdepend +Form_pg_statistic +Form_pg_statistic_ext +Form_pg_statistic_ext_data +Form_pg_subscription +Form_pg_subscription_rel +Form_pg_tablespace +Form_pg_transform +Form_pg_trigger +Form_pg_ts_config +Form_pg_ts_config_map +Form_pg_ts_dict +Form_pg_ts_parser +Form_pg_ts_template +Form_pg_type +Form_pg_user_mapping +FormatNode +FreeBlockNumberArray +FreeListData +FreePageBtree +FreePageBtreeHeader +FreePageBtreeInternalKey +FreePageBtreeLeafKey +FreePageBtreeSearchResult +FreePageManager +FreePageSpanLeader +FromCharDateMode +FromExpr +FullTransactionId +FuncCall +FuncCallContext +FuncCandidateList +FuncDetailCode +FuncExpr +FuncInfo +FuncLookupError +FunctionCallInfo +FunctionCallInfoBaseData +FunctionParameter +FunctionParameterMode +FunctionScan +FunctionScanPerFuncState +FunctionScanState +FuzzyAttrMatchState +GBT_NUMKEY +GBT_NUMKEY_R +GBT_VARKEY +GBT_VARKEY_R +GENERAL_NAME +GISTBuildBuffers +GISTBuildState +GISTDeletedPageContents +GISTENTRY +GISTInsertStack +GISTInsertState +GISTIntArrayBigOptions +GISTIntArrayOptions +GISTNodeBuffer +GISTNodeBufferPage +GISTPageOpaque +GISTPageOpaqueData +GISTPageSplitInfo +GISTSTATE +GISTScanOpaque +GISTScanOpaqueData +GISTSearchHeapItem +GISTSearchItem +GISTTYPE +GIST_SPLITVEC +GMReaderTupleBuffer +GROUP +GV +Gather +GatherMerge +GatherMergePath +GatherMergeState +GatherPath +GatherState +Gene +GeneratePruningStepsContext +GenerationBlock +GenerationChunk +GenerationContext +GenerationPointer +GenericCosts +GenericXLogState +GeqoPrivateData +GetForeignJoinPaths_function +GetForeignModifyBatchSize_function +GetForeignPaths_function +GetForeignPlan_function +GetForeignRelSize_function +GetForeignRowMarkType_function +GetForeignUpperPaths_function +GetState +GiSTOptions +GinBtree +GinBtreeData +GinBtreeDataLeafInsertData +GinBtreeEntryInsertData +GinBtreeStack +GinBuildState +GinChkVal +GinEntries +GinEntryAccumulator +GinIndexStat +GinMetaPageData +GinNullCategory +GinOptions +GinPageOpaque +GinPageOpaqueData +GinPlaceToPageRC +GinPostingList +GinQualCounts +GinScanEntry +GinScanKey +GinScanOpaque +GinScanOpaqueData +GinState +GinStatsData +GinTernaryValue +GinTupleCollector +GinVacuumState +GistBuildMode +GistEntryVector +GistHstoreOptions +GistInetKey +GistNSN +GistOptBufferingMode +GistSortedBuildLevelState +GistSplitUnion +GistSplitVector +GistTsVectorOptions +GistVacState +GlobalTransaction +GlobalVisHorizonKind +GlobalVisState +GrantRoleStmt +GrantStmt +GrantTargetType +Group +GroupClause +GroupPath +GroupPathExtraData +GroupResultPath +GroupState +GroupVarInfo +GroupingFunc +GroupingSet +GroupingSetData +GroupingSetKind +GroupingSetsPath +GucAction +GucBoolAssignHook +GucBoolCheckHook +GucContext +GucEnumAssignHook +GucEnumCheckHook +GucIntAssignHook +GucIntCheckHook +GucRealAssignHook +GucRealCheckHook +GucShowHook +GucSource +GucStack +GucStackState +GucStringAssignHook +GucStringCheckHook +HANDLE +HASHACTION +HASHBUCKET +HASHCTL +HASHELEMENT +HASHHDR +HASHSEGMENT +HASH_SEQ_STATUS +HE +HEntry +HIST_ENTRY +HKEY +HLOCAL +HMAC_CTX +HMODULE +HOldEntry +HRESULT +HSParser +HSpool +HStore +HTAB +HTSV_Result +HV +Hash +HashAggBatch +HashAggSpill +HashAllocFunc +HashBuildState +HashCompareFunc +HashCopyFunc +HashIndexStat +HashInstrumentation +HashJoin +HashJoinState +HashJoinTable +HashJoinTuple +HashMemoryChunk +HashMetaPage +HashMetaPageData +HashOptions +HashPageOpaque +HashPageOpaqueData +HashPageStat +HashPath +HashScanOpaque +HashScanOpaqueData +HashScanPosData +HashScanPosItem +HashSkewBucket +HashState +HashValueFunc +HbaLine +HeadlineJsonState +HeadlineParsedText +HeadlineWordEntry +HeapCheckContext +HeapScanDesc +HeapTuple +HeapTupleData +HeapTupleFields +HeapTupleForceOption +HeapTupleHeader +HeapTupleHeaderData +HeapTupleTableSlot +HistControl +HotStandbyState +I32 +ICU_Convert_Func +ID +INFIX +INT128 +INTERFACE_INFO +IOFuncSelector +IPCompareMethod +ITEM +IV +IdentLine +IdentifierLookup +IdentifySystemCmd +IfStackElem +ImportForeignSchemaStmt +ImportForeignSchemaType +ImportForeignSchema_function +ImportQual +InProgressEnt +IncludeWal +InclusionOpaque +IncrementVarSublevelsUp_context +IncrementalSort +IncrementalSortExecutionStatus +IncrementalSortGroupInfo +IncrementalSortInfo +IncrementalSortPath +IncrementalSortState +Index +IndexAMProperty +IndexAmRoutine +IndexArrayKeyInfo +IndexAttachInfo +IndexAttrBitmapKind +IndexBuildCallback +IndexBuildResult +IndexBulkDeleteCallback +IndexBulkDeleteResult +IndexClause +IndexClauseSet +IndexDeleteCounts +IndexDeletePrefetchState +IndexElem +IndexFetchHeapData +IndexFetchTableData +IndexInfo +IndexList +IndexOnlyScan +IndexOnlyScanState +IndexOptInfo +IndexOrderByDistance +IndexPath +IndexRuntimeKeyInfo +IndexScan +IndexScanDesc +IndexScanState +IndexStateFlagsAction +IndexStmt +IndexTuple +IndexTupleData +IndexUniqueCheck +IndexVacuumInfo +IndxInfo +InferClause +InferenceElem +InfoItem +InhInfo +InheritableSocket +InitSampleScan_function +InitializeDSMForeignScan_function +InitializeWorkerForeignScan_function +InlineCodeBlock +InsertStmt +Instrumentation +Int128AggState +Int8TransTypeData +IntRBTreeNode +Integer +IntegerSet +InternalDefaultACL +InternalGrant +Interval +IntoClause +InvalMessageArray +InvalidationMsgsGroup +IpcMemoryId +IpcMemoryKey +IpcMemoryState +IpcSemaphoreId +IpcSemaphoreKey +IsForeignPathAsyncCapable_function +IsForeignRelUpdatable_function +IsForeignScanParallelSafe_function +IsoConnInfo +IspellDict +Item +ItemId +ItemIdData +ItemPointer +ItemPointerData +IterateDirectModify_function +IterateForeignScan_function +IterateJsonStringValuesState +JEntry +JHashState +JOBOBJECTINFOCLASS +JOBOBJECT_BASIC_LIMIT_INFORMATION +JOBOBJECT_BASIC_UI_RESTRICTIONS +JOBOBJECT_SECURITY_LIMIT_INFORMATION +JitContext +JitInstrumentation +JitProviderCallbacks +JitProviderCompileExprCB +JitProviderInit +JitProviderReleaseContextCB +JitProviderResetAfterErrorCB +Join +JoinCostWorkspace +JoinExpr +JoinHashEntry +JoinPath +JoinPathExtraData +JoinState +JoinType +JsObject +JsValue +JsonAggConstructor +JsonAggState +JsonArgument +JsonArrayAgg +JsonArrayConstructor +JsonArrayQueryConstructor +JsonBaseObjectInfo +JsonBehavior +JsonBehaviorType +JsonCoercion +JsonCommon +JsonConstructorExpr +JsonConstructorType +JsonEncoding +JsonExpr +JsonExprOp +JsonFormat +JsonFormatType +JsonFunc +JsonFuncExpr +JsonHashEntry +JsonIsPredicate +JsonItemCoercions +JsonIterateStringValuesAction +JsonKeyValue +JsonLexContext +JsonLikeRegexContext +JsonManifestFileField +JsonManifestParseContext +JsonManifestParseState +JsonManifestSemanticState +JsonManifestWALRangeField +JsonObjectAgg +JsonObjectConstructor +JsonOutput +JsonParseContext +JsonParseErrorType +JsonParseExpr +JsonPath +JsonPathBool +JsonPathDatatypeStatus +JsonPathExecContext +JsonPathExecResult +JsonPathGinAddPathItemFunc +JsonPathGinContext +JsonPathGinExtractNodesFunc +JsonPathGinNode +JsonPathGinNodeType +JsonPathGinPath +JsonPathGinPathItem +JsonPathItem +JsonPathItemType +JsonPathKeyword +JsonPathMutableContext +JsonPathParseItem +JsonPathParseResult +JsonPathPredicateCallback +JsonPathString +JsonPathVarCallback +JsonPathVariableEvalContext +JsonQuotes +JsonReturning +JsonScalarExpr +JsonSemAction +JsonTokenType +JsonTransformStringValuesAction +JsonTypeCategory +JsonUniqueBuilderState +JsonUniqueCheckState +JsonUniqueHashEntry +JsonUniqueParsingState +JsonUniqueStackEntry +JsonValueExpr +JsonValueList +JsonValueListIterator +JsonValueType +JsonWrapper +Jsonb +JsonbAggState +JsonbContainer +JsonbInState +JsonbIterState +JsonbIterator +JsonbIteratorToken +JsonbPair +JsonbParseState +JsonbSubWorkspace +JsonbTypeCategory +JsonbValue +JumbleState +JunkFilter +KeyAction +KeyActions +KeyArray +KeySuffix +KeyWord +LARGE_INTEGER +LDAP +LDAPMessage +LDAPURLDesc +LDAP_TIMEVAL +LINE +LLVMAttributeRef +LLVMBasicBlockRef +LLVMBuilderRef +LLVMIntPredicate +LLVMJitContext +LLVMJitHandle +LLVMMemoryBufferRef +LLVMModuleRef +LLVMOrcJITStackRef +LLVMOrcModuleHandle +LLVMOrcTargetAddress +LLVMPassManagerBuilderRef +LLVMPassManagerRef +LLVMSharedModuleRef +LLVMTargetMachineRef +LLVMTargetRef +LLVMTypeRef +LLVMValueRef +LOCALLOCK +LOCALLOCKOWNER +LOCALLOCKTAG +LOCALPREDICATELOCK +LOCK +LOCKMASK +LOCKMETHODID +LOCKMODE +LOCKTAG +LONG +LONG_PTR +LOOP +LPBYTE +LPCTSTR +LPCWSTR +LPDWORD +LPFILETIME +LPSECURITY_ATTRIBUTES +LPSERVICE_STATUS +LPSTR +LPTHREAD_START_ROUTINE +LPTSTR +LPVOID +LPWSTR +LSEG +LUID +LVPagePruneState +LVRelState +LVSavedErrInfo +LWLock +LWLockHandle +LWLockMode +LWLockPadded +LZ4F_compressionContext_t +LZ4F_decompressOptions_t +LZ4F_decompressionContext_t +LZ4F_errorCode_t +LZ4F_preferences_t +LabelProvider +LagTracker +LargeObjectDesc +LastAttnumInfo +Latch +LerpFunc +LexDescr +LexemeEntry +LexemeHashKey +LexemeInfo +LexemeKey +LexizeData +LibraryInfo +Limit +LimitOption +LimitPath +LimitState +LimitStateCond +List +ListCell +ListDictionary +ListParsedLex +ListenAction +ListenActionKind +ListenStmt +LoadStmt +LocalBufferLookupEnt +LocalPgBackendStatus +LocalTransactionId +LocationIndex +LocationLen +LockAcquireResult +LockClauseStrength +LockData +LockInfoData +LockInstanceData +LockMethod +LockMethodData +LockRelId +LockRows +LockRowsPath +LockRowsState +LockStmt +LockTagType +LockTupleMode +LockViewRecurse_context +LockWaitPolicy +LockingClause +LogOpts +LogStmtLevel +LogicalDecodeBeginCB +LogicalDecodeBeginPrepareCB +LogicalDecodeChangeCB +LogicalDecodeCommitCB +LogicalDecodeCommitPreparedCB +LogicalDecodeFilterByOriginCB +LogicalDecodeFilterPrepareCB +LogicalDecodeMessageCB +LogicalDecodePrepareCB +LogicalDecodeRollbackPreparedCB +LogicalDecodeShutdownCB +LogicalDecodeStartupCB +LogicalDecodeStreamAbortCB +LogicalDecodeStreamChangeCB +LogicalDecodeStreamCommitCB +LogicalDecodeStreamMessageCB +LogicalDecodeStreamPrepareCB +LogicalDecodeStreamStartCB +LogicalDecodeStreamStopCB +LogicalDecodeStreamTruncateCB +LogicalDecodeTruncateCB +LogicalDecodingContext +LogicalErrorCallbackState +LogicalOutputPluginInit +LogicalOutputPluginWriterPrepareWrite +LogicalOutputPluginWriterUpdateProgress +LogicalOutputPluginWriterWrite +LogicalRepBeginData +LogicalRepCommitData +LogicalRepCommitPreparedTxnData +LogicalRepCtxStruct +LogicalRepMsgType +LogicalRepPartMapEntry +LogicalRepPreparedTxnData +LogicalRepRelId +LogicalRepRelMapEntry +LogicalRepRelation +LogicalRepRollbackPreparedTxnData +LogicalRepTupleData +LogicalRepTyp +LogicalRepWorker +LogicalRewriteMappingData +LogicalTape +LogicalTapeSet +LsnReadQueue +LsnReadQueueNextFun +LsnReadQueueNextStatus +LtreeGistOptions +LtreeSignature +MAGIC +MBuf +MCVItem +MCVList +MEMORY_BASIC_INFORMATION +MGVTBL +MINIDUMPWRITEDUMP +MINIDUMP_TYPE +MJEvalResult +MTTargetRelLookup +MVDependencies +MVDependency +MVNDistinct +MVNDistinctItem +Material +MaterialPath +MaterialState +MdfdVec +Memoize +MemoizeEntry +MemoizeInstrumentation +MemoizeKey +MemoizePath +MemoizeState +MemoizeTuple +MemoryContext +MemoryContextCallback +MemoryContextCallbackFunction +MemoryContextCounters +MemoryContextData +MemoryContextMethods +MemoryStatsPrintFunc +MergeAction +MergeActionState +MergeAppend +MergeAppendPath +MergeAppendState +MergeJoin +MergeJoinClause +MergeJoinState +MergePath +MergeScanSelCache +MergeStmt +MergeWhenClause +MetaCommand +MinMaxAggInfo +MinMaxAggPath +MinMaxExpr +MinMaxMultiOptions +MinMaxOp +MinimalTuple +MinimalTupleData +MinimalTupleTableSlot +MinmaxMultiOpaque +MinmaxOpaque +ModifyTable +ModifyTableContext +ModifyTablePath +ModifyTableState +MonotonicFunction +MorphOpaque +MsgType +MultiAssignRef +MultiSortSupport +MultiSortSupportData +MultiXactId +MultiXactMember +MultiXactOffset +MultiXactStateData +MultiXactStatus +MultirangeIOData +MultirangeParseState +MultirangeType +NDBOX +NODE +NTSTATUS +NUMCacheEntry +NUMDesc +NUMProc +NV +Name +NameData +NameHashEntry +NamedArgExpr +NamedLWLockTranche +NamedLWLockTrancheRequest +NamedTuplestoreScan +NamedTuplestoreScanState +NamespaceInfo +NestLoop +NestLoopParam +NestLoopState +NestPath +NewColumnValue +NewConstraint +NextSampleBlock_function +NextSampleTuple_function +NextValueExpr +Node +NodeTag +NonEmptyRange +Notification +NotificationHash +NotificationList +NotifyStmt +Nsrt +NtDllRoutine +NullIfExpr +NullTest +NullTestType +NullableDatum +Numeric +NumericAggState +NumericDigit +NumericSortSupport +NumericSumAccum +NumericVar +OM_uint32 +OP +OSAPerGroupState +OSAPerQueryState +OSInfo +OSSLCipher +OSSLDigest +OVERLAPPED +ObjectAccessDrop +ObjectAccessNamespaceSearch +ObjectAccessPostAlter +ObjectAccessPostCreate +ObjectAccessType +ObjectAddress +ObjectAddressAndFlags +ObjectAddressExtra +ObjectAddressStack +ObjectAddresses +ObjectClass +ObjectPropertyType +ObjectType +ObjectWithArgs +Offset +OffsetNumber +OffsetVarNodes_context +Oid +OidOptions +OkeysState +OldSnapshotControlData +OldSnapshotTimeMapping +OldToNewMapping +OldToNewMappingData +OnCommitAction +OnCommitItem +OnConflictAction +OnConflictClause +OnConflictExpr +OnConflictSetState +OpBtreeInterpretation +OpClassCacheEnt +OpExpr +OpFamilyMember +OpFamilyOpFuncGroup +OpclassInfo +Operator +OperatorElement +OpfamilyInfo +OprCacheEntry +OprCacheKey +OprInfo +OprProofCacheEntry +OprProofCacheKey +OutputContext +OutputPluginCallbacks +OutputPluginOptions +OutputPluginOutputType +OverrideSearchPath +OverrideStackEntry +OverridingKind +PACE_HEADER +PACL +PATH +PBOOL +PCtxtHandle +PERL_CONTEXT +PERL_SI +PFN +PGAlignedBlock +PGAlignedXLogBlock +PGAsyncStatusType +PGCALL2 +PGChecksummablePage +PGContextVisibility +PGEvent +PGEventConnDestroy +PGEventConnReset +PGEventId +PGEventProc +PGEventRegister +PGEventResultCopy +PGEventResultCreate +PGEventResultDestroy +PGFInfoFunction +PGFileType +PGFunction +PGLZ_HistEntry +PGLZ_Strategy +PGMessageField +PGModuleMagicFunction +PGNoticeHooks +PGOutputData +PGOutputTxnData +PGPROC +PGP_CFB +PGP_Context +PGP_MPI +PGP_PubKey +PGP_S2K +PGPing +PGQueryClass +PGRUsage +PGSemaphore +PGSemaphoreData +PGShmemHeader +PGTargetServerType +PGTernaryBool +PGTransactionStatusType +PGVerbosity +PG_Locale_Strategy +PG_Lock_Status +PG_init_t +PGcancel +PGcmdQueueEntry +PGconn +PGdataValue +PGlobjfuncs +PGnotify +PGpipelineStatus +PGresAttDesc +PGresAttValue +PGresParamDesc +PGresult +PGresult_data +PHANDLE +PLAINTREE +PLAssignStmt +PLUID_AND_ATTRIBUTES +PLcword +PLpgSQL_case_when +PLpgSQL_condition +PLpgSQL_datum +PLpgSQL_datum_type +PLpgSQL_diag_item +PLpgSQL_exception +PLpgSQL_exception_block +PLpgSQL_execstate +PLpgSQL_expr +PLpgSQL_func_hashkey +PLpgSQL_function +PLpgSQL_getdiag_kind +PLpgSQL_if_elsif +PLpgSQL_label_type +PLpgSQL_nsitem +PLpgSQL_nsitem_type +PLpgSQL_plugin +PLpgSQL_promise_type +PLpgSQL_raise_option +PLpgSQL_raise_option_type +PLpgSQL_rec +PLpgSQL_recfield +PLpgSQL_resolve_option +PLpgSQL_row +PLpgSQL_stmt +PLpgSQL_stmt_assert +PLpgSQL_stmt_assign +PLpgSQL_stmt_block +PLpgSQL_stmt_call +PLpgSQL_stmt_case +PLpgSQL_stmt_close +PLpgSQL_stmt_commit +PLpgSQL_stmt_dynexecute +PLpgSQL_stmt_dynfors +PLpgSQL_stmt_execsql +PLpgSQL_stmt_exit +PLpgSQL_stmt_fetch +PLpgSQL_stmt_forc +PLpgSQL_stmt_foreach_a +PLpgSQL_stmt_fori +PLpgSQL_stmt_forq +PLpgSQL_stmt_fors +PLpgSQL_stmt_getdiag +PLpgSQL_stmt_if +PLpgSQL_stmt_loop +PLpgSQL_stmt_open +PLpgSQL_stmt_perform +PLpgSQL_stmt_raise +PLpgSQL_stmt_return +PLpgSQL_stmt_return_next +PLpgSQL_stmt_return_query +PLpgSQL_stmt_rollback +PLpgSQL_stmt_type +PLpgSQL_stmt_while +PLpgSQL_trigtype +PLpgSQL_type +PLpgSQL_type_type +PLpgSQL_var +PLpgSQL_variable +PLwdatum +PLword +PLyArrayToOb +PLyCursorObject +PLyDatumToOb +PLyDatumToObFunc +PLyExceptionEntry +PLyExecutionContext +PLyObToArray +PLyObToDatum +PLyObToDatumFunc +PLyObToDomain +PLyObToScalar +PLyObToTransform +PLyObToTuple +PLyObject_AsString_t +PLyPlanObject +PLyProcedure +PLyProcedureEntry +PLyProcedureKey +PLyResultObject +PLySRFState +PLySavedArgs +PLyScalarToOb +PLySubtransactionData +PLySubtransactionObject +PLyTransformToOb +PLyTupleToOb +PLyUnicode_FromStringAndSize_t +PLy_elog_impl_t +PMINIDUMP_CALLBACK_INFORMATION +PMINIDUMP_EXCEPTION_INFORMATION +PMINIDUMP_USER_STREAM_INFORMATION +PMSignalData +PMSignalReason +PMState +POLYGON +PQArgBlock +PQEnvironmentOption +PQExpBuffer +PQExpBufferData +PQcommMethods +PQconninfoOption +PQnoticeProcessor +PQnoticeReceiver +PQprintOpt +PQsslKeyPassHook_OpenSSL_type +PREDICATELOCK +PREDICATELOCKTAG +PREDICATELOCKTARGET +PREDICATELOCKTARGETTAG +PROCESS_INFORMATION +PROCLOCK +PROCLOCKTAG +PROC_HDR +PROC_QUEUE +PSID +PSID_AND_ATTRIBUTES +PSQL_COMP_CASE +PSQL_ECHO +PSQL_ECHO_HIDDEN +PSQL_ERROR_ROLLBACK +PTEntryArray +PTIterationArray +PTOKEN_PRIVILEGES +PTOKEN_USER +PULONG +PUTENVPROC +PVIndStats +PVIndVacStatus +PVOID +PVShared +PX_Alias +PX_Cipher +PX_Combo +PX_HMAC +PX_MD +Page +PageData +PageGistNSN +PageHeader +PageHeaderData +PageXLogRecPtr +PagetableEntry +Pairs +ParallelAppendState +ParallelBitmapHeapState +ParallelBlockTableScanDesc +ParallelBlockTableScanWorker +ParallelBlockTableScanWorkerData +ParallelCompletionPtr +ParallelContext +ParallelExecutorInfo +ParallelHashGrowth +ParallelHashJoinBatch +ParallelHashJoinBatchAccessor +ParallelHashJoinState +ParallelIndexScanDesc +ParallelReadyList +ParallelSlot +ParallelSlotArray +ParallelSlotResultHandler +ParallelState +ParallelTableScanDesc +ParallelTableScanDescData +ParallelVacuumState +ParallelWorkerContext +ParallelWorkerInfo +Param +ParamCompileHook +ParamExecData +ParamExternData +ParamFetchHook +ParamKind +ParamListInfo +ParamPathInfo +ParamRef +ParamsErrorCbData +ParentMapEntry +ParseCallbackState +ParseExprKind +ParseNamespaceColumn +ParseNamespaceItem +ParseParamRefHook +ParseState +ParsedLex +ParsedScript +ParsedText +ParsedWord +ParserSetupHook +ParserState +PartClauseInfo +PartClauseMatchStatus +PartClauseTarget +PartitionBoundInfo +PartitionBoundInfoData +PartitionBoundSpec +PartitionCmd +PartitionDesc +PartitionDescData +PartitionDirectory +PartitionDirectoryEntry +PartitionDispatch +PartitionElem +PartitionHashBound +PartitionKey +PartitionListValue +PartitionMap +PartitionPruneCombineOp +PartitionPruneContext +PartitionPruneInfo +PartitionPruneState +PartitionPruneStep +PartitionPruneStepCombine +PartitionPruneStepOp +PartitionPruningData +PartitionRangeBound +PartitionRangeDatum +PartitionRangeDatumKind +PartitionScheme +PartitionSpec +PartitionTupleRouting +PartitionedRelPruneInfo +PartitionedRelPruningData +PartitionwiseAggregateType +PasswordType +Path +PathClauseUsage +PathCostComparison +PathHashStack +PathKey +PathKeyInfo +PathKeysComparison +PathTarget +PathkeyMutatorState +PathkeySortCost +PatternInfo +PatternInfoArray +Pattern_Prefix_Status +Pattern_Type +PendingFsyncEntry +PendingRelDelete +PendingRelSync +PendingUnlinkEntry +PendingWriteback +PerLockTagEntry +PerlInterpreter +Perl_ppaddr_t +Permutation +PermutationStep +PermutationStepBlocker +PermutationStepBlockerType +PgArchData +PgBackendGSSStatus +PgBackendSSLStatus +PgBackendStatus +PgBenchExpr +PgBenchExprLink +PgBenchExprList +PgBenchExprType +PgBenchFunction +PgBenchValue +PgBenchValueType +PgChecksumMode +PgFdwAnalyzeState +PgFdwConnState +PgFdwDirectModifyState +PgFdwModifyState +PgFdwOption +PgFdwPathExtraData +PgFdwRelationInfo +PgFdwScanState +PgIfAddrCallback +PgStatShared_Archiver +PgStatShared_BgWriter +PgStatShared_Checkpointer +PgStatShared_Common +PgStatShared_Database +PgStatShared_Function +PgStatShared_HashEntry +PgStatShared_Relation +PgStatShared_ReplSlot +PgStatShared_SLRU +PgStatShared_Subscription +PgStatShared_Wal +PgStat_ArchiverStats +PgStat_BackendFunctionEntry +PgStat_BackendSubEntry +PgStat_BgWriterStats +PgStat_CheckpointerStats +PgStat_Counter +PgStat_EntryRef +PgStat_EntryRefHashEntry +PgStat_FetchConsistency +PgStat_FunctionCallUsage +PgStat_FunctionCounts +PgStat_HashKey +PgStat_Kind +PgStat_KindInfo +PgStat_LocalState +PgStat_PendingDroppedStatsItem +PgStat_SLRUStats +PgStat_ShmemControl +PgStat_Snapshot +PgStat_SnapshotEntry +PgStat_StatDBEntry +PgStat_StatFuncEntry +PgStat_StatReplSlotEntry +PgStat_StatSubEntry +PgStat_StatTabEntry +PgStat_SubXactStatus +PgStat_TableCounts +PgStat_TableStatus +PgStat_TableXactStatus +PgStat_WalStats +PgXmlErrorContext +PgXmlStrictness +Pg_finfo_record +Pg_magic_struct +PipeProtoChunk +PipeProtoHeader +PlaceHolderInfo +PlaceHolderVar +Plan +PlanDirectModify_function +PlanForeignModify_function +PlanInvalItem +PlanRowMark +PlanState +PlannedStmt +PlannerGlobal +PlannerInfo +PlannerParamItem +Point +Pointer +PolicyInfo +PolyNumAggState +Pool +PopulateArrayContext +PopulateArrayState +PopulateRecordCache +PopulateRecordsetState +Port +Portal +PortalHashEnt +PortalStatus +PortalStrategy +PostParseColumnRefHook +PostgresPollingStatusType +PostingItem +PostponedQual +PreParseColumnRefHook +PredClass +PredIterInfo +PredIterInfoData +PredXactList +PredXactListElement +PredicateLockData +PredicateLockTargetType +PrefetchBufferResult +PrepParallelRestorePtrType +PrepareStmt +PreparedStatement +PresortedKeyData +PrewarmType +PrintExtraTocPtrType +PrintTocDataPtrType +PrintfArgType +PrintfArgValue +PrintfTarget +PrinttupAttrInfo +PrivTarget +PrivateRefCountEntry +ProcArrayStruct +ProcLangInfo +ProcSignalBarrierType +ProcSignalHeader +ProcSignalReason +ProcSignalSlot +ProcState +ProcWaitStatus +ProcessUtilityContext +ProcessUtility_hook_type +ProcessingMode +ProgressCommandType +ProjectSet +ProjectSetPath +ProjectSetState +ProjectionInfo +ProjectionPath +PromptInterruptContext +ProtocolVersion +PrsStorage +PruneState +PruneStepResult +PsqlScanCallbacks +PsqlScanQuoteType +PsqlScanResult +PsqlScanState +PsqlScanStateData +PsqlSettings +Publication +PublicationActions +PublicationDesc +PublicationInfo +PublicationObjSpec +PublicationObjSpecType +PublicationPartOpt +PublicationRelInfo +PublicationSchemaInfo +PublicationTable +PullFilter +PullFilterOps +PushFilter +PushFilterOps +PushFunction +PyCFunction +PyMappingMethods +PyMethodDef +PyModuleDef +PyObject +PySequenceMethods +PyTypeObject +Py_ssize_t +QPRS_STATE +QTN2QTState +QTNode +QUERYTYPE +QUERY_SECURITY_CONTEXT_TOKEN_FN +QualCost +QualItem +Query +QueryCompletion +QueryDesc +QueryEnvironment +QueryInfo +QueryItem +QueryItemType +QueryMode +QueryOperand +QueryOperator +QueryRepresentation +QueryRepresentationOperand +QuerySource +QueueBackendStatus +QueuePosition +QuitSignalReason +RBTNode +RBTOrderControl +RBTree +RBTreeIterator +REPARSE_JUNCTION_DATA_BUFFER +RIX +RI_CompareHashEntry +RI_CompareKey +RI_ConstraintInfo +RI_QueryHashEntry +RI_QueryKey +RTEKind +RWConflict +RWConflictPoolHeader +Range +RangeBound +RangeBox +RangeFunction +RangeIOData +RangeQueryClause +RangeSubselect +RangeTableFunc +RangeTableFuncCol +RangeTableSample +RangeTblEntry +RangeTblFunction +RangeTblRef +RangeType +RangeVar +RangeVarGetRelidCallback +Ranges +RawColumnDefault +RawParseMode +RawStmt +ReInitializeDSMForeignScan_function +ReScanForeignScan_function +ReadBufPtrType +ReadBufferMode +ReadBytePtrType +ReadExtraTocPtrType +ReadFunc +ReadLocalXLogPageNoWaitPrivate +ReadReplicationSlotCmd +ReassignOwnedStmt +RecheckForeignScan_function +RecordCacheEntry +RecordCompareData +RecordIOData +RecoveryLockListsEntry +RecoveryPauseState +RecoveryState +RecoveryTargetTimeLineGoal +RecoveryTargetType +RectBox +RecursionContext +RecursiveUnion +RecursiveUnionPath +RecursiveUnionState +RefetchForeignRow_function +RefreshMatViewStmt +RegProcedure +Regis +RegisNode +RegisteredBgWorker +ReindexErrorInfo +ReindexIndexInfo +ReindexObjectType +ReindexParams +ReindexStmt +ReindexType +RelFileNode +RelFileNodeBackend +RelIdCacheEnt +RelInfo +RelInfoArr +RelMapFile +RelMapping +RelOptInfo +RelOptKind +RelToCheck +RelToCluster +RelabelType +Relation +RelationData +RelationInfo +RelationPtr +RelationSyncEntry +RelcacheCallbackFunction +ReleaseMatchCB +RelfilenodeMapEntry +RelfilenodeMapKey +Relids +RelocationBufferInfo +RelptrFreePageBtree +RelptrFreePageManager +RelptrFreePageSpanLeader +RenameStmt +ReopenPtrType +ReorderBuffer +ReorderBufferApplyChangeCB +ReorderBufferApplyTruncateCB +ReorderBufferBeginCB +ReorderBufferChange +ReorderBufferChangeType +ReorderBufferCommitCB +ReorderBufferCommitPreparedCB +ReorderBufferDiskChange +ReorderBufferIterTXNEntry +ReorderBufferIterTXNState +ReorderBufferMessageCB +ReorderBufferPrepareCB +ReorderBufferRollbackPreparedCB +ReorderBufferStreamAbortCB +ReorderBufferStreamChangeCB +ReorderBufferStreamCommitCB +ReorderBufferStreamMessageCB +ReorderBufferStreamPrepareCB +ReorderBufferStreamStartCB +ReorderBufferStreamStopCB +ReorderBufferStreamTruncateCB +ReorderBufferTXN +ReorderBufferTXNByIdEnt +ReorderBufferToastEnt +ReorderBufferTupleBuf +ReorderBufferTupleCidEnt +ReorderBufferTupleCidKey +ReorderTuple +RepOriginId +ReparameterizeForeignPathByChild_function +ReplaceVarsFromTargetList_context +ReplaceVarsNoMatchOption +ReplicaIdentityStmt +ReplicationKind +ReplicationSlot +ReplicationSlotCtlData +ReplicationSlotOnDisk +ReplicationSlotPersistency +ReplicationSlotPersistentData +ReplicationState +ReplicationStateCtl +ReplicationStateOnDisk +ResTarget +ReservoirState +ReservoirStateData +ResourceArray +ResourceOwner +ResourceReleaseCallback +ResourceReleaseCallbackItem +ResourceReleasePhase +RestoreOptions +RestorePass +RestrictInfo +Result +ResultRelInfo +ResultState +ReturnSetInfo +ReturnStmt +RevmapContents +RewriteMappingDataEntry +RewriteMappingFile +RewriteRule +RewriteState +RmgrData +RmgrDescData +RmgrId +RoleNameItem +RoleSpec +RoleSpecType +RoleStmtType +RollupData +RowCompareExpr +RowCompareType +RowExpr +RowIdentityVarInfo +RowMarkClause +RowMarkType +RowSecurityDesc +RowSecurityPolicy +RtlGetLastNtStatus_t +RuleInfo +RuleLock +RuleStmt +RunningTransactions +RunningTransactionsData +SC_HANDLE +SECURITY_ATTRIBUTES +SECURITY_STATUS +SEG +SERIALIZABLEXACT +SERIALIZABLEXID +SERIALIZABLEXIDTAG +SERVICE_STATUS +SERVICE_STATUS_HANDLE +SERVICE_TABLE_ENTRY +SHM_QUEUE +SID_AND_ATTRIBUTES +SID_IDENTIFIER_AUTHORITY +SID_NAME_USE +SISeg +SIZE_T +SMgrRelation +SMgrRelationData +SMgrSortArray +SOCKADDR +SOCKET +SPELL +SPICallbackArg +SPIExecuteOptions +SPIParseOpenOptions +SPIPlanPtr +SPIPrepareOptions +SPITupleTable +SPLITCOST +SPNode +SPNodeData +SPPageDesc +SQLDropObject +SQLFunctionCache +SQLFunctionCachePtr +SQLFunctionParseInfo +SQLFunctionParseInfoPtr +SQLValueFunction +SQLValueFunctionOp +SSL +SSLExtensionInfoContext +SSL_CTX +STARTUPINFO +STRLEN +SV +SYNCHRONIZATION_BARRIER +SampleScan +SampleScanGetSampleSize_function +SampleScanState +SavedTransactionCharacteristics +ScalarArrayOpExpr +ScalarArrayOpExprHashEntry +ScalarArrayOpExprHashTable +ScalarIOData +ScalarItem +ScalarMCVItem +Scan +ScanDirection +ScanKey +ScanKeyData +ScanKeywordHashFunc +ScanKeywordList +ScanState +ScanTypeControl +ScannerCallbackState +SchemaQuery +SecBuffer +SecBufferDesc +SecLabelItem +SecLabelStmt +SeenRelsEntry +SelectLimit +SelectStmt +Selectivity +SemTPadded +SemiAntiJoinFactors +SeqScan +SeqScanState +SeqTable +SeqTableData +SerCommitSeqNo +SerialControl +SerializableXactHandle +SerializedActiveRelMaps +SerializedRanges +SerializedReindexState +SerializedSnapshotData +SerializedTransactionState +Session +SessionBackupState +SessionEndType +SetConstraintState +SetConstraintStateData +SetConstraintTriggerData +SetExprState +SetFunctionReturnMode +SetOp +SetOpCmd +SetOpPath +SetOpState +SetOpStatePerGroup +SetOpStrategy +SetOperation +SetOperationStmt +SetQuantifier +SetToDefault +SetupWorkerPtrType +ShDependObjectInfo +SharedAggInfo +SharedBitmapState +SharedDependencyObjectType +SharedDependencyType +SharedExecutorInstrumentation +SharedFileSet +SharedHashInfo +SharedIncrementalSortInfo +SharedInvalCatalogMsg +SharedInvalCatcacheMsg +SharedInvalRelcacheMsg +SharedInvalRelmapMsg +SharedInvalSmgrMsg +SharedInvalSnapshotMsg +SharedInvalidationMessage +SharedJitInstrumentation +SharedMemoizeInfo +SharedRecordTableEntry +SharedRecordTableKey +SharedRecordTypmodRegistry +SharedSortInfo +SharedTuplestore +SharedTuplestoreAccessor +SharedTuplestoreChunk +SharedTuplestoreParticipant +SharedTypmodTableEntry +Sharedsort +ShellTypeInfo +ShippableCacheEntry +ShippableCacheKey +ShmemIndexEnt +ShutdownForeignScan_function +ShutdownInformation +ShutdownMode +SignTSVector +SimpleActionList +SimpleActionListCell +SimpleEcontextStackEntry +SimpleOidList +SimpleOidListCell +SimplePtrList +SimplePtrListCell +SimpleStats +SimpleStringList +SimpleStringListCell +SingleBoundSortItem +Size +SkipPages +SlabBlock +SlabChunk +SlabContext +SlabSlot +SlotNumber +SlruCtl +SlruCtlData +SlruErrorCause +SlruPageStatus +SlruScanCallback +SlruShared +SlruSharedData +SlruWriteAll +SlruWriteAllData +SnapBuild +SnapBuildOnDisk +SnapBuildState +Snapshot +SnapshotData +SnapshotType +SockAddr +Sort +SortBy +SortByDir +SortByNulls +SortCoordinate +SortGroupClause +SortItem +SortPath +SortShimExtra +SortState +SortSupport +SortSupportData +SortTuple +SortTupleComparator +SortedPoint +SpGistBuildState +SpGistCache +SpGistDeadTuple +SpGistDeadTupleData +SpGistInnerTuple +SpGistInnerTupleData +SpGistLUPCache +SpGistLastUsedPage +SpGistLeafTuple +SpGistLeafTupleData +SpGistMetaPageData +SpGistNodeTuple +SpGistNodeTupleData +SpGistOptions +SpGistPageOpaque +SpGistPageOpaqueData +SpGistScanOpaque +SpGistScanOpaqueData +SpGistSearchItem +SpGistState +SpGistTypeDesc +SpecialJoinInfo +SpinDelayStatus +SplitInterval +SplitLR +SplitPoint +SplitTextOutputData +SplitVar +SplitedPageLayout +StackElem +StartBlobPtrType +StartBlobsPtrType +StartDataPtrType +StartReplicationCmd +StartupStatusEnum +StatEntry +StatExtEntry +StateFileChunk +StatisticExtInfo +StatsBuildData +StatsData +StatsElem +StatsExtInfo +StdAnalyzeData +StdRdOptIndexCleanup +StdRdOptions +Step +StopList +StrategyNumber +StreamCtl +String +StringInfo +StringInfoData +StripnullState +SubLink +SubLinkType +SubOpts +SubPlan +SubPlanState +SubRemoveRels +SubTransactionId +SubXactCallback +SubXactCallbackItem +SubXactEvent +SubXactInfo +SubqueryScan +SubqueryScanPath +SubqueryScanState +SubqueryScanStatus +SubscriptExecSetup +SubscriptExecSteps +SubscriptRoutines +SubscriptTransform +SubscriptingRef +SubscriptingRefState +Subscription +SubscriptionInfo +SubscriptionRelState +SupportRequestCost +SupportRequestIndexCondition +SupportRequestRows +SupportRequestSelectivity +SupportRequestSimplify +SupportRequestWFuncMonotonic +Syn +SyncOps +SyncRepConfigData +SyncRepStandbyData +SyncRequestHandler +SyncRequestType +SysFKRelationship +SysScanDesc +SyscacheCallbackFunction +SystemRowsSamplerData +SystemSamplerData +SystemTimeSamplerData +TAR_MEMBER +TBMIterateResult +TBMIteratingState +TBMIterator +TBMSharedIterator +TBMSharedIteratorState +TBMStatus +TBlockState +TIDBitmap +TM_FailureData +TM_IndexDelete +TM_IndexDeleteOp +TM_IndexStatus +TM_Result +TOKEN_DEFAULT_DACL +TOKEN_INFORMATION_CLASS +TOKEN_PRIVILEGES +TOKEN_USER +TParser +TParserCharTest +TParserPosition +TParserSpecial +TParserState +TParserStateAction +TParserStateActionItem +TQueueDestReceiver +TRGM +TSAnyCacheEntry +TSConfigCacheEntry +TSConfigInfo +TSDictInfo +TSDictionaryCacheEntry +TSExecuteCallback +TSLexeme +TSParserCacheEntry +TSParserInfo +TSQuery +TSQueryData +TSQueryParserState +TSQuerySign +TSReadPointer +TSTemplateInfo +TSTernaryValue +TSTokenTypeStorage +TSVector +TSVectorBuildState +TSVectorData +TSVectorParseState +TSVectorStat +TState +TStatus +TStoreState +TXNEntryFile +TYPCATEGORY +T_Action +T_WorkerStatus +TableAmRoutine +TableAttachInfo +TableDataInfo +TableFunc +TableFuncRoutine +TableFuncScan +TableFuncScanState +TableInfo +TableLikeClause +TableSampleClause +TableScanDesc +TableScanDescData +TableSpaceCacheEntry +TableSpaceOpts +TablespaceList +TablespaceListCell +TapeBlockTrailer +TapeShare +TarMethodData +TarMethodFile +TargetEntry +TclExceptionNameMap +Tcl_DString +Tcl_FileProc +Tcl_HashEntry +Tcl_HashTable +Tcl_Interp +Tcl_NotifierProcs +Tcl_Obj +Tcl_Time +TempNamespaceStatus +TestDecodingData +TestDecodingTxnData +TestSpec +TextFreq +TextPositionState +TheLexeme +TheSubstitute +TidExpr +TidExprType +TidHashKey +TidOpExpr +TidPath +TidRangePath +TidRangeScan +TidRangeScanState +TidScan +TidScanState +TimeADT +TimeLineHistoryCmd +TimeLineHistoryEntry +TimeLineID +TimeOffset +TimeStamp +TimeTzADT +TimeZoneAbbrevTable +TimeoutId +TimeoutType +Timestamp +TimestampTz +TmFromChar +TmToChar +ToastAttrInfo +ToastCompressionId +ToastTupleContext +ToastedAttribute +TocEntry +TokenAuxData +TokenizedAuthLine +TrackItem +TransInvalidationInfo +TransState +TransactionId +TransactionState +TransactionStateData +TransactionStmt +TransactionStmtKind +TransformInfo +TransformJsonStringValuesState +TransitionCaptureState +TrgmArc +TrgmArcInfo +TrgmBound +TrgmColor +TrgmColorInfo +TrgmGistOptions +TrgmNFA +TrgmPackArcInfo +TrgmPackedArc +TrgmPackedGraph +TrgmPackedState +TrgmPrefix +TrgmState +TrgmStateKey +TrieChar +Trigger +TriggerData +TriggerDesc +TriggerEvent +TriggerFlags +TriggerInfo +TriggerTransition +TruncateStmt +TsmRoutine +TupOutputState +TupSortStatus +TupStoreStatus +TupleConstr +TupleConversionMap +TupleDesc +TupleHashEntry +TupleHashEntryData +TupleHashIterator +TupleHashTable +TupleQueueReader +TupleTableSlot +TupleTableSlotOps +TuplesortInstrumentation +TuplesortMethod +TuplesortSpaceType +Tuplesortstate +Tuplestorestate +TwoPhaseCallback +TwoPhaseFileHeader +TwoPhaseLockRecord +TwoPhasePgStatRecord +TwoPhasePredicateLockRecord +TwoPhasePredicateRecord +TwoPhasePredicateRecordType +TwoPhasePredicateXactRecord +TwoPhaseRecordOnDisk +TwoPhaseRmgrId +TwoPhaseStateData +Type +TypeCacheEntry +TypeCacheEnumData +TypeCast +TypeCat +TypeFuncClass +TypeInfo +TypeName +U +U32 +U8 +UChar +UCharIterator +UColAttribute +UColAttributeValue +UCollator +UConverter +UErrorCode +UINT +ULARGE_INTEGER +ULONG +ULONG_PTR +UV +UVersionInfo +UnicodeNormalizationForm +UnicodeNormalizationQC +Unique +UniquePath +UniquePathMethod +UniqueState +UnlistenStmt +UnresolvedTup +UnresolvedTupData +UpdateContext +UpdateStmt +UpperRelationKind +UpperUniquePath +UserAuth +UserMapping +UserOpts +VacAttrStats +VacAttrStatsP +VacDeadItems +VacErrPhase +VacOptValue +VacuumParams +VacuumRelation +VacuumStmt +ValidateIndexState +ValuesScan +ValuesScanState +Var +VarBit +VarChar +VarParamState +VarString +VarStringSortSupport +Variable +VariableAssignHook +VariableCache +VariableCacheData +VariableSetKind +VariableSetStmt +VariableShowStmt +VariableSpace +VariableStatData +VariableSubstituteHook +Variables +VersionedQuery +Vfd +ViewCheckOption +ViewOptCheckOption +ViewOptions +ViewStmt +VirtualTransactionId +VirtualTupleTableSlot +VolatileFunctionStatus +Vsrt +WAIT_ORDER +WALAvailability +WALInsertLock +WALInsertLockPadded +WALOpenSegment +WALReadError +WALSegmentCloseCB +WALSegmentContext +WALSegmentOpenCB +WCHAR +WCOKind +WFW_WaitOption +WIDGET +WORD +WORKSTATE +WSABUF +WSADATA +WSANETWORKEVENTS +WSAPROTOCOL_INFO +WaitEvent +WaitEventActivity +WaitEventClient +WaitEventIO +WaitEventIPC +WaitEventSet +WaitEventTimeout +WaitPMResult +WalCloseMethod +WalCompression +WalLevel +WalRcvData +WalRcvExecResult +WalRcvExecStatus +WalRcvState +WalRcvStreamOptions +WalReceiverConn +WalReceiverFunctionsType +WalSnd +WalSndCtlData +WalSndSendDataCallback +WalSndState +WalTimeSample +WalUsage +WalWriteMethod +Walfile +WindowAgg +WindowAggPath +WindowAggState +WindowAggStatus +WindowClause +WindowClauseSortData +WindowDef +WindowFunc +WindowFuncExprState +WindowFuncLists +WindowObject +WindowObjectData +WindowStatePerAgg +WindowStatePerAggData +WindowStatePerFunc +WithCheckOption +WithClause +WordEntry +WordEntryIN +WordEntryPos +WordEntryPosVector +WordEntryPosVector1 +WorkTableScan +WorkTableScanState +WorkerInfo +WorkerInfoData +WorkerInstrumentation +WorkerJobDumpPtrType +WorkerJobRestorePtrType +Working_State +WriteBufPtrType +WriteBytePtrType +WriteDataCallback +WriteDataPtrType +WriteExtraTocPtrType +WriteFunc +WriteManifestState +WriteTarState +WritebackContext +X509 +X509_EXTENSION +X509_NAME +X509_NAME_ENTRY +X509_STORE +X509_STORE_CTX +XLTW_Oper +XLogCtlData +XLogCtlInsert +XLogDumpConfig +XLogDumpPrivate +XLogLongPageHeader +XLogLongPageHeaderData +XLogPageHeader +XLogPageHeaderData +XLogPageReadCB +XLogPageReadPrivate +XLogPageReadResult +XLogPrefetchStats +XLogPrefetcher +XLogPrefetcherFilter +XLogReaderRoutine +XLogReaderState +XLogRecData +XLogRecPtr +XLogRecStats +XLogRecord +XLogRecordBlockCompressHeader +XLogRecordBlockHeader +XLogRecordBlockImageHeader +XLogRecordBuffer +XLogRecoveryCtlData +XLogRedoAction +XLogSegNo +XLogSource +XLogStats +XLogwrtResult +XLogwrtRqst +XPV +XPVIV +XPVMG +XactCallback +XactCallbackItem +XactEvent +XactLockTableWaitInfo +XidBoundsViolation +XidCacheStatus +XidCommitStatus +XidStatus +XmlExpr +XmlExprOp +XmlOptionType +XmlSerialize +XmlTableBuilderData +YYLTYPE +YYSTYPE +YY_BUFFER_STATE +ZSTD_CCtx +ZSTD_DCtx +ZSTD_inBuffer +ZSTD_outBuffer +_SPI_connection +_SPI_plan +__AssignProcessToJobObject +__CreateJobObject +__CreateRestrictedToken +__IsProcessInJob +__QueryInformationJobObject +__SetInformationJobObject +__time64_t +_dev_t +_ino_t +_locale_t +_resultmap +_stringlist +acquireLocksOnSubLinks_context +adjust_appendrel_attrs_context +aff_regex_struct +allocfunc +amadjustmembers_function +ambeginscan_function +ambuild_function +ambuildempty_function +ambuildphasename_function +ambulkdelete_function +amcanreturn_function +amcostestimate_function +amendscan_function +amestimateparallelscan_function +amgetbitmap_function +amgettuple_function +aminitparallelscan_function +aminsert_function +ammarkpos_function +amoptions_function +amparallelrescan_function +amproperty_function +amrescan_function +amrestrpos_function +amvacuumcleanup_function +amvalidate_function +array_iter +array_unnest_fctx +assign_collations_context +autovac_table +av_relation +avl_dbase +avl_node +avl_tree +avw_dbase +backslashResult +backup_manifest_info +backup_manifest_option +base_yy_extra_type +basebackup_options +bbsink +bbsink_copystream +bbsink_gzip +bbsink_lz4 +bbsink_ops +bbsink_server +bbsink_shell +bbsink_state +bbsink_throttle +bbsink_zstd +bbstreamer +bbstreamer_archive_context +bbstreamer_extractor +bbstreamer_gzip_decompressor +bbstreamer_gzip_writer +bbstreamer_lz4_frame +bbstreamer_member +bbstreamer_ops +bbstreamer_plain_writer +bbstreamer_recovery_injector +bbstreamer_tar_archiver +bbstreamer_tar_parser +bbstreamer_zstd_frame +bgworker_main_type +binaryheap +binaryheap_comparator +bitmapword +bits16 +bits32 +bits8 +bloom_filter +boolKEY +brin_column_state +brin_serialize_callback_type +bytea +cached_re_str +canonicalize_state +cashKEY +catalogid_hash +cfp +check_agg_arguments_context +check_function_callback +check_network_data +check_object_relabel_type +check_password_hook_type +check_ungrouped_columns_context +chr +clock_t +cmpEntriesArg +codes_t +collation_cache_entry +color +colormaprange +compare_context +config_var_value +contain_aggs_of_level_context +convert_testexpr_context +copy_data_source_cb +core_YYSTYPE +core_yy_extra_type +core_yyscan_t +corrupt_items +cost_qual_eval_context +cp_hash_func +create_upper_paths_hook_type +createdb_failure_params +crosstab_HashEnt +crosstab_cat_desc +datapagemap_iterator_t +datapagemap_t +dateKEY +datetkn +dce_uuid_t +decimal +deparse_columns +deparse_context +deparse_expr_cxt +deparse_namespace +destructor +dev_t +digit +disassembledLeaf +dlist_head +dlist_iter +dlist_mutable_iter +dlist_node +ds_state +dsa_area +dsa_area_control +dsa_area_pool +dsa_area_span +dsa_handle +dsa_pointer +dsa_pointer_atomic +dsa_segment_header +dsa_segment_index +dsa_segment_map +dshash_compare_function +dshash_hash +dshash_hash_function +dshash_parameters +dshash_partition +dshash_seq_status +dshash_table +dshash_table_control +dshash_table_handle +dshash_table_item +dsm_control_header +dsm_control_item +dsm_handle +dsm_op +dsm_segment +dsm_segment_detach_callback +eLogType +ean13 +eary +ec_matches_callback_type +ec_member_foreign_arg +ec_member_matches_arg +emit_log_hook_type +eval_const_expressions_context +exec_thread_arg +execution_state +explain_get_index_name_hook_type +f_smgr +fd_set +fe_scram_state +fe_scram_state_enum +fetch_range_request +file_action_t +file_entry_t +file_type_t +filehash_hash +filehash_iterator +filemap_t +fill_string_relopt +finalize_primnode_context +find_dependent_phvs_context +find_expr_references_context +fix_join_expr_context +fix_scan_expr_context +fix_upper_expr_context +fix_windowagg_cond_context +flatten_join_alias_vars_context +float4 +float4KEY +float8 +float8KEY +floating_decimal_32 +floating_decimal_64 +fmAggrefPtr +fmExprContextCallbackFunction +fmNodePtr +fmStringInfo +fmgr_hook_type +foreign_glob_cxt +foreign_loc_cxt +freeaddrinfo_ptr_t +freefunc +fsec_t +gbt_vsrt_arg +gbtree_ninfo +gbtree_vinfo +generate_series_fctx +generate_series_numeric_fctx +generate_series_timestamp_fctx +generate_series_timestamptz_fctx +generate_subscripts_fctx +get_attavgwidth_hook_type +get_index_stats_hook_type +get_relation_info_hook_type +get_relation_stats_hook_type +getaddrinfo_ptr_t +getnameinfo_ptr_t +gid_t +gin_leafpage_items_state +ginxlogCreatePostingTree +ginxlogDeleteListPages +ginxlogDeletePage +ginxlogInsert +ginxlogInsertDataInternal +ginxlogInsertEntry +ginxlogInsertListPage +ginxlogRecompressDataLeaf +ginxlogSplit +ginxlogUpdateMeta +ginxlogVacuumDataLeafPage +gistxlogDelete +gistxlogPage +gistxlogPageDelete +gistxlogPageReuse +gistxlogPageSplit +gistxlogPageUpdate +grouping_sets_data +gseg_picksplit_item +gss_buffer_desc +gss_cred_id_t +gss_ctx_id_t +gss_name_t +gtrgm_consistent_cache +gzFile +hashfunc +hbaPort +heap_page_items_state +help_handler +hlCheck +hstoreCheckKeyLen_t +hstoreCheckValLen_t +hstorePairs_t +hstoreUniquePairs_t +hstoreUpgrade_t +hyperLogLogState +ifState +ilist +import_error_callback_arg +indexed_tlist +inet +inetKEY +inet_struct +init_function +inline_cte_walker_context +inline_error_callback_arg +ino_t +instr_time +int128 +int16 +int16KEY +int2vector +int32 +int32KEY +int32_t +int64 +int64KEY +int8 +internalPQconninfoOption +intptr_t +intset_internal_node +intset_leaf_node +intset_node +intvKEY +itemIdCompact +itemIdCompactData +iterator +jmp_buf +join_search_hook_type +json_aelem_action +json_manifest_error_callback +json_manifest_perfile_callback +json_manifest_perwalrange_callback +json_ofield_action +json_scalar_action +json_struct_action +keyEntryData +key_t +lclContext +lclTocEntry +leafSegmentInfo +leaf_item +libpq_source +line_t +lineno_t +list_sort_comparator +local_relopt +local_relopts +local_source +locale_t +locate_agg_of_level_context +locate_var_of_level_context +locate_windowfunc_context +logstreamer_param +lquery +lquery_level +lquery_variant +ltree +ltree_gist +ltree_level +ltxtquery +mXactCacheEnt +mac8KEY +macKEY +macaddr +macaddr8 +macaddr_sortsupport_state +manifest_file +manifest_files_hash +manifest_files_iterator +manifest_wal_range +map_variable_attnos_context +max_parallel_hazard_context +mb2wchar_with_len_converter +mbchar_verifier +mbcharacter_incrementer +mbdisplaylen_converter +mblen_converter +mbstr_verifier +memoize_hash +memoize_iterator +metastring +mix_data_t +mixedStruct +mode_t +movedb_failure_params +multirange_bsearch_comparison +multirange_unnest_fctx +mxact +mxtruncinfo +needs_fmgr_hook_type +network_sortsupport_state +nodeitem +normal_rand_fctx +ntile_context +numeric +object_access_hook_type +object_access_hook_type_str +off_t +oidKEY +oidvector +on_dsm_detach_callback +on_exit_nicely_callback +openssl_tls_init_hook_typ +ossl_EVP_cipher_func +other +output_type +pagetable_hash +pagetable_iterator +pairingheap +pairingheap_comparator +pairingheap_node +parallel_worker_main_type +parse_error_callback_arg +parser_context +partition_method_t +pendingPosition +pgParameterStatus +pg_atomic_flag +pg_atomic_uint32 +pg_atomic_uint64 +pg_be_sasl_mech +pg_checksum_context +pg_checksum_raw_context +pg_checksum_type +pg_compress_algorithm +pg_compress_specification +pg_conn_host +pg_conn_host_type +pg_conv_map +pg_crc32 +pg_crc32c +pg_cryptohash_ctx +pg_cryptohash_errno +pg_cryptohash_type +pg_ctype_cache +pg_enc +pg_enc2gettext +pg_enc2name +pg_encname +pg_fe_sasl_mech +pg_funcptr_t +pg_gssinfo +pg_hmac_ctx +pg_hmac_errno +pg_int64 +pg_local_to_utf_combined +pg_locale_t +pg_mb_radix_tree +pg_md5_ctx +pg_on_exit_callback +pg_prng_state +pg_re_flags +pg_saslprep_rc +pg_sha1_ctx +pg_sha224_ctx +pg_sha256_ctx +pg_sha384_ctx +pg_sha512_ctx +pg_snapshot +pg_stack_base_t +pg_time_t +pg_time_usec_t +pg_tz +pg_tz_cache +pg_tzenum +pg_unicode_decompinfo +pg_unicode_decomposition +pg_unicode_norminfo +pg_unicode_normprops +pg_unicode_recompinfo +pg_utf_to_local_combined +pg_uuid_t +pg_wc_probefunc +pg_wchar +pg_wchar_tbl +pgp_armor_headers_state +pgpid_t +pgsocket +pgsql_thing_t +pgssEntry +pgssGlobalStats +pgssHashKey +pgssSharedState +pgssStoreKind +pgssVersion +pgstat_entry_ref_hash_hash +pgstat_entry_ref_hash_iterator +pgstat_page +pgstat_snapshot_hash +pgstattuple_type +pgthreadlock_t +pid_t +pivot_field +planner_hook_type +plperl_array_info +plperl_call_data +plperl_interp_desc +plperl_proc_desc +plperl_proc_key +plperl_proc_ptr +plperl_query_desc +plperl_query_entry +plpgsql_CastHashEntry +plpgsql_CastHashKey +plpgsql_HashEnt +pltcl_call_state +pltcl_interp_desc +pltcl_proc_desc +pltcl_proc_key +pltcl_proc_ptr +pltcl_query_desc +pointer +polymorphic_actuals +pos_trgm +post_parse_analyze_hook_type +postprocess_result_function +pqbool +pqsigfunc +printQueryOpt +printTableContent +printTableFooter +printTableOpt +printTextFormat +printTextLineFormat +printTextLineWrap +printTextRule +printfunc +priv_map +process_file_callback_t +process_sublinks_context +proclist_head +proclist_mutable_iter +proclist_node +promptStatus_t +pthread_barrier_t +pthread_cond_t +pthread_key_t +pthread_mutex_t +pthread_once_t +pthread_t +ptrdiff_t +pull_var_clause_context +pull_varattnos_context +pull_varnos_context +pull_vars_context +pullup_replace_vars_context +pushdown_safety_info +qc_hash_func +qsort_arg_comparator +qsort_comparator +query_pathkeys_callback +radius_attribute +radius_packet +rangeTableEntry_used_context +rank_context +rbt_allocfunc +rbt_combiner +rbt_comparator +rbt_freefunc +reduce_outer_joins_state +reference +regex_arc_t +regex_t +regexp +regexp_matches_ctx +registered_buffer +regmatch_t +regoff_t +regproc +relopt_bool +relopt_enum +relopt_enum_elt_def +relopt_gen +relopt_int +relopt_kind +relopt_parse_elt +relopt_real +relopt_string +relopt_type +relopt_value +relopts_validator +remoteConn +remoteConnHashEnt +remoteDep +rendezvousHashEntry +replace_rte_variables_callback +replace_rte_variables_context +ret_type +rewind_source +rewrite_event +rf_context +rm_detail_t +role_auth_extra +row_security_policy_hook_type +rsv_callback +saophash_hash +save_buffer +scram_state +scram_state_enum +sem_t +sequence_magic +set_join_pathlist_hook_type +set_rel_pathlist_hook_type +shm_mq +shm_mq_handle +shm_mq_iovec +shm_mq_result +shm_toc +shm_toc_entry +shm_toc_estimator +shmem_request_hook_type +shmem_startup_hook_type +sig_atomic_t +sigjmp_buf +signedbitmapword +sigset_t +size_t +slist_head +slist_iter +slist_mutable_iter +slist_node +slock_t +socket_set +socklen_t +spgBulkDeleteState +spgChooseIn +spgChooseOut +spgChooseResultType +spgConfigIn +spgConfigOut +spgInnerConsistentIn +spgInnerConsistentOut +spgLeafConsistentIn +spgLeafConsistentOut +spgNodePtr +spgPickSplitIn +spgPickSplitOut +spgVacPendingItem +spgxlogAddLeaf +spgxlogAddNode +spgxlogMoveLeafs +spgxlogPickSplit +spgxlogSplitTuple +spgxlogState +spgxlogVacuumLeaf +spgxlogVacuumRedirect +spgxlogVacuumRoot +split_pathtarget_context +split_pathtarget_item +sql_error_callback_arg +sqlparseInfo +sqlparseState +ss_lru_item_t +ss_scan_location_t +ss_scan_locations_t +ssize_t +standard_qp_extra +stemmer_module +stmtCacheEntry +storeInfo +storeRes_func +stream_stop_callback +string +substitute_actual_parameters_context +substitute_actual_srf_parameters_context +substitute_phv_relids_context +symbol +tablespaceinfo +teSection +temp_tablespaces_extra +test_re_flags +test_regex_ctx +test_shm_mq_header +test_spec +test_start_function +text +timeKEY +time_t +timeout_handler_proc +timeout_params +timerCA +tlist_vinfo +toast_compress_header +transferMode +transfer_thread_arg +trgm +trgm_mb_char +trivalue +tsKEY +ts_parserstate +ts_tokenizer +ts_tokentype +tsearch_readline_state +tuplehash_hash +tuplehash_iterator +type +tzEntry +u_char +u_int +uchr +uid_t +uint128 +uint16 +uint16_t +uint32 +uint32_t +uint64 +uint64_t +uint8 +uint8_t +uintptr_t +unicodeStyleBorderFormat +unicodeStyleColumnFormat +unicodeStyleFormat +unicodeStyleRowFormat +unicode_linestyle +unit_conversion +unlogged_relation_entry +utf_local_conversion_func +uuidKEY +uuid_rc_t +uuid_sortsupport_state +uuid_t +va_list +vacuumingOptions +validate_string_relopt +varatt_expanded +varattrib_1b +varattrib_1b_e +varattrib_4b +vbits +verifier_context +walrcv_check_conninfo_fn +walrcv_connect_fn +walrcv_create_slot_fn +walrcv_disconnect_fn +walrcv_endstreaming_fn +walrcv_exec_fn +walrcv_get_backend_pid_fn +walrcv_get_conninfo_fn +walrcv_get_senderinfo_fn +walrcv_identify_system_fn +walrcv_readtimelinehistoryfile_fn +walrcv_receive_fn +walrcv_send_fn +walrcv_server_version_fn +walrcv_startstreaming_fn +wchar2mb_with_len_converter +wchar_t +win32_deadchild_waitinfo +wint_t +worker_state +worktable +wrap +xl_brin_createidx +xl_brin_desummarize +xl_brin_insert +xl_brin_revmap_extend +xl_brin_samepage_update +xl_brin_update +xl_btree_dedup +xl_btree_delete +xl_btree_insert +xl_btree_mark_page_halfdead +xl_btree_metadata +xl_btree_newroot +xl_btree_reuse_page +xl_btree_split +xl_btree_unlink_page +xl_btree_update +xl_btree_vacuum +xl_clog_truncate +xl_commit_ts_truncate +xl_dbase_create_file_copy_rec +xl_dbase_create_wal_log_rec +xl_dbase_drop_rec +xl_end_of_recovery +xl_hash_add_ovfl_page +xl_hash_delete +xl_hash_init_bitmap_page +xl_hash_init_meta_page +xl_hash_insert +xl_hash_move_page_contents +xl_hash_split_allocate_page +xl_hash_split_complete +xl_hash_squeeze_page +xl_hash_update_meta_page +xl_hash_vacuum_one_page +xl_heap_confirm +xl_heap_delete +xl_heap_freeze_page +xl_heap_freeze_tuple +xl_heap_header +xl_heap_inplace +xl_heap_insert +xl_heap_lock +xl_heap_lock_updated +xl_heap_multi_insert +xl_heap_new_cid +xl_heap_prune +xl_heap_rewrite_mapping +xl_heap_truncate +xl_heap_update +xl_heap_vacuum +xl_heap_visible +xl_invalid_page +xl_invalid_page_key +xl_invalidations +xl_logical_message +xl_multi_insert_tuple +xl_multixact_create +xl_multixact_truncate +xl_overwrite_contrecord +xl_parameter_change +xl_relmap_update +xl_replorigin_drop +xl_replorigin_set +xl_restore_point +xl_running_xacts +xl_seq_rec +xl_smgr_create +xl_smgr_truncate +xl_standby_lock +xl_standby_locks +xl_tblspc_create_rec +xl_tblspc_drop_rec +xl_xact_abort +xl_xact_assignment +xl_xact_commit +xl_xact_dbinfo +xl_xact_invals +xl_xact_origin +xl_xact_parsed_abort +xl_xact_parsed_commit +xl_xact_parsed_prepare +xl_xact_prepare +xl_xact_relfilenodes +xl_xact_stats_item +xl_xact_stats_items +xl_xact_subxacts +xl_xact_twophase +xl_xact_xinfo +xmlBuffer +xmlBufferPtr +xmlChar +xmlDocPtr +xmlErrorPtr +xmlExternalEntityLoader +xmlGenericErrorFunc +xmlNodePtr +xmlNodeSetPtr +xmlParserCtxtPtr +xmlParserInputPtr +xmlStructuredErrorFunc +xmlTextWriter +xmlTextWriterPtr +xmlXPathCompExprPtr +xmlXPathContextPtr +xmlXPathObjectPtr +xmltype +xpath_workspace +xsltSecurityPrefsPtr +xsltStylesheetPtr +xsltTransformContextPtr +yy_parser +yy_size_t +yyscan_t +z_stream +z_streamp +zic_t diff --git a/src/tools/pgtest b/src/tools/pgtest new file mode 100755 index 0000000..70f6a62 --- /dev/null +++ b/src/tools/pgtest @@ -0,0 +1,57 @@ +#!/bin/sh + +# src/tools/pgtest [-n] [...] + +# This runs a build/initdb/regression test suite +# +# This will start a temporary postmaster, so you have to +# have enough kernel resources to run two postmasters or +# stop your main postmaster before running this script. +# +# Use -n to prevent 'make clean' + +MAKE="make" + +[ ! -d src ] && echo "This must be run from the top of the PostgreSQL source tree" 1>&2 && exit 1 + +trap "rm -rf /tmp/$$" 0 1 2 3 15 +mkdir /tmp/$$ +TMP="/tmp/$$" + +if [ "X$1" != "X-n" ] +then CLEAN="Y" +else CLEAN="" + shift +fi + +rm -f tmp_install/log/install.log + +# Run "make check" and store return code in $TMP/ret. +# Display output but also capture it in $TMP/0. +( + if [ "$CLEAN" ] + then $MAKE "$@" clean 2>&1 + echo "$?" > $TMP/ret + fi + if [ $(cat $TMP/ret) -eq 0 ] + then $MAKE "$@" 2>&1 && $MAKE "$@" check 2>&1 + echo "$?" > $TMP/ret + fi +) | tee $TMP/0 + +# Grab possible warnings from install.log +[ -e tmp_install/log/install.log ] && cat tmp_install/log/install.log >> $TMP/0 + +# If success, display warnings +if [ $(cat $TMP/ret) -eq 0 ] +then cat $TMP/0 | + # The following grep's have to be adjusted for your setup because + # certain warnings are acceptable. + grep -i warning | + grep -v setproctitle | + grep -v find_rule | + grep -v yy_flex_realloc +fi + +# return original make error code +exit `cat $TMP/ret` diff --git a/src/tools/testint128.c b/src/tools/testint128.c new file mode 100644 index 0000000..2ebd4f5 --- /dev/null +++ b/src/tools/testint128.c @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------- + * + * testint128.c + * Testbed for roll-our-own 128-bit integer arithmetic. + * + * This is a standalone test program that compares the behavior of an + * implementation in int128.h to an (assumed correct) int128 native type. + * + * Copyright (c) 2017-2022, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/tools/testint128.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +/* + * By default, we test the non-native implementation in int128.h; but + * by predefining USE_NATIVE_INT128 to 1, you can test the native + * implementation, just to be sure. + */ +#ifndef USE_NATIVE_INT128 +#define USE_NATIVE_INT128 0 +#endif + +#include "common/int128.h" +#include "common/pg_prng.h" + +/* + * We assume the parts of this union are laid out compatibly. + */ +typedef union +{ + int128 i128; + INT128 I128; + union + { +#ifdef WORDS_BIGENDIAN + int64 hi; + uint64 lo; +#else + uint64 lo; + int64 hi; +#endif + } hl; +} test128; + + +/* + * Control version of comparator. + */ +static inline int +my_int128_compare(int128 x, int128 y) +{ + if (x < y) + return -1; + if (x > y) + return 1; + return 0; +} + +/* + * Main program. + * + * Generates a lot of random numbers and tests the implementation for each. + * The results should be reproducible, since we use a fixed PRNG seed. + * + * You can give a loop count if you don't like the default 1B iterations. + */ +int +main(int argc, char **argv) +{ + long count; + + pg_prng_seed(&pg_global_prng_state, 0); + + if (argc >= 2) + count = strtol(argv[1], NULL, 0); + else + count = 1000000000; + + while (count-- > 0) + { + int64 x = pg_prng_uint64(&pg_global_prng_state); + int64 y = pg_prng_uint64(&pg_global_prng_state); + int64 z = pg_prng_uint64(&pg_global_prng_state); + test128 t1; + test128 t2; + + /* check unsigned addition */ + t1.hl.hi = x; + t1.hl.lo = y; + t2 = t1; + t1.i128 += (int128) (uint64) z; + int128_add_uint64(&t2.I128, (uint64) z); + + if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo) + { + printf("%016lX%016lX + unsigned %lX\n", x, y, z); + printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check signed addition */ + t1.hl.hi = x; + t1.hl.lo = y; + t2 = t1; + t1.i128 += (int128) z; + int128_add_int64(&t2.I128, z); + + if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo) + { + printf("%016lX%016lX + signed %lX\n", x, y, z); + printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check multiplication */ + t1.i128 = (int128) x * (int128) y; + + t2.hl.hi = t2.hl.lo = 0; + int128_add_int64_mul_int64(&t2.I128, x, y); + + if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo) + { + printf("%lX * %lX\n", x, y); + printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check comparison */ + t1.hl.hi = x; + t1.hl.lo = y; + t2.hl.hi = z; + t2.hl.lo = pg_prng_uint64(&pg_global_prng_state); + + if (my_int128_compare(t1.i128, t2.i128) != + int128_compare(t1.I128, t2.I128)) + { + printf("comparison failure: %d vs %d\n", + my_int128_compare(t1.i128, t2.i128), + int128_compare(t1.I128, t2.I128)); + printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check case with identical hi parts; above will hardly ever hit it */ + t2.hl.hi = x; + + if (my_int128_compare(t1.i128, t2.i128) != + int128_compare(t1.I128, t2.I128)) + { + printf("comparison failure: %d vs %d\n", + my_int128_compare(t1.i128, t2.i128), + int128_compare(t1.I128, t2.I128)); + printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + } + + return 0; +} diff --git a/src/tools/valgrind.supp b/src/tools/valgrind.supp new file mode 100644 index 0000000..4e8c482 --- /dev/null +++ b/src/tools/valgrind.supp @@ -0,0 +1,182 @@ +# This is a suppression file for use with Valgrind tools. File format +# documentation: +# http://valgrind.org/docs/manual/mc-manual.html#mc-manual.suppfiles + +# The libc symbol that implements a particular standard interface is +# implementation-dependent. For example, strncpy() shows up as "__GI_strncpy" +# on some platforms. Use wildcards to avoid mentioning such specific names. +# Avoid mentioning functions that are good candidates for inlining, +# particularly single-caller static functions. Suppressions mentioning them +# would be ineffective at higher optimization levels. + + +# We have occasion to write raw binary structures to disk or to the network. +# These may contain uninitialized padding bytes. Since recipients also ignore +# those bytes as padding, this is harmless. + +{ + padding_pgstat_write + Memcheck:Param + write(buf) + + ... + fun:pgstat_write_statsfiles +} + +{ + padding_XLogRecData_CRC + Memcheck:Value8 + + fun:pg_comp_crc32c* + fun:XLogRecordAssemble +} + +{ + padding_XLogRecData_write + Memcheck:Param + pwrite64(buf) + + ... + fun:XLogWrite +} + +{ + padding_relcache + Memcheck:Param + write(buf) + + ... + fun:write_relcache_init_file +} + +{ + padding_reorderbuffer_serialize + Memcheck:Param + write(buf) + + ... + fun:ReorderBufferSerializeTXN +} + +{ + padding_twophase_prepare + Memcheck:Param + write(buf) + + ... + fun:EndPrepare +} + + +{ + padding_twophase_CRC + Memcheck:Value8 + fun:pg_comp_crc32c* + fun:EndPrepare +} + +{ + padding_bootstrap_initial_xlog_write + Memcheck:Param + write(buf) + + ... + fun:BootStrapXLOG +} + +{ + padding_bootstrap_control_file_write + Memcheck:Param + write(buf) + + ... + fun:WriteControlFile + fun:BootStrapXLOG +} + +{ + bootstrap_write_relmap_overlap + Memcheck:Overlap + fun:memcpy* + fun:write_relmap_file + fun:RelationMapFinishBootstrap +} + + +# gcc on ppc64 can generate a four-byte read to fetch the final "char" fields +# of a FormData_pg_cast. This is valid compiler behavior, because a proper +# FormData_pg_cast has trailing padding. Tuples we treat as structures omit +# that padding, so Valgrind reports an invalid read. Practical trouble would +# entail the missing pad bytes falling in a different memory page. So long as +# the structure is aligned, that will not happen. +{ + overread_tuplestruct_pg_cast + Memcheck:Addr4 + + fun:IsBinaryCoercible +} + +# Python's allocator does some low-level tricks for efficiency. Those +# can be disabled for better instrumentation; but few people testing +# postgres will have such a build of python. So add broad +# suppressions of the resulting errors. +# See also https://svn.python.org/projects/python/trunk/Misc/README.valgrind +{ + python_clever_allocator + Memcheck:Addr4 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Addr8 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Value4 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Value8 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Cond + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Addr4 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Addr8 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Value4 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Value8 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Cond + fun:PyObject_Realloc +} diff --git a/src/tools/version_stamp.pl b/src/tools/version_stamp.pl new file mode 100755 index 0000000..bf8fdfb --- /dev/null +++ b/src/tools/version_stamp.pl @@ -0,0 +1,116 @@ +#! /usr/bin/perl + +################################################################# +# version_stamp.pl -- update version stamps throughout the source tree +# +# Copyright (c) 2008-2022, PostgreSQL Global Development Group +# +# src/tools/version_stamp.pl +################################################################# + +# +# This script updates the version stamp in configure.ac, and also in assorted +# other files wherein it's not convenient to obtain the version number from +# configure's output. Note that you still have to run autoconf afterward +# to regenerate configure from the updated configure.ac. +# +# Usage: cd to top of source tree and issue +# src/tools/version_stamp.pl MINORVERSION +# where MINORVERSION can be a minor release number (0, 1, etc), or +# "devel", "alphaN", "betaN", "rcN". +# + +use strict; +use warnings; + +# Major version is hard-wired into the script. We update it when we branch +# a new development version. +my $majorversion = 15; + +# Validate argument and compute derived variables +my $minor = shift; +defined($minor) || die "$0: missing required argument: minor-version\n"; + +my ($dotneeded); + +if ($minor =~ m/^\d+$/) +{ + $dotneeded = 1; +} +elsif ($minor eq "devel") +{ + $dotneeded = 0; +} +elsif ($minor =~ m/^alpha\d+$/) +{ + $dotneeded = 0; +} +elsif ($minor =~ m/^beta\d+$/) +{ + $dotneeded = 0; +} +elsif ($minor =~ m/^rc\d+$/) +{ + $dotneeded = 0; +} +else +{ + die "$0: minor-version must be N, devel, alphaN, betaN, or rcN\n"; +} + +my $fullversion; + +# Create various required forms of the version number +if ($dotneeded) +{ + $fullversion = $majorversion . "." . $minor; +} +else +{ + $fullversion = $majorversion . $minor; +} + +# Get the autoconf version number for eventual nag message +# (this also ensures we're in the right directory) + +my $aconfver = ""; +open(my $fh, '<', "configure.ac") || die "could not read configure.ac: $!\n"; +while (<$fh>) +{ + if (m/^m4_if\(m4_defn\(\[m4_PACKAGE_VERSION\]\), \[(.*)\], \[\], \[m4_fatal/ + ) + { + $aconfver = $1; + last; + } +} +close($fh); +$aconfver ne "" + || die "could not find autoconf version number in configure.ac\n"; + +# Update configure.ac and other files that contain version numbers + +my $fixedfiles = ""; + +sed_file("configure.ac", + "-e 's/AC_INIT(\\[PostgreSQL\\], \\[[0-9a-z.]*\\]/AC_INIT([PostgreSQL], [$fullversion]/'" +); + +print "Stamped these files with version number $fullversion:\n$fixedfiles"; +print "Don't forget to run autoconf $aconfver before committing.\n"; + +exit 0; + +sub sed_file +{ + my ($filename, $sedargs) = @_; + my ($tmpfilename) = $filename . ".tmp"; + + system("sed $sedargs $filename >$tmpfilename") == 0 + or die "sed failed: $?"; + system("mv $tmpfilename $filename") == 0 + or die "mv failed: $?"; + + $fixedfiles .= "\t$filename\n"; + return; +} diff --git a/src/tools/win32tzlist.pl b/src/tools/win32tzlist.pl new file mode 100755 index 0000000..c43ab7e --- /dev/null +++ b/src/tools/win32tzlist.pl @@ -0,0 +1,140 @@ +################################################################# +# +# win32tzlist.pl -- compare Windows timezone information +# +# Copyright (c) 2008-2022, PostgreSQL Global Development Group +# +# src/tools/win32tzlist.pl +################################################################# + +# +# This script compares the timezone information in the Windows registry +# with that in src/bin/initdb/findtimezone.c. A list of changes will be +# written to stdout - no attempt is made to automatically edit the file. +# +# Run the script from the top-level PG source directory. +# + +use strict; +use warnings; + +use Win32::Registry; + +my $tzfile = 'src/bin/initdb/findtimezone.c'; + +# +# Fetch all timezones in the registry +# +my $basekey; +$HKEY_LOCAL_MACHINE->Open( + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", $basekey) + or die $!; + +my @subkeys; +$basekey->GetKeys(\@subkeys); + +my @system_zones; + +foreach my $keyname (@subkeys) +{ + my $subkey; + my %vals; + + $basekey->Open($keyname, $subkey) or die $!; + $subkey->GetValues(\%vals) or die $!; + $subkey->Close(); + + die "Incomplete timezone data for $keyname!\n" + unless ($vals{Std} && $vals{Dlt} && $vals{Display}); + push @system_zones, + { + 'std' => $vals{Std}->[2], + 'dlt' => $vals{Dlt}->[2], + 'display' => clean_displayname($vals{Display}->[2]), + }; +} + +$basekey->Close(); + +# +# Fetch all timezones currently in the file +# +my @file_zones; +my $pgtz; +open(my $tzfh, '<', $tzfile) or die "Could not open $tzfile!\n"; +{ + local $/ = undef; + $pgtz = <$tzfh>; +} +close($tzfh); + +# Attempt to locate and extract the complete win32_tzmap struct +$pgtz =~ /win32_tzmap\[\] =\s+{\s+\/\*[^\/]+\*\/\s+(.+?)};/gs + or die "Could not locate struct win32_tzmap in $tzfile!"; +$pgtz = $1; + +# Extract each individual record from the struct +while ($pgtz =~ + m/{\s+\/\*(.+?)\*\/\s+"([^"]+)",\s+"([^"]+)",\s+"([^"]+)",?\s+},/gs) +{ + push @file_zones, + { + 'display' => clean_displayname($1), + 'std' => $2, + 'dlt' => $3, + 'match' => $4, + }; +} + +# +# Look for anything that has changed +# +my @add; + +for my $sys (@system_zones) +{ + my $match = 0; + for my $file (@file_zones) + { + if ($sys->{std} eq $file->{std}) + { + $match = 1; + if ($sys->{dlt} ne $file->{dlt}) + { + print + "Timezone $sys->{std}, changed name of daylight zone!\n"; + } + if ($sys->{display} ne $file->{display}) + { + print + "Timezone $sys->{std} changed displayname ('$sys->{display}' from '$file->{display}')!\n"; + } + last; + } + } + unless ($match) + { + push @add, $sys; + } +} + +if (@add) +{ + print "\n\nOther than that, add the following timezones:\n"; + for my $z (@add) + { + print + "\t{\n\t\t/* $z->{display} */\n\t\t\"$z->{std}\", \"$z->{dlt}\",\n\t\t\"FIXME\"\n\t},\n"; + } +} + +sub clean_displayname +{ + my $dn = shift; + + $dn =~ s/\*//gs; + $dn =~ s/\s+/ /gs; + $dn =~ s/^\s+//gs; + $dn =~ s/\s+$//gs; + return $dn; +} |