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 borrowed
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) ||
52 /^\.git\z/s && ($File::Find
::prune
= 1) ||
54 /^$exclude\z/s && ($File::Find
::prune
= 1) ||
55 /^\.cvsignore\z/s && ($File::Find
::prune
= 1) ||
56 /^\..*obj\z/s && ($File::Find
::prune
= 1) ||
57 /^Templates\.DB\z/s && ($File::Find
::prune
= 1) ||
58 /^Debug\z/s && ($File::Find
::prune
= 1) ||
59 /^Release\z/s && ($File::Find
::prune
= 1) ||
60 /^Static_Debug\z/s && ($File::Find
::prune
= 1) ||
61 /^Static_Release\z/s && ($File::Find
::prune
= 1)
66 $matches &&= (! -l
$_ &&
67 ! ( -f
$_ && /^core\z/s) &&
86 ! ( -f
$_ && /^.*\.d\z/s )
92 $matches = (! /^.*\.dsp\z/s &&
97 ! /^GNUmakefile.*\z/s &&
99 ! /^\.depend\..*\z/s &&
114 ! /^.*\.classpath\z/s &&
115 ! /^.*\.project\z/s &&
116 ! /^.*\.wrproject\z/s &&
117 ! /^.*\.wrmakefile\z/s &&
123 ## Remove the beginning dot slash as we save the file
124 push(@foundFiles, $File::Find
::name
);
125 $foundFiles[$#foundFiles] =~ s/^\.[\\\/]+//;
133 File
::Find
::find
({wanted
=> \
&findCallback
}, '.');
138 sub backupAndMoveModified
{
139 my($realpath, $linkpath) = @_;
140 my $mltime = -M
$linkpath;
141 my $mrtime = -M
$realpath;
144 ## -M returns the number of days since modification. Therefore,
145 ## a smaller time means that it has been modified more recently.
146 ## This is different than what stat() returns.
148 ## If the hard linked file is newer than the original file, that means
149 ## the link has been broken by something and needs to be "fixed". We
150 ## will back up the original file and move the modified file into it's
152 if ($mltime < $mrtime) {
155 ## Move the real file to a backup
156 unlink("$realpath.bak");
157 if (rename($realpath, "$realpath.bak")) {
158 ## Move the linked file to the real file name
159 if (move
($linkpath, $realpath)) {
163 ## The move failed, so we will attempt to put
164 ## the original file back.
166 rename("$realpath.bak", $realpath);
170 elsif ($mltime != $mrtime || -s
$linkpath != -s
$realpath) {
171 ## The two files are different in some way, we need to make a backup
172 ## so that we don't cause a loss of data/work.
177 ## We were not able to properly deal with this file. We will
178 ## attempt to preserve the modified file.
179 unlink("$linkpath.bak");
180 rename($linkpath, "$linkpath.bak");
186 my($realpath, $linkpath) = @_;
188 if ($^O
eq 'MSWin32' && ! -e
$realpath) {
189 ## If the real file "doesn't exist", then we need to
190 ## look up the short file name.
191 my $short = Win32
::GetShortPathName
($realpath);
193 ## If we were able to find the short file name, then we need to
195 if (defined $short) {
199 ## This should never happen, but there appears to be a bug
200 ## with the underlying Win32 APIs on Windows Server 2003.
201 ## Long paths will cause an error which perl will ignore.
202 ## Unicode versions of the APIs seem to work fine.
203 ## To experiment try Win32 _fullpath() and CreateHardLink with
205 print "WARNING: Skipping $realpath.\n";
210 return link($realpath, $linkpath);
215 my($files, $fullbuild, $dmode, $startdir, $absolute) = @_;
216 my $sdlength = length($startdir) + 1;
217 my $partial = ($absolute ?
undef :
218 substr($fullbuild, $sdlength,
219 length($fullbuild) - $sdlength));
221 foreach my $file (@
$files) {
222 my $fullpath = "$fullbuild/$file";
224 ## We need to make sure that we're not attempting to mix hardlinks
226 if (! -d
$fullpath && ! -l
$fullpath) {
227 my $stat = stat($fullpath);
228 if ($stat->nlink() > 1) {
229 print STDERR
"ERROR: Attempting to mix softlinks ",
230 "with a hardlink build.\n",
231 "$fullpath has ", $stat->nlink(), " links.\n";
239 print "Creating $fullpath\n";
241 if (!mkpath
($fullpath, 0, $dmode)) {
242 print STDERR
"ERROR: Unable to create $fullpath\n";
249 print "symlink $startdir/$file $fullpath\n";
251 if (!symlink("$startdir/$file", $fullpath)) {
252 print STDERR
"ERROR: Unable to symlink $fullpath\n";
257 my $buildfile = "$partial/$file";
258 my $slashcount = ($buildfile =~ tr/\///);
259 my $real = ($slashcount == 0 ?
'./' : ('../' x
$slashcount)) .
262 print "symlink $real $fullpath\n" if ($verbose);
263 if (!symlink($real, $fullpath)) {
264 print STDERR
"ERROR: Unable to symlink $fullpath\n";
272 ## Remove links that point to non-existant files. The subroutine is
273 ## now anonymous to avoid the "will not stay shared" warning for %dirs.
275 File
::Find
::find
({wanted
=> sub {
276 if (-l
$_ && ! -e
$_) {
278 $dirs{$File::Find
::dir
} = 1;
280 print "Removing $File::Find::dir/$_\n";
285 foreach my $key (keys %dirs) {
293 my($files, $fullbuild, $dmode, $startdir) = @_;
296 foreach my $file (@
$files) {
297 my $fullpath = "$fullbuild/$file";
299 if (! -e
$fullpath) {
301 print "Creating $fullpath\n";
303 if (!mkpath
($fullpath, 0, $dmode)) {
304 print STDERR
"ERROR: Unable to create $fullpath\n";
311 ## We need to make sure that we're not attempting to mix hardlinks
314 print STDERR
"ERROR: Attempting to mix hardlinks ",
315 "with a softlink build.\n",
316 "$fullpath is a softlink.\n";
319 backupAndMoveModified
($file, $fullpath);
321 if (! -e
$fullpath) {
323 print "hardlink $file $fullpath\n";
325 if (!hardlink
($file, $fullpath)) {
326 print STDERR
"ERROR: Unable to link $fullpath\n";
331 ## If we successfully linked the file or it already exists,
332 ## we need to keep track of it.
333 push(@hardlinks, $file);
337 ## Remove links that point to non-existant files
338 my $lfh = new FileHandle
();
339 my $txt = "$fullbuild/clone_build_tree.links";
340 if (open($lfh, $txt)) {
346 my $full = "$fullbuild/$line";
348 $dirs{dirname
($full)} = 1;
349 print "Removing $full\n" if ($verbose);
353 foreach my $key (keys %dirs) {
358 ## Rewrite the link file.
360 if (open($lfh, ">$txt")) {
361 foreach my $file (@hardlinks) {
362 print $lfh "$file\n";
372 my($absolute, $dmode, $hardlink, $builddir, $builds) = @_;
374 my $starttime = time();
375 my $startdir = getcwd
();
377 ## Ensure that the build directory exists and is writable
378 mkpath
($builddir, 0, $dmode);
379 if (! -d
$builddir || ! -w
$builddir) {
380 print STDERR
"ERROR: Unable to create or write to $builddir\n";
384 ## Search for the clonable files
385 print "Searching $startdir for files...\n";
386 my $files = getFileList
();
387 my $findtime = time() - $starttime;
388 print 'Found ', scalar(@
$files), ' files and directories in ',
389 $findtime, ' second', ($findtime == 1 ?
'' : 's'), ".\n";
391 foreach my $build (@
$builds) {
392 my $fullbuild = "$builddir/$build";
394 ## Create all of the links for this build
396 print "Updating $fullbuild\n";
399 print "Creating $fullbuild\n";
400 mkpath
($fullbuild, 0, $dmode);
404 $status += hardlinkFiles
($files, $fullbuild, $dmode, $startdir);
407 $status += symlinkFiles
($files, $fullbuild,
408 $dmode, $startdir, $absolute);
410 print "Finished in $fullbuild\n";
413 print 'Total time: ', time() - $starttime, " seconds.\n" if ($status == 0);
422 print STDERR
"$msg\n" if (defined $msg);
424 my $base = basename
($0);
425 my $spc = ' ' x
(length($base) + 8);
427 print STDERR
"$base v$version\n\n",
428 "Create a tree identical in layout to the current directory\n",
429 "with the use of ", ($hasSymlink ?
"symbolic links or " : ''),
431 "Usage: $base [-b <builddir>] [-d <dmode>] [-f] [-n]",
432 ($hasSymlink ?
"[-a] [-l] " : ''),
434 $spc, "[build names...]\n\n",
436 "-a Use absolute paths when creating soft links.\n" .
437 "-l Use hard links instead of soft links.\n" : ''),
438 "-b Set the build directory. It defaults to the ",
439 "<current directory>/build.\n",
440 "-d Set the directory permissions mode.\n",
441 "-f Link build files (Makefile, .dsw, .sln, .etc).\n",
442 "-n Link non-build files normally avoided (.o,.so, etc.).\n",
443 "-s Set the start directory. It defaults to the ",
444 "<current directory>.\n",
445 "-v Enable verbose mode.\n";
451 # ******************************************************************
453 # ******************************************************************
457 my $hardlink = !$hasSymlink;
462 for(my $i = 0; $i <= $#ARGV; ++$i) {
463 if ($ARGV[$i] eq '-a') {
466 elsif ($ARGV[$i] eq '-b') {
468 if (defined $ARGV[$i]) {
469 $builddir = $ARGV[$i];
471 ## Convert backslashes to slashes
472 $builddir =~ s/\\/\//g
;
474 ## Remove trailing slashes
475 $builddir =~ s/\/+$//;
477 ## Remove duplicate slashes
478 while($builddir =~ s/\/\//\//g
) {
482 usageAndExit
('-b requires an argument');
485 elsif ($ARGV[$i] eq '-d') {
487 if (defined $ARGV[$i]) {
491 usageAndExit
('-d requires an argument');
494 elsif ($ARGV[$i] eq '-f') {
497 elsif ($ARGV[$i] eq '-l') {
500 elsif ($ARGV[$i] eq '-n') {
503 elsif ($ARGV[$i] eq '-v') {
506 elsif ($ARGV[$i] eq '-s') {
508 if (defined $ARGV[$i]) {
509 $startdir = $ARGV[$i];
512 usageAndExit
('-s requires an argument');
515 elsif ($ARGV[$i] =~ /^-/) {
516 usageAndExit
('Unknown option: ' . $ARGV[$i]);
519 push(@builds, $ARGV[$i]);
523 if (defined $startdir && !chdir($startdir)) {
524 print "ERROR: Unable to change directory to $startdir\n";
528 $builddir = getcwd
() . '/build' if (!defined $builddir);
530 if (index($builddir, getcwd
()) == 0) {
531 $exclude = substr($builddir, length(getcwd
()) + 1);
532 $exclude =~ s/([\+\-\\\$\[\]\(\)\.])/\\$1/g;
533 $exclude =~ s/.*?([^\/]+)$/$1/;
539 if (!defined $builds[0]) {
541 if (chdir($builddir)) {
546 usageAndExit
('There are no builds to update.');
550 exit(linkFiles
($absolute, $dmode, $hardlink, $builddir, \
@builds));