1#!/usr/bin/perl 2# These two should go upon release to make the script Perl 5.005 compatible 3use strict; 4use warnings; 5 6=head1 NAME 7 8make_patchnum.pl - make patchnum 9 10=head1 SYNOPSIS 11 12 miniperl make_patchnum.pl 13 14 perl make_patchnum.pl 15 16=head1 DESCRIPTION 17 18This program creates the files holding the information 19about locally applied patches to the source code. The created 20files are C<git_version.h> and C<lib/Config_git.pl>. 21 22=item C<lib/Config_git.pl> 23 24Contains status information from git in a form meant to be processed 25by the tied hash logic of Config.pm. It is actually optional, 26although -V:git.\* will be uninformative without it. 27 28C<git_version.h> contains similar information in a C header file 29format, designed to be used by patchlevel.h. This file is obtained 30from stock_git_version.h if miniperl is not available, and then 31later on replaced by the version created by this script. 32 33=head1 AUTHOR 34 35Yves Orton, Kenichi Ishigaki, Max Maischein 36 37=head1 COPYRIGHT 38 39Same terms as Perl itself. 40 41=cut 42 43# from a -Dmksymlink target dir, I need to cd to the git-src tree to 44# use git (like script does). Presuming that's not unique, one fix is 45# to follow Configure's symlink-path to run git. Maybe GIT_DIR or 46# path-args can solve it, if so we should advise here, I tried only 47# very briefly ('cd -' works too). 48 49my ($subcd, $srcdir); 50our $opt_v = scalar grep $_ eq '-v', @ARGV; 51 52BEGIN { 53 my $root="."; 54 # test 1st to see if we're a -Dmksymlinks target dir 55 $subcd = ''; 56 $srcdir = $root; 57 if (-l "./Configure") { 58 $srcdir = readlink("./Configure"); 59 $srcdir =~ s/Configure//; 60 $subcd = "cd $srcdir &&"; # activate backtick fragment 61 } 62 while (!-e "$root/perl.c" and length($root)<100) { 63 if ($root eq '.') { 64 $root=".."; 65 } else { 66 $root.="/.."; 67 } 68 } 69 die "Can't find toplevel" if !-e "$root/perl.c"; 70 sub path_to { "$root/$_[0]" } # use $_[0] if this'd be placed in toplevel. 71} 72 73sub read_file { 74 my $file = path_to(@_); 75 return "" unless -e $file; 76 open my $fh, '<', $file 77 or die "Failed to open for read '$file':$!"; 78 return do { local $/; <$fh> }; 79} 80 81sub write_file { 82 my ($file, $content) = @_; 83 $file= path_to($file); 84 open my $fh, '>', $file 85 or die "Failed to open for write '$file':$!"; 86 print $fh $content; 87 close $fh; 88} 89 90sub backtick { 91 # only for git. If we're in a -Dmksymlinks build-dir, we need to 92 # cd to src so git will work . Probably a better way. 93 my $command = shift; 94 if (wantarray) { 95 my @result= `$subcd $command`; 96 warn "$subcd $command: \$?=$?\n" if $?; 97 print "#> $subcd $command ->\n @result\n" if !$? and $opt_v; 98 chomp @result; 99 return @result; 100 } else { 101 my $result= `$subcd $command`; 102 $result="" if ! defined $result; 103 warn "$subcd $command: \$?=$?\n" if $?; 104 print "#> $subcd $command ->\n $result\n" if !$? and $opt_v; 105 chomp $result; 106 return $result; 107 } 108} 109 110sub write_files { 111 my %content= map { /WARNING: '([^']+)'/ || die "Bad mojo!"; $1 => $_ } @_; 112 my @files= sort keys %content; 113 my $files= join " and ", map { "'$_'" } @files; 114 foreach my $file (@files) { 115 if (read_file($file) ne $content{$file}) { 116 print "Updating $files\n"; 117 write_file($_,$content{$_}) for @files; 118 return 1; 119 } 120 } 121 print "Reusing $files\n"; 122 return 0; 123} 124 125my $unpushed_commits = '/*no-op*/'; 126my ($read, $branch, $snapshot_created, $commit_id, $describe)= ("") x 5; 127my ($changed, $extra_info, $commit_title, $new_patchnum, $status)= ("") x 5; 128 129if (my $patch_file= read_file(".patch")) { 130 ($branch, $snapshot_created, $commit_id, $describe) = split /\s+/, $patch_file; 131 $extra_info = "git_snapshot_date='$snapshot_created'"; 132 $commit_title = "Snapshot of:"; 133} 134elsif (-d "$srcdir/.git") { 135 # git branch | awk 'BEGIN{ORS=""} /\*/ { print $2 }' 136 ($branch) = map { /\* ([^(]\S*)/ ? $1 : () } backtick("git branch"); 137 my ($remote,$merge); 138 if (length $branch) { 139 $merge= backtick("git config branch.$branch.merge"); 140 $merge =~ s!^refs/heads/!!; 141 $remote= backtick("git config branch.$branch.remote"); 142 } 143 $commit_id = backtick("git rev-parse HEAD"); 144 $describe = backtick("git describe"); 145 my $commit_created = backtick(qq{git log -1 --pretty="format:%ci"}); 146 $new_patchnum = "describe: $describe"; 147 $extra_info = "git_commit_date='$commit_created'"; 148 if (length $branch && length $remote) { 149 # git cherry $remote/$branch | awk 'BEGIN{ORS=","} /\+/ {print $2}' | sed -e 's/,$//' 150 my $unpushed_commit_list = 151 join ",", map { (split /\s/, $_)[1] } 152 grep {/\+/} backtick("git cherry $remote/$merge"); 153 # git cherry $remote/$branch | awk 'BEGIN{ORS="\t\\\\\n"} /\+/ {print ",\"" $2 "\""}' 154 $unpushed_commits = 155 join "", map { ',"'.(split /\s/, $_)[1]."\"\t\\\n" } 156 grep {/\+/} backtick("git cherry $remote/$merge"); 157 if (length $unpushed_commits) { 158 $commit_title = "Local Commit:"; 159 my $ancestor = backtick("git rev-parse $remote/$merge"); 160 $extra_info = "$extra_info 161git_ancestor='$ancestor' 162git_remote_branch='$remote/$merge' 163git_unpushed='$unpushed_commit_list'"; 164 } 165 } 166 if ($changed) { # not touched since init'd. never true. 167 $changed = 'true'; 168 $commit_title = "Derived from:"; 169 $status='"uncommitted-changes"' 170 } else { 171 $status='/*clean-working-directory-maybe*/' 172 } 173 $commit_title ||= "Commit id:"; 174} 175 176# we extract the filename out of the warning header, so dont mess with that 177write_files(<<"EOF_HEADER", <<"EOF_CONFIG"); 178/************************************************************************** 179* WARNING: 'git_version.h' is automatically generated by make_patchnum.pl 180* DO NOT EDIT DIRECTLY - edit make_patchnum.pl instead 181***************************************************************************/ 182#define PERL_GIT_UNCOMMITTED_CHANGES $status 183#define PERL_PATCHNUM "$describe" 184#define PERL_GIT_UNPUSHED_COMMITS\t\t\\ 185$unpushed_commits/*leave-this-comment*/ 186EOF_HEADER 187###################################################################### 188# WARNING: 'lib/Config_git.pl' is generated by make_patchnum.pl 189# DO NOT EDIT DIRECTLY - edit make_patchnum.pl instead 190###################################################################### 191\$Config::Git_Data=<<'ENDOFGIT'; 192git_commit_id='$commit_id' 193git_describe='$describe' 194git_branch='$branch' 195git_uncommitted_changes='$changed' 196git_commit_id_title='$commit_title' 197$extra_info 198ENDOFGIT 199EOF_CONFIG 200# ex: set ts=8 sts=4 sw=4 et ft=perl: 201