diff options
Diffstat (limited to 'ACE/bin/create_ace_build.pl')
-rwxr-xr-x | ACE/bin/create_ace_build.pl | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/ACE/bin/create_ace_build.pl b/ACE/bin/create_ace_build.pl new file mode 100755 index 00000000000..e7876bd3791 --- /dev/null +++ b/ACE/bin/create_ace_build.pl @@ -0,0 +1,463 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# +# Creates an ACE build tree in directory "build/<build name>" below the $ACE_ROOT +# directory. The build tree directory structure mirrors that of the ACE +# top level directory structure, except that instead of containing any plain +# files, it contains only links to the files in the ACE top level structure. +# Symbolic links will be used instead of hard links if available. +# +# Makefiles, projects, and other build files are not linked. This allows +# use of MPC to generate the correct project types as needed. Use the +# -nompc option to disable this. +# +# This program has a similar purpose to "clone", but in addition to +# only creating symlinks (clone creates hard links, by default), this +# script: +# 1) uses relative rather than absolute symlinks, +# 2) tries not to put junk files into the build tree, +# 3) only creates a new tree in a build/ directory below the current, +# top level ACE directory (it's a feature :-), but it does enforce +# consistency). +# +# This program can be re-run on a build tree at any time in order to +# update it. It will add links for newly added files, and remove +# any that are no longer valid. +# Specifying no command line options will cause all current builds +# to be updated. +# +# If the <build name> starts with "build/", that part will be removed +# from it. + +use File::Find (); +use File::Basename; +use FileHandle; +use File::stat; +use File::Copy; +use File::Path; + +print "You should consider using clone_build_tree.pl found with MPC\n"; + +$usage = "usage: $0 -? | [-a] [-d <directory mode>] [-v] [-nompc] <build name>\n"; +$directory_mode = 0777; #### Will be modified by umask, also. +$verbose = 0; +$mpc = 1; #### When using mpc, we don't want links created for mpc-generated files. +$update_all = 1; +$source='.'; +$absolute = 0; + +#### +#### Check that we're in an ACE "top level" directory. +#### +unless (-d 'ace' && -d 'include') { + die "Must be in an ACE top level (ACE_ROOT) directory!\n"; +} +if (-e 'create_ace_build.links') { + die "Must be in an ACE top level (ACE_ROOT) directory!\n"; +} + +$perl_version = $] + 0; +if ($perl_version >= 5) { + #### Use an eval so that this script will compile with perl4. + eval <<'PERL5_CWD' + require Cwd; + sub cwd { + Cwd::getcwd (); + } +PERL5_CWD +} else { + sub cwd { + local ($pwd); + + chop ($pwd = `pwd`); + $pwd; + } +} + +my($starting_dir) = cwd (); +my(@nlinks) = (); +my(@build_re) = (); + +print "Creating or updating builds in $starting_dir\n"; + +#### If the $linked file is newer than the real file then +#### backup the real file, and replace it with the linked +#### version. + +sub backup_and_copy_changed { + my($real, $linked) = @_; + my($status_real) = stat($real); + + if (! $status_real) { + die "ERROR: cannot access $real.\n"; + } + + my($status_linked) = stat($linked); + if ($status_linked->mtime > $status_real->mtime) { + rename($real, $real . '.bak'); + rename($linked, $real); + return 1; + } + + if ($status_real->mtime != $status_linked->mtime) { + unlink($linked); + return 1; + } + if ($status_real->size != $status_linked->size) { + unlink($linked); + return 1; + } + return 0; +} + +sub cab_link { + my($real,$linked,$build_regex) = @_; + + my($status) = 0; + if ($^O eq 'MSWin32') { + my($fixed) = $linked; + $fixed =~ s/$build_regex//; + push(@nlinks, $fixed); + + my($curdir) = "$starting_dir/" . dirname($linked); + if (! -d $curdir) { + die "ERROR: Dir not found: $curdir\n"; + } + $status = chdir($curdir); + if (! $status) { + die "ERROR: cab_link() chdir " . $curdir . " failed.\n"; + } + + my($base_linked) = basename($linked); + + if (! -e $real) { + ## If the real file "doesn't exist", then we need to change back to + ## the starting directory and look up the short file name. + chdir($starting_dir); + my($short) = Win32::GetShortPathName($fixed); + + ## If we were able to find the short file name, then we need to + ## modyfy $real. Note, we don't need to change back to $curdir + ## unless the short name lookup was successful. + if (defined $short) { + ## Replace a section of $real (the part that isn't a relative + ## path) with the short file name. The hard link will still have + ## the right name, it's just pointing to the short name. + substr($real, length($real) - length($fixed)) = $short; + + ## Get back to the right directory for when we make the hard link + chdir($curdir); + } + else { + ## This should never happen, but there appears to be a bug + ## with the underlying win32 apis on Windows Server 2003. + ## Long paths will cause an error which perl will ignore. + ## Unicode versions of the apis seem to work fine. + ## To experiment try Win32 _fullpath() and CreateHardLink with + ## long paths. + print "ERROR : Skipping $real.\n"; + return; + } + } + + if (-e $base_linked) { + if (! backup_and_copy_changed($real, $base_linked)) { + return; + } + } + + print "link $real $linked\n" if $verbose; + $status = link ($real, $base_linked); + if (! $status) { + ## Once again, this happens for long paths on Win2003 + print "ERROR: Can't link $real\n"; + return; + } + chdir($starting_dir); + } else { + print "$symlink $real $linked\n" if $verbose; + $status = symlink ($real, $linked); + } + if (!$status) { + die "$0: $real -> $linked failed\n"; + } +} + +#### +#### Process command line args. +#### +while ($#ARGV >= 0 && $ARGV[0] =~ /^-/) { + if ($ARGV[0] eq '-v') { + $verbose = 1; + } elsif ($ARGV[0] eq '-d') { + if ($ARGV[1] =~ /^\d+$/) { + $directory_mode = eval ($ARGV[1]); shift; + } else { + warn "$0: must provide argument for -d option\n"; + die $usage; + } + } elsif ($ARGV[0] eq '-a' && ! ($^O eq 'MSWin32')) { + $source = &cwd (); + $absolute = 1; + } elsif ($ARGV[0] =~ /-[?hH]$/) { + die "$usage"; + } elsif ($ARGV[0] eq '-nompc') { + $mpc = 0; + } else { + warn "$0: unknown option $ARGV[0]\n"; + die $usage; + } + shift; +} + +@builds = (); + +if ($#ARGV == 0) { + $update_all = 0; + $builds[0] = $ARGV[0]; + $builds[0] =~ s%^build[/\\]%%; #### remove leading "build/", if any + $builds[0] = "build/$builds[0]"; +} else { + @builds = glob "build/*"; +} + +sub create_build_regex { + if ($^O eq 'MSWin32') { + for ($idx = 0; $idx <= $#builds; $idx++) { + ## Get the original build name + $build_re[$idx] = $builds[idx]; + + ## Remove any trailing slashes + $build_re[$idx] =~ s/[\\\/]+$//; + + ## Add a single trailing slash + $build_re[$idx] .= '/'; + + ## Escape any special characters + $build_re[$idx] =~ s/([\\\$\[\]\(\)\.])/\\$1/g; + } + } +} + +create_build_regex(); + +# all builds go in ACE_wrappers\build +unless (-d "$starting_dir/build") { + print "Creating $starting_dir/build\n"; + mkdir ("$starting_dir/build", $directory_mode); +} +foreach $build (@builds) { + unless (-d "$starting_dir/$build") { + print "Creating $starting_dir/$build\n"; + mkpath ("$starting_dir/$build", 0, $directory_mode); + } +} + +#### +#### Get all ACE plain file and directory names. +#### +@files = (); + +sub wanted { + my ($dev,$ino,$mode,$nlink,$uid,$gid); + + $matches = ! ( + /^CVS\z/s && ($File::Find::prune = 1) + || + /^build\z/s && ($File::Find::prune = 1) + || + /^\..*obj\z/s && ($File::Find::prune = 1) + || + /^Templates\.DB\z/s && ($File::Find::prune = 1) + || + /^Debug\z/s && ($File::Find::prune = 1) + || + /^Release\z/s && ($File::Find::prune = 1) + || + /^Static_Debug\z/s && ($File::Find::prune = 1) + || + /^Static_Release\z/s && ($File::Find::prune = 1) + || + /^\.svn\z/s && ($File::Find::prune = 1) + ); + + $matches = $matches && + ( + ($nlink || (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) && + ! -l $_ && + ! /^core\z/s && + ! /^.*\.state\z/s && + ! /^.*\.so\z/s && + ! /^.*\.[oa]\z/s && + ! /^.*\.dll\z/s && + ! /^.*\.lib\z/s && + ! /^.*\.obj\z/s && + ! /^.*~\z/s && + ! /^\.\z/s && + ! /^\.#.*\z/s && + ! /^.*\.log\z/s + ); + + if ($mpc && $matches) { + $matches = + ($File::Find::dir =~ /include\/makeinclude*/) || + ( + ! /^.*\.dsp\z/s && + ! /^.*\.vcproj\z/s && + ! /^.*\.bor\z/s && + ! /^.*\.dsw\z/s && + ! /^.*\.sln\z/s && + ! /^.*\.vcp\z/s && + ! /^.*\.nmake\z/s && + ! /^.*\.am\z/s && + ! /^.*\.vcw\z/s && + ! /^.*\.mak\z/s && + ! /^.*\.bld\z/s && + ! /^.*\.icc\z/s && + ! /^.*\.icp\z/s && + ! /^.*\.ncb\z/s && + ! /^.*\.opt\z/s && + ! /^.*\.bak\z/s && + ! /^.*\.ilk\z/s && + ! /^.*\.pdb\z/s && + ! /^\.cvsignore\z/s && + ! /^\.disable\z/s && + ! /^GNUmakefile.*\z/s + ); + } + + if ($matches) { + push(@files, $File::Find::name); + } +} + +File::Find::find({wanted => \&wanted}, '.'); + +print "Found $#files files and directories.\n"; + +#### +#### Create directories and symlinks to files. +#### +foreach $file (@files) { + $file =~ s%^./%%g; #### excise leading ./ directory component + my($fullname) = "$starting_dir/$file"; + for ($idx = 0; $idx <= $#builds; $idx++) { + my($build) = $builds[$idx]; + if (-d $fullname) { + unless (-d "$starting_dir/$build/$file") { + print "Creating $build/$file\n" if $verbose; + mkdir ("$starting_dir/$build/$file", $directory_mode); + } + } else { + unless (($^O ne 'MSWin32') && (-e "$build/$file")) { + if (!$absolute) { + $up = '..'; + while ($build =~ m%/%g) { + $up .= '/..'; + } + while ($file =~ m%/%g) { + $up .= '/..'; + } + cab_link("$up/$file", "$build/$file", $build_re[$idx]); + } else { + $path = $source . '/' . $file; + cab_link("$path", "$build/$file", $build_re[$idx]); + } + + } + } + } +} + +print "Finished creating and updating links.\n"; + +foreach $build (@builds) { + #### + #### Find all the symlinks in the build directory, and remove ones + #### that are no longer actually linked to a file. + #### + + if ($^O eq 'MSWin32') { + my($lfh) = new FileHandle(); + my($links_file) = "$starting_dir/$build/create_ace_build.links"; + if (-e $links_file) { + if (open($lfh, $links_file)) { + while(<$lfh>) { + my($line) = $_; + $line =~ s/\s+$//; + if (-e "$starting_dir/$line") { + ## The links were already added in cab_link when they + ## were checked for changes. + } else { + print "Removing $build/$line \n" if $verbose; + unlink("$starting_dir/$build/$line") || warn "$0: unlink of $build/$line failed\n"; + } + } + close($lfh); + } + unless (unlink($links_file)) { + die "Couldn't delete links file.\n"; + } + } + print "Writing $#nlinks links to link file.\n"; + if (open($lfh, ">$links_file")) { + foreach my $lnk (@nlinks) { + print $lfh "$lnk\n"; + } + close($lfh); + } else { + die "Couldn't open links file.\n"; + } + } + else { + @lfiles = (); + + sub lcheck { + ## There's no way to know if we have hard linked back to a now + ## non-existent file. So, just do the normal -l on the file + ## which will cause no files to be pushed on Windows. + if (-l $_) { + push(@lfiles, $File::Find::name); + } + } + + File::Find::find({wanted => \&lcheck}, $build); + + foreach (@lfiles) { + local @s = stat $_; + if ($#s == -1) { + print "Removing $_ \n" if $verbose; + unlink $_ || warn "$0: unlink of $_ failed\n"; + } + } + } + + #### + #### Done: print message. + #### + print "\nCompleted creation of $build/.\n"; + +foreach $build (@builds) { + unless (-d "$starting_dir/$build") { + print "Creating $starting_dir/$build\n"; + mkdir ("$starting_dir/$build", $directory_mode); + } + + + if (! -e "$starting_dir/$build/ace/config.h") { + print "Be sure to setup $build/ace/config.h"; + } + + if ($^O ne 'MSWin32' && + ! -e "$starting_dir/$build/include/makeinclude/platform_macros.GNU") { + print " and\n$build/include/makeinclude/platform_macros.GNU"; + } + print ".\n"; +} + +} + +#### EOF |