On Wed Dec 31, 2025 at 4:48 PM CET, Andrew Dunstan wrote:
I'd kinda like to unify these universes, though. We could import the pgperltidy logic into pgindent but disable it unless some flag were given and the correct version of perltidy were present.
Attached is an initial patchset that does that, as well as improving pgindent in various other ways. This has removed the make/meson integration and instead made pgindent smarter at finding pg_bsd_indent and perltidy. I believe that with all of these QoL improvements we could enable perltidy in pgindent by default (after doing a full indent run). I want to call pretty much all of this code was written by Claude Code (i.e. an AI). I plan to do another review pass over this code soonish. Especially the later commits, which I haven't checked in detail yet. But for now I at least wanted to share the direction of what I've been working on, because I saw that Peter marked himself as reviewer on the commitfest app and I don't think it makes sense for him to look at the previous patchset.
From efa77687594e7f52b194aeb81292b3ecb7a165dc Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Wed, 4 Mar 2026 10:02:39 +0100 Subject: [PATCH v2 1/7] pgindent: Clean up temp files on SIGINT When pressing Ctrl+C while running pgindent, it would often leave around files like pgtypedefAXUEEA. This slightly changes SIGINT handling so those files are cleaned up. --- src/tools/pgindent/pgindent | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index 7481696a584..ccc5db4b205 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -19,6 +19,9 @@ use File::Temp; use IO::Handle; use Getopt::Long; +# Ensure SIGINT triggers a clean exit so File::Temp can remove temp files. +$SIG{INT} = sub { exit 130; }; + # Update for pg_bsd_indent version my $INDENT_VERSION = "2.1.2"; base-commit: 2a525cc97e19868940c533787165bc7e7de3a80a -- 2.53.0
From 87e05a46fba366e03df6c8b1fe7bd2b0a1ef560a Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Tue, 3 Mar 2026 23:46:52 +0100 Subject: [PATCH v2 2/7] pgindent: Try to find pg_bsd_indent binary in common locations To run pgindent you need to to have the right version of pg_bsd_indent in your PATH, or specify it manually. This is a bit of a hassle, especially for newcomers or when working on backbranches. So this chnages pgindent to search for a pg_bsd_indent that's built from the current sources, both in-tree (for in-tree autoconf builds) and in a build directory (for meson or autoconf vpath builds). --- src/tools/pgindent/pgindent | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index ccc5db4b205..6570726381c 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -56,11 +56,34 @@ usage("Cannot use --commit with command line file list") # dir, then default location $typedefs_file ||= $ENV{PGTYPEDEFS}; -# get indent location for environment or default -$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent"; - my $sourcedir = locate_sourcedir(); +# get indent location: command line wins, then environment, then try to find +# a compiled pg_bsd_indent in the source tree, then fall back to PATH. +$indent ||= $ENV{PGINDENT} || $ENV{INDENT}; +if (!$indent && $sourcedir) +{ + my $srcroot = "$sourcedir/../../.."; + my $bsd_indent_subdir = "src/tools/pg_bsd_indent/pg_bsd_indent"; + + # Look for pg_bsd_indent: first in-tree (autoconf in-tree build), + # then in a "build" directory (meson or autoconf vpath), + # then any "build*" directory. + foreach my $candidate ( + "$srcroot/$bsd_indent_subdir", + glob("$srcroot/build/$bsd_indent_subdir"), + glob("$srcroot/build*/$bsd_indent_subdir")) + { + if (-x $candidate) + { + $indent = $candidate; + last; + } + } +} +$indent ||= "pg_bsd_indent"; + + # if it's the base of a postgres tree, we will exclude the files # postgres wants excluded if ($sourcedir) -- 2.53.0
From 0c7e7a2215cb017ba59dc20258f9edfb19a14c1a Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Wed, 31 Dec 2025 11:07:58 +0100 Subject: [PATCH v2 3/7] pgindent: Integrate pgperltidy functionality into pgindent Over time our pgindent script has gotten a lot of quality of life features, like the --commit, --diff and --check flags. This integrates the functionality of pgperltidy into pgindent, so it can benefit from these same quality of life improvements, as well as future ones. This commit adds a --perltidy flag to pgindent, which when given will cause pgindent to also format Perl files in addition to the C files it would normally format. It also adds a --perl-only flag, to (as the name suggests) only format Perl files. --- src/tools/pgindent/README | 14 +-- src/tools/pgindent/pgindent | 185 ++++++++++++++++++++++++++++------ src/tools/pgindent/pgperltidy | 18 ---- 3 files changed, 161 insertions(+), 56 deletions(-) delete mode 100755 src/tools/pgindent/pgperltidy diff --git a/src/tools/pgindent/README b/src/tools/pgindent/README index b6cd4c6f6b7..2e31471d7e6 100644 --- a/src/tools/pgindent/README +++ b/src/tools/pgindent/README @@ -35,6 +35,10 @@ DOING THE INDENT RUN BEFORE A NORMAL COMMIT: src/tools/pgindent/pgindent . + To also format Perl files at the same time, add --perltidy: + + src/tools/pgindent/pgindent --perltidy=perltidy . + If any files generate errors, restore their original versions with "git checkout", and see below for cleanup ideas. @@ -76,12 +80,12 @@ AT LEAST ONCE PER RELEASE CYCLE: 2) Run pgindent as above. -3) Indent the Perl code using perltidy: +3) Indent the Perl code using perltidy (if not already done in step 2): - src/tools/pgindent/pgperltidy . + src/tools/pgindent/pgindent --perl-only . If you want to use some perltidy version that's not in your PATH, - first set the PERLTIDY environment variable to point to it. + use --perltidy=PATH or set the PERLTIDY environment variable. 4) Reformat the bootstrap catalog data files: @@ -166,6 +170,4 @@ 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. +When --perltidy is given, pgindent also processes *.pl and *.pm files. diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index 6570726381c..f8ac9c8268b 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -2,12 +2,12 @@ # Copyright (c) 2021-2026, PostgreSQL Global Development Group -# Program to maintain uniform layout style in our C code. +# Program to maintain uniform layout style in our C and Perl code. # Exit codes: # 0 -- all OK # 1 -- error invoking pgindent, nothing done # 2 -- --check mode and at least one file requires changes -# 3 -- pg_bsd_indent failed on at least one file +# 3 -- pg_bsd_indent or perltidy failed on at least one file use strict; use warnings FATAL => 'all'; @@ -32,10 +32,15 @@ my $indent_opts = my $devnull = File::Spec->devnull; my ($typedefs_file, $typedef_str, @excludes, $indent, $diff, - $check, $help, @commits,); + $check, $help, @commits, $perltidy_arg, $perl_only,); $help = 0; +# Save @ARGV before parsing so we can distinguish --perltidy=PATH (where +# the value should be used as the perltidy path) from --perltidy PATH +# (where PATH is a file to format that Getopt::Long greedily consumed). +my @orig_argv = @ARGV; + my %options = ( "help" => \$help, "commit=s" => \@commits, @@ -43,10 +48,21 @@ my %options = ( "list-of-typedefs=s" => \$typedef_str, "excludes=s" => \@excludes, "indent=s" => \$indent, + "perltidy:s" => \$perltidy_arg, + "perl-only" => \$perl_only, "diff" => \$diff, "check" => \$check,); GetOptions(%options) || usage("bad command line argument"); +if (defined($perltidy_arg) && $perltidy_arg ne '') +{ + unless (grep { $_ eq "--perltidy=$perltidy_arg" } @orig_argv) + { + unshift(@ARGV, $perltidy_arg); + $perltidy_arg = ''; + } +} + usage() if $help; usage("Cannot use --commit with command line file list") @@ -56,6 +72,12 @@ usage("Cannot use --commit with command line file list") # dir, then default location $typedefs_file ||= $ENV{PGTYPEDEFS}; +# get perltidy location: command line wins, then environment, then default. +# --perltidy (with or without a path) and --perl-only all imply perltidy is +# wanted, falling back to the PERLTIDY env var, then "perltidy" in PATH. +my $perltidy = $perltidy_arg || $ENV{PERLTIDY} + || (defined($perltidy_arg) || $perl_only ? "perltidy" : undef); + my $sourcedir = locate_sourcedir(); # get indent location: command line wins, then environment, then try to find @@ -142,6 +164,30 @@ sub check_indent return; } +my $PERLTIDY_VERSION = "20230309"; + +sub check_perltidy +{ + my $ver = `$perltidy -v 2>&1`; + if ($? != 0) + { + print STDERR + "You do not appear to have $perltidy installed on your system.\n" + . "See src/tools/pgindent/README for installation instructions.\n"; + exit 1; + } + + if ($ver !~ m/$PERLTIDY_VERSION/) + { + print STDERR + "You do not appear to have $perltidy version $PERLTIDY_VERSION installed on your system.\n" + . "See src/tools/pgindent/README for installation instructions.\n"; + exit 1; + } + + return; +} + sub locate_sourcedir { # try fairly hard to locate the sourcedir @@ -327,6 +373,43 @@ sub run_indent return $source; } +sub format_c +{ + my $source = shift; + my $source_filename = shift; + my $error_message = ''; + + my $formatted = pre_indent($source); + $formatted = run_indent($formatted, \$error_message); + if ($formatted eq "") + { + print STDERR "Failure in $source_filename: " . $error_message . "\n"; + return ("", 1); + } + return post_indent($formatted); +} + +sub format_perl +{ + my $source = shift; + my $source_filename = shift; + + my $tmp_fh = new File::Temp(TEMPLATE => "pgperltidyXXXXX"); + my $tmp_filename = $tmp_fh->filename; + print $tmp_fh $source; + $tmp_fh->close(); + + my $perltidy_profile = "$sourcedir/perltidyrc"; + my $err = + `$perltidy --profile=$perltidy_profile -b -bext='/' $tmp_filename 2>&1`; + if ($? != 0) + { + print STDERR "Failure in $source_filename: " . $err . "\n"; + return ("", 1); + } + return read_source($tmp_filename); +} + sub diff { my $indented = shift; @@ -356,6 +439,8 @@ Options: --list-of-typedefs=STR string containing typedefs, space separated --excludes=PATH file containing list of filename patterns to ignore --indent=PATH path to pg_bsd_indent program + --perltidy[=PATH] enable Perl formatting (optionally set perltidy path) + --perl-only format only Perl files, skip C formatting --diff show the changes that would be made --check exit with status 2 if any changes would be made The --excludes and --commit options can be given more than once. @@ -374,20 +459,64 @@ EOF # main -$filtered_typedefs_fh = load_typedefs(); +my $do_c = !$perl_only; +my $do_perl = defined($perltidy); + +if ($do_c) +{ + $filtered_typedefs_fh = load_typedefs(); + check_indent(); +} + +if ($do_perl) +{ + check_perltidy(); +} -check_indent(); +sub is_c_file +{ + my $filename = shift; + # It needs to have .c or .h extension + return 0 unless $filename =~ /\.[ch]$/; + # Automatically ignore .c and .h files that correspond to a .y or .l file. + # pg_bsd_indent tends to get badly confused by Bison/flex output, and + # there's no value in indenting derived files anyway. + my $otherfile = $filename; + $otherfile =~ s/\.[ch]$/.y/; + return 0 if $otherfile ne $filename && -f $otherfile; + $otherfile =~ s/\.y$/.l/; + return 0 if $otherfile ne $filename && -f $otherfile; + return 1; +} + +sub is_perl_file +{ + my $filename = shift; + # take all .pl and .pm files + return 1 if $filename =~ /\.p[lm]$/; + # take executable files that file(1) thinks are perl files + return 0 unless -x $filename; + my $file_out = `file "$filename"`; + return $file_out =~ /:.*perl[0-9]*\b/i; +} my $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); + || return; + if ($do_c && is_c_file($File::Find::name)) + { + push(@files, $File::Find::name); + } + elsif ($do_perl && is_perl_file($File::Find::name)) + { + push(@files, $File::Find::name); + } }; # any non-option arguments are files or directories to be processed -File::Find::find({ wanted => $wanted }, @ARGV) if @ARGV; +File::Find::find({ wanted => $wanted, no_chdir => 1 }, @ARGV) if @ARGV; # commit file locations are relative to the source root chdir "$sourcedir/../../.." if @commits && $sourcedir; @@ -399,7 +528,8 @@ foreach my $commit (@commits) my @affected = `git diff --diff-filter=ACMR --name-only $prev $commit`; die "git error" if $?; chomp(@affected); - push(@files, @affected); + push(@files, grep { is_c_file($_) } @affected) if $do_c; + push(@files, grep { is_perl_file($_) } @affected) if $do_perl; } warn "No files to process" unless @files; @@ -416,51 +546,42 @@ foreach my $source_filename (@files) next if $processed{$source_filename}; $processed{$source_filename} = 1; - # ignore anything that's not a .c or .h file - next unless $source_filename =~ /\.[ch]$/; - - # don't try to indent a file that doesn't exist + # don't try to format a file that doesn't exist unless (-f $source_filename) { warn "Could not find $source_filename"; next; } - # 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 = ''; + my ($formatted, $failure); - $source = pre_indent($source); + if ($source_filename =~ /\.[ch]$/) + { + ($formatted, $failure) = format_c($source, $source_filename); + } + else + { + ($formatted, $failure) = format_perl($source, $source_filename); + } - $source = run_indent($source, \$error_message); - if ($source eq "") + if ($failure) { - print STDERR "Failure in $source_filename: " . $error_message . "\n"; $status = 3; next; } - $source = post_indent($source); - - if ($source ne $orig_source) + if ($formatted ne $source) { if (!$diff && !$check) { - write_source($source, $source_filename); + write_source($formatted, $source_filename); } else { if ($diff) { - print diff($source, $source_filename); + print diff($formatted, $source_filename); } if ($check) diff --git a/src/tools/pgindent/pgperltidy b/src/tools/pgindent/pgperltidy deleted file mode 100755 index 87838d6bde3..00000000000 --- a/src/tools/pgindent/pgperltidy +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# src/tools/pgindent/pgperltidy - -set -e - -# set this to override default perltidy program: -PERLTIDY=${PERLTIDY:-perltidy} - -PERLTIDY_VERSION=20230309 -if ! $PERLTIDY -v | grep -q $PERLTIDY_VERSION; then - echo "You do not appear to have $PERLTIDY version $PERLTIDY_VERSION installed on your system." >&2 - exit 1 -fi - -. src/tools/perlcheck/find_perl_files - -find_perl_files "$@" | xargs $PERLTIDY --profile=src/tools/pgindent/perltidyrc -- 2.53.0
From ba28acaa64c33d8a8afe12c91a76f5dd5b57483c Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Tue, 3 Mar 2026 09:43:33 +0100 Subject: [PATCH v2 4/7] pgindent: Use git ls-files to discover files --- src/tools/pgindent/pgindent | 56 +++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index f8ac9c8268b..983fe0b1d4f 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -13,7 +13,6 @@ use strict; use warnings FATAL => 'all'; use Cwd qw(abs_path getcwd); -use File::Find; use File::Spec; use File::Temp; use IO::Handle; @@ -500,23 +499,56 @@ sub is_perl_file return $file_out =~ /:.*perl[0-9]*\b/i; } -my $wanted = sub { - my ($dev, $ino, $mode, $nlink, $uid, $gid); - (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) - && -f _ - || return; - if ($do_c && is_c_file($File::Find::name)) +sub discover_files +{ + my @paths = @_; + my @discovered; + + # Separate individual files from directories + my @dirs; + foreach my $path (@paths) { - push(@files, $File::Find::name); + if (-f $path) + { + push(@discovered, $path); + } + elsif (-d $path) + { + push(@dirs, $path); + } + else + { + warn "Could not find $path"; + } } - elsif ($do_perl && is_perl_file($File::Find::name)) + + # Use git ls-files for directories to avoid searching build trees etc. + if (@dirs) { - push(@files, $File::Find::name); + my @git_files = `git ls-files -- @dirs`; + die "git ls-files error" if $?; + chomp(@git_files); + push(@discovered, @git_files); } -}; + + return @discovered; +} # any non-option arguments are files or directories to be processed -File::Find::find({ wanted => $wanted, no_chdir => 1 }, @ARGV) if @ARGV; +if (@ARGV) +{ + foreach my $f (discover_files(@ARGV)) + { + if ($do_c && is_c_file($f)) + { + push(@files, $f); + } + elsif ($do_perl && is_perl_file($f)) + { + push(@files, $f); + } + } +} # commit file locations are relative to the source root chdir "$sourcedir/../../.." if @commits && $sourcedir; -- 2.53.0
From d9e41c2931340a7632b35e0369b8aa61a7db8a7b Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Tue, 3 Mar 2026 20:56:15 +0100 Subject: [PATCH v2 5/7] pgindent: Default to indenting the current directory if no files are given Previously running pgindent without giving it any files would result in this output: src/tools/pgindent/pgindent No files to process at src/tools/pgindent/pgindent line 526. This instead indents the current directory, which is probably what the user intended. --- src/tools/pgindent/pgindent | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index 983fe0b1d4f..f0a87c68d97 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -67,6 +67,9 @@ usage() if $help; usage("Cannot use --commit with command line file list") if (@commits && @ARGV); +# default to current directory if no files/dirs given +@ARGV = ('.') unless @ARGV || @commits; + # command line option wins, then environment, then locations based on current # dir, then default location $typedefs_file ||= $ENV{PGTYPEDEFS}; -- 2.53.0
From deaad631d0b99970a20743571403eafd0fd8d15d Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Wed, 4 Mar 2026 09:04:15 +0100 Subject: [PATCH v2 6/7] pgindent: Add easy way of getting perltidy We need a very specific perltidy version, but getting that installed is quite a hassle. Especially for people who don't use perl on a daily basis. This adds a small script to download the exact perltidy version that we need. --- src/tools/pgindent/.gitignore | 1 + src/tools/pgindent/README | 16 ++++----- src/tools/pgindent/get_perltidy | 62 +++++++++++++++++++++++++++++++++ src/tools/pgindent/pgindent | 30 +++++++++++++--- 4 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 src/tools/pgindent/.gitignore create mode 100755 src/tools/pgindent/get_perltidy diff --git a/src/tools/pgindent/.gitignore b/src/tools/pgindent/.gitignore new file mode 100644 index 00000000000..1497217113b --- /dev/null +++ b/src/tools/pgindent/.gitignore @@ -0,0 +1 @@ +/perltidy/ diff --git a/src/tools/pgindent/README b/src/tools/pgindent/README index 2e31471d7e6..264038e7e09 100644 --- a/src/tools/pgindent/README +++ b/src/tools/pgindent/README @@ -17,14 +17,14 @@ PREREQUISITES: 2) Install perltidy. Please be sure it is version 20230309 (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-20230309.tar.gz - Or if you have cpanm installed, you can just use: - cpanm https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/Perl-Tidy-20230309.tar.gz + + The easiest way is to use the get_perltidy script, which downloads, + verifies, and installs the correct version into the source tree: + + src/tools/pgindent/get_perltidy + + This installs perltidy into src/tools/pgindent/perltidy/, which + pgindent will find automatically. DOING THE INDENT RUN BEFORE A NORMAL COMMIT: diff --git a/src/tools/pgindent/get_perltidy b/src/tools/pgindent/get_perltidy new file mode 100755 index 00000000000..09ce4a195cb --- /dev/null +++ b/src/tools/pgindent/get_perltidy @@ -0,0 +1,62 @@ +#!/bin/sh + +# src/tools/pgindent/get_perltidy +# +# Downloads and installs perltidy 20230309 into src/tools/pgindent/perltidy/. + +set -e + +PERLTIDY_VERSION=20230309 +PERLTIDY_TARBALL="Perl-Tidy-${PERLTIDY_VERSION}.tar.gz" +PERLTIDY_URL="https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/${PERLTIDY_TARBALL}" +PERLTIDY_SHA256="e22949a208c618d671a18c5829b451abbe9da0da2cddd78fdbfcb036c7361c18" + +# Determine the directory this script lives in, i.e. src/tools/pgindent +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) + +PERLTIDY_DIR="$SCRIPT_DIR/perltidy" +PERLTIDY_BIN="$PERLTIDY_DIR/bin/perltidy" + +# Check if already installed with the correct version +if [ -x "$PERLTIDY_BIN" ]; then + perltidy_lib="$PERLTIDY_DIR/lib/perl5" + installed_version=$(PERL5LIB="$perltidy_lib${PERL5LIB:+:$PERL5LIB}" "$PERLTIDY_BIN" -v 2>/dev/null || true) + if echo "$installed_version" | grep -q "$PERLTIDY_VERSION"; then + echo "perltidy $PERLTIDY_VERSION is already installed in $PERLTIDY_DIR" + exit 0 + fi +fi + +WORK_DIR=$(mktemp -d) +trap 'rm -rf "$WORK_DIR"' EXIT + +# Download +TARBALL="$WORK_DIR/$PERLTIDY_TARBALL" +if command -v curl >/dev/null 2>&1; then + curl -sSL -o "$TARBALL" "$PERLTIDY_URL" +elif command -v wget >/dev/null 2>&1; then + wget -q -O "$TARBALL" "$PERLTIDY_URL" +else + echo "error: neither curl nor wget found" >&2 + exit 1 +fi + +# Verify SHA256 +if command -v sha256sum >/dev/null 2>&1; then + echo "$PERLTIDY_SHA256 $TARBALL" | sha256sum -c - >/dev/null +elif command -v shasum >/dev/null 2>&1; then + echo "$PERLTIDY_SHA256 $TARBALL" | shasum -a 256 -c - >/dev/null +else + echo "error: neither sha256sum nor shasum found" >&2 + exit 1 +fi + +# Extract, build, install +cd "$WORK_DIR" +tar xzf "$TARBALL" +cd "Perl-Tidy-${PERLTIDY_VERSION}" +perl Makefile.PL INSTALL_BASE="$PERLTIDY_DIR" >/dev/null +make >/dev/null +make install >/dev/null + +echo "perltidy $PERLTIDY_VERSION installed in $PERLTIDY_DIR" diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index f0a87c68d97..d906c00059f 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -74,14 +74,34 @@ usage("Cannot use --commit with command line file list") # dir, then default location $typedefs_file ||= $ENV{PGTYPEDEFS}; -# get perltidy location: command line wins, then environment, then default. -# --perltidy (with or without a path) and --perl-only all imply perltidy is -# wanted, falling back to the PERLTIDY env var, then "perltidy" in PATH. -my $perltidy = $perltidy_arg || $ENV{PERLTIDY} - || (defined($perltidy_arg) || $perl_only ? "perltidy" : undef); +# get perltidy location: command line wins, then environment, then try to +# find a get_perltidy-installed copy in the source tree, then fall back to +# PATH. --perltidy (with or without a path) and --perl-only all imply +# perltidy is wanted. +my $perltidy = $perltidy_arg || $ENV{PERLTIDY}; my $sourcedir = locate_sourcedir(); +if (!$perltidy && (defined($perltidy_arg) || $perl_only) && $sourcedir) +{ + # Look for a get_perltidy-installed perltidy in the source tree. + my $candidate = "$sourcedir/perltidy/bin/perltidy"; + if (-x $candidate) + { + $perltidy = $candidate; + + # Local installs need PERL5LIB set so perltidy can find its + # modules. + my $libdir = "$sourcedir/perltidy/lib/perl5"; + if (-d $libdir) + { + $ENV{PERL5LIB} = + $ENV{PERL5LIB} ? "$libdir:$ENV{PERL5LIB}" : $libdir; + } + } +} +$perltidy ||= (defined($perltidy_arg) || $perl_only ? "perltidy" : undef); + # get indent location: command line wins, then environment, then try to find # a compiled pg_bsd_indent in the source tree, then fall back to PATH. $indent ||= $ENV{PGINDENT} || $ENV{INDENT}; -- 2.53.0
From 73d5a2647e19b451ad25f8eaa3da5c04b30b821d Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio <[email protected]> Date: Wed, 4 Mar 2026 09:24:24 +0100 Subject: [PATCH v2 7/7] pgindent: Allow parallel pgindent runs Running pgindent on the whole source tree can take a while, especially when also having it run perltidy on perl files. This adds support for pgindent to indent files in parallel. This speeds up a full pgindent run from more than a minute on my machine to ~7 seconds. --- src/tools/pgindent/pgindent | 112 ++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 19 deletions(-) diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent index d906c00059f..d286bba3750 100755 --- a/src/tools/pgindent/pgindent +++ b/src/tools/pgindent/pgindent @@ -17,6 +17,8 @@ use File::Spec; use File::Temp; use IO::Handle; use Getopt::Long; +use Fcntl qw(:flock); +use POSIX qw(:sys_wait_h); # Ensure SIGINT triggers a clean exit so File::Temp can remove temp files. $SIG{INT} = sub { exit 130; }; @@ -30,10 +32,12 @@ my $indent_opts = my $devnull = File::Spec->devnull; -my ($typedefs_file, $typedef_str, @excludes, $indent, $diff, - $check, $help, @commits, $perltidy_arg, $perl_only,); +my ($typedefs_file, $typedef_str, @excludes, $indent, + $diff, $check, $help, @commits, + $perltidy_arg, $perl_only, $jobs,); $help = 0; +$jobs = 0; # Save @ARGV before parsing so we can distinguish --perltidy=PATH (where # the value should be used as the perltidy path) from --perltidy PATH @@ -50,7 +54,8 @@ my %options = ( "perltidy:s" => \$perltidy_arg, "perl-only" => \$perl_only, "diff" => \$diff, - "check" => \$check,); + "check" => \$check, + "jobs|j=i" => \$jobs,); GetOptions(%options) || usage("bad command line argument"); if (defined($perltidy_arg) && $perltidy_arg ne '') @@ -62,6 +67,27 @@ if (defined($perltidy_arg) && $perltidy_arg ne '') } } +sub get_num_cpus +{ + # Try nproc (Linux, some BSDs), then sysctl (macOS, FreeBSD). + for my $cmd ('nproc', 'sysctl -n hw.ncpu') + { + my $n = `$cmd 2>$devnull`; + chomp $n; + return $n + 0 if ($? == 0 && $n =~ /^\d+$/ && $n > 0); + } + return 1; +} + +if ($jobs == 0) +{ + $jobs = get_num_cpus(); +} +elsif ($jobs < 0) +{ + usage("--jobs must be a non-negative number"); +} + usage() if $help; usage("Cannot use --commit with command line file list") @@ -465,6 +491,7 @@ Options: --perl-only format only Perl files, skip C formatting --diff show the changes that would be made --check exit with status 2 if any changes would be made + --jobs=N, -j N number of parallel workers (0 = num CPUs, default 0) The --excludes and --commit options can be given more than once. EOF if ($help) @@ -592,20 +619,18 @@ warn "No files to process" unless @files; # remove excluded files from the file list process_exclude(); -my %processed; -my $status = 0; +# Used by forked children to serialize diff output to STDOUT via flock(). +my $stdout_lock_fh = new File::Temp(TEMPLATE => "pglockXXXXX"); -foreach my $source_filename (@files) +sub process_file { - # skip duplicates - next if $processed{$source_filename}; - $processed{$source_filename} = 1; + my $source_filename = shift; # don't try to format a file that doesn't exist unless (-f $source_filename) { warn "Could not find $source_filename"; - next; + return 0; } my $source = read_source($source_filename); @@ -620,11 +645,7 @@ foreach my $source_filename (@files) ($formatted, $failure) = format_perl($source, $source_filename); } - if ($failure) - { - $status = 3; - next; - } + return 3 if $failure; if ($formatted ne $source) { @@ -636,14 +657,67 @@ foreach my $source_filename (@files) { if ($diff) { - print diff($formatted, $source_filename); + my $output = diff($formatted, $source_filename); + flock($stdout_lock_fh, LOCK_EX); + print $output; + STDOUT->flush(); + flock($stdout_lock_fh, LOCK_UN); } - if ($check) + return 2 if $check; + } + } + + return 0; +} + +# deduplicate file list +my %seen; +@files = grep { !$seen{$_}++ } @files; + +my $status = 0; + +if ($jobs <= 1) +{ + foreach my $source_filename (@files) + { + my $file_status = process_file($source_filename); + $status = $file_status if $file_status > $status; + last if $check && $status >= 2 && !$diff; + } +} +else +{ + my %children; # pid => 1 + + my $file_idx = 0; + while ($file_idx < scalar(@files) || %children) + { + # Fork new children up to $jobs limit + while ($file_idx < scalar(@files) && scalar(keys %children) < $jobs) + { + my $source_filename = $files[ $file_idx++ ]; + + my $pid = fork(); + die "fork failed: $!\n" unless defined $pid; + + if ($pid == 0) { - $status ||= 2; - last unless $diff; + # child + my $child_status = process_file($source_filename); + exit $child_status; } + + $children{$pid} = 1; + } + + # Wait for at least one child to finish + my $pid = waitpid(-1, 0); + if ($pid > 0 && exists $children{$pid}) + { + delete $children{$pid}; + my $child_status = $? >> 8; + $status = $child_status if $child_status > $status; } } } -- 2.53.0
