2 eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}'
3 & eval 'exec perl -w -S $0 $argv:q'
6 # ******************************************************************
9 # Description: Clone a build tree into an alternate location.
10 # This script is a rewrite of create_ace_build.pl and
11 # does not restrict the user to place the build
12 # in any particular location or that it be used with
13 # ACE_wrappers. Some of the functions were barrowed
14 # from create_ace_build.pl, but were modified quite a
16 # ******************************************************************
18 # ******************************************************************
20 # ******************************************************************
31 # ******************************************************************
33 # ******************************************************************
42 eval 'symlink("", "");';
43 my $hasSymlink = ($@
eq '');
45 # ******************************************************************
47 # ******************************************************************
50 my $matches = !(/^CVS\z/s && ($File::Find
::prune
= 1) ||
51 /^\.svn\z/s && ($File::Find
::prune
= 1) ||
53 /^$exclude\z/s && ($File::Find
::prune
= 1) ||
54 /^\.cvsignore\z/s && ($File::Find
::prune
= 1) ||
55 /^\..*obj\z/s && ($File::Find
::prune
= 1) ||
56 /^Templates\.DB\z/s && ($File::Find
::prune
= 1) ||
57 /^Debug\z/s && ($File::Find
::prune
= 1) ||
58 /^Release\z/s && ($File::Find
::prune
= 1) ||
59 /^Static_Debug\z/s && ($File::Find
::prune
= 1) ||
60 /^Static_Release\z/s && ($File::Find
::prune
= 1)
65 $matches &&= (! -l
$_ &&
66 ! ( -f
$_ && /^core\z/s) &&
85 ! ( -f
$_ && /^.*\.d\z/s )
91 $matches = (! /^.*\.dsp\z/s &&
96 ! /^GNUmakefile.*\z/s &&
98 ! /^\.depend\..*\z/s &&
113 ! /^.*\.classpath\z/s &&
114 ! /^.*\.project\z/s &&
115 ! /^.*\.wrproject\z/s &&
116 ! /^.*\.wrmakefile\z/s &&
122 ## Remove the beginning dot slash as we save the file
123 push(@foundFiles, $File::Find
::name
);
124 $foundFiles[$#foundFiles] =~ s/^\.[\\\/]+//;
132 File
::Find
::find
({wanted
=> \
&findCallback
}, '.');
137 sub backupAndMoveModified
{
138 my($realpath, $linkpath) = @_;
139 my $mltime = -M
$linkpath;
140 my $mrtime = -M
$realpath;
143 ## -M returns the number of days since modification. Therefore,
144 ## a smaller time means that it has been modified more recently.
145 ## This is different than what stat() returns.
147 ## If the hard linked file is newer than the original file, that means
148 ## the link has been broken by something and needs to be "fixed". We
149 ## will back up the original file and move the modified file into it's
151 if ($mltime < $mrtime) {
154 ## Move the real file to a backup
155 unlink("$realpath.bak");
156 if (rename($realpath, "$realpath.bak")) {
157 ## Move the linked file to the real file name
158 if (move
($linkpath, $realpath)) {
162 ## The move failed, so we will attempt to put
163 ## the original file back.
165 rename("$realpath.bak", $realpath);
169 elsif ($mltime != $mrtime || -s
$linkpath != -s
$realpath) {
170 ## The two files are different in some way, we need to make a backup
171 ## so that we don't cause a loss of data/work.
176 ## We were not able to properly deal with this file. We will
177 ## attempt to preserve the modified file.
178 unlink("$linkpath.bak");
179 rename($linkpath, "$linkpath.bak");
185 my($realpath, $linkpath) = @_;
187 if ($^O
eq 'MSWin32' && ! -e
$realpath) {
188 ## If the real file "doesn't exist", then we need to
189 ## look up the short file name.
190 my $short = Win32
::GetShortPathName
($realpath);
192 ## If we were able to find the short file name, then we need to
194 if (defined $short) {
198 ## This should never happen, but there appears to be a bug
199 ## with the underlying Win32 APIs on Windows Server 2003.
200 ## Long paths will cause an error which perl will ignore.
201 ## Unicode versions of the APIs seem to work fine.
202 ## To experiment try Win32 _fullpath() and CreateHardLink with
204 print "WARNING: Skipping $realpath.\n";
209 return link($realpath, $linkpath);
214 my($files, $fullbuild, $dmode, $startdir, $absolute) = @_;
215 my $sdlength = length($startdir) + 1;
216 my $partial = ($absolute ?
undef :
217 substr($fullbuild, $sdlength,
218 length($fullbuild) - $sdlength));
220 foreach my $file (@
$files) {
221 my $fullpath = "$fullbuild/$file";
223 ## We need to make sure that we're not attempting to mix hardlinks
225 if (! -d
$fullpath && ! -l
$fullpath) {
226 my $stat = stat($fullpath);
227 if ($stat->nlink() > 1) {
228 print STDERR
"ERROR: Attempting to mix softlinks ",
229 "with a hardlink build.\n",
230 "$fullpath has ", $stat->nlink(), " links.\n";
238 print "Creating $fullpath\n";
240 if (!mkpath
($fullpath, 0, $dmode)) {
241 print STDERR
"ERROR: Unable to create $fullpath\n";
248 print "symlink $startdir/$file $fullpath\n";
250 if (!symlink("$startdir/$file", $fullpath)) {
251 print STDERR
"ERROR: Unable to symlink $fullpath\n";
256 my $buildfile = "$partial/$file";
257 my $slashcount = ($buildfile =~ tr/\///);
258 my $real = ($slashcount == 0 ?
'./' : ('../' x
$slashcount)) .
261 print "symlink $real $fullpath\n" if ($verbose);
262 if (!symlink($real, $fullpath)) {
263 print STDERR
"ERROR: Unable to symlink $fullpath\n";
271 ## Remove links that point to non-existant files. The subroutine is
272 ## now anonymous to avoid the "will not stay shared" warning for %dirs.
274 File
::Find
::find
({wanted
=> sub {
275 if (-l
$_ && ! -e
$_) {
277 $dirs{$File::Find
::dir
} = 1;
279 print "Removing $File::Find::dir/$_\n";
284 foreach my $key (keys %dirs) {
292 my($files, $fullbuild, $dmode, $startdir) = @_;
295 foreach my $file (@
$files) {
296 my $fullpath = "$fullbuild/$file";
298 if (! -e
$fullpath) {
300 print "Creating $fullpath\n";
302 if (!mkpath
($fullpath, 0, $dmode)) {
303 print STDERR
"ERROR: Unable to create $fullpath\n";
310 ## We need to make sure that we're not attempting to mix hardlinks
313 print STDERR
"ERROR: Attempting to mix hardlinks ",
314 "with a softlink build.\n",
315 "$fullpath is a softlink.\n";
318 backupAndMoveModified
($file, $fullpath);
320 if (! -e
$fullpath) {
322 print "hardlink $file $fullpath\n";
324 if (!hardlink
($file, $fullpath)) {
325 print STDERR
"ERROR: Unable to link $fullpath\n";
330 ## If we successfully linked the file or it already exists,
331 ## we need to keep track of it.
332 push(@hardlinks, $file);
336 ## Remove links that point to non-existant files
337 my $lfh = new FileHandle
();
338 my $txt = "$fullbuild/clone_build_tree.links";
339 if (open($lfh, $txt)) {
345 my $full = "$fullbuild/$line";
347 $dirs{dirname
($full)} = 1;
348 print "Removing $full\n" if ($verbose);
352 foreach my $key (keys %dirs) {
357 ## Rewrite the link file.
359 if (open($lfh, ">$txt")) {
360 foreach my $file (@hardlinks) {
361 print $lfh "$file\n";
371 my($absolute, $dmode, $hardlink, $builddir, $builds) = @_;
373 my $starttime = time();
374 my $startdir = getcwd
();
376 ## Ensure that the build directory exists and is writable
377 mkpath
($builddir, 0, $dmode);
378 if (! -d
$builddir || ! -w
$builddir) {
379 print STDERR
"ERROR: Unable to create or write to $builddir\n";
383 ## Search for the clonable files
384 print "Searching $startdir for files...\n";
385 my $files = getFileList
();
386 my $findtime = time() - $starttime;
387 print 'Found ', scalar(@
$files), ' files and directories in ',
388 $findtime, ' second', ($findtime == 1 ?
'' : 's'), ".\n";
390 foreach my $build (@
$builds) {
391 my $fullbuild = "$builddir/$build";
393 ## Create all of the links for this build
395 print "Updating $fullbuild\n";
398 print "Creating $fullbuild\n";
399 mkpath
($fullbuild, 0, $dmode);
403 $status += hardlinkFiles
($files, $fullbuild, $dmode, $startdir);
406 $status += symlinkFiles
($files, $fullbuild,
407 $dmode, $startdir, $absolute);
409 print "Finished in $fullbuild\n";
412 print 'Total time: ', time() - $starttime, " seconds.\n" if ($status == 0);
421 print STDERR
"$msg\n" if (defined $msg);
423 my $base = basename
($0);
424 my $spc = ' ' x
(length($base) + 8);
426 print STDERR
"$base v$version\n\n",
427 "Create a tree identical in layout to the current directory\n",
428 "with the use of ", ($hasSymlink ?
"symbolic links or " : ''),
430 "Usage: $base [-b <builddir>] [-d <dmode>] [-f] [-n]",
431 ($hasSymlink ?
"[-a] [-l] " : ''),
433 $spc, "[build names...]\n\n",
435 "-a Use absolute paths when creating soft links.\n" .
436 "-l Use hard links instead of soft links.\n" : ''),
437 "-b Set the build directory. It defaults to the ",
438 "<current directory>/build.\n",
439 "-d Set the directory permissions mode.\n",
440 "-f Link build files (Makefile, .dsw, .sln, .etc).\n",
441 "-n Link non-build files normally avoided (.o,.so, etc.).\n",
442 "-s Set the start directory. It defaults to the ",
443 "<current directory>.\n",
444 "-v Enable verbose mode.\n";
450 # ******************************************************************
452 # ******************************************************************
456 my $hardlink = !$hasSymlink;
461 for(my $i = 0; $i <= $#ARGV; ++$i) {
462 if ($ARGV[$i] eq '-a') {
465 elsif ($ARGV[$i] eq '-b') {
467 if (defined $ARGV[$i]) {
468 $builddir = $ARGV[$i];
470 ## Convert backslashes to slashes
471 $builddir =~ s/\\/\//g
;
473 ## Remove trailing slashes
474 $builddir =~ s/\/+$//;
476 ## Remove duplicate slashes
477 while($builddir =~ s/\/\//\//g
) {
481 usageAndExit
('-b requires an argument');
484 elsif ($ARGV[$i] eq '-d') {
486 if (defined $ARGV[$i]) {
490 usageAndExit
('-d requires an argument');
493 elsif ($ARGV[$i] eq '-f') {
496 elsif ($ARGV[$i] eq '-l') {
499 elsif ($ARGV[$i] eq '-n') {
502 elsif ($ARGV[$i] eq '-v') {
505 elsif ($ARGV[$i] eq '-s') {
507 if (defined $ARGV[$i]) {
508 $startdir = $ARGV[$i];
511 usageAndExit
('-s requires an argument');
514 elsif ($ARGV[$i] =~ /^-/) {
515 usageAndExit
('Unknown option: ' . $ARGV[$i]);
518 push(@builds, $ARGV[$i]);
522 if (defined $startdir && !chdir($startdir)) {
523 print "ERROR: Unable to change directory to $startdir\n";
527 $builddir = getcwd
() . '/build' if (!defined $builddir);
529 if (index($builddir, getcwd
()) == 0) {
530 $exclude = substr($builddir, length(getcwd
()) + 1);
531 $exclude =~ s/([\+\-\\\$\[\]\(\)\.])/\\$1/g;
532 $exclude =~ s/.*?([^\/]+)$/$1/;
538 if (!defined $builds[0]) {
540 if (chdir($builddir)) {
545 usageAndExit
('There are no builds to update.');
549 exit(linkFiles
($absolute, $dmode, $hardlink, $builddir, \
@builds));