- #!/usr/bin/perl
- #
- # PERFORCE BUNDLE WRAPPER v0.2
- # Rachel 'Sparks' Blackman <sparks@noderunner.net>
- #
- # NO GUARANTEES OF OPERATION WHATSOEVER ARE MADE. You're welcome to use
- # this file however you want (though I'd love credit to be left in it),
- # but I can't be held responsible for the operation of it. It works on
- # my machines, I can't promise more. I'd welcome changes being submitted
- # back, this is an ugly hack and I'm sure there are better ways to do it. :)
- #
- # This wrapper will watch for Perforce operations performed on
- # files matching certain rulesets, and execute those rules before
- # actually passing the operations on to the real p4 client.
- #
- # This is intended to allow MacOS X .nib files and other 'bundles'
- # to be tarballed on p4 operations so they can be treated as a single
- # file, for purposes of making Xcode work with Perforce.
- #
- # It could, however, be presumably used for other files easily enough,
- # as it should work under any UNIX p4 client situation as well. Right
- # now, however, it's just written to do .nib files.
- #
- # THE ONE SIGNIFICANT GOTCHA IS THAT YOU MUST MANUALLY CHECK OUT NIB FILES.
- # Interface Builder will whine if you open a nib file which is not
- # opened for edit (I managed that much), but Interface Builder does not
- # know to run an SCM edit command. However, you can use the SCM 'Edit'
- # command from the project window, and it will properly check out the
- # NIB file and set it up for edit. Yay!
- #
- # Occasionally, Xcode seems to get brain-damaged about the 'add' command
- # and lose track of what it's trying to do with .nib files, and lose the
- # 'SCM Edit' option in the UI. You can bring it back in sync from the
- # command-line, but if anyone knows a good way to fix this, let me know.
- #
- # Also, at present, any p4 command which requires stdin to the user doesn't
- # work with this wrapper, i.e., 'p4 submit' doesn't work if you don't
- # provide the changelist description on the command line. If someone can
- # think of a good way around that, I'd love to hear it. :)
- #
- # To install, just rename your existing p4 to p4.orig, and copy this
- # script (set executable) into the directory as p4.
- #
- # If you are modifying this wrapper, you have my deepest sympathies. ;)
- #
- ####
-
- use Getopt::Std;
-
- my $p4 = "";
-
- # First we find the actual local Perforce executable
- # choosing from a list of possibilities. We prefer '.orig'
- # because most likely /we/ are installed as 'p4', after all!
- #
- for $p4chk ("/usr/local/bin/p4.orig","/usr/bin/p4.orig",
- "/usr/local/bin/p4","/usr/bin/p4") {
-
- if (!$p4 && -e $p4chk) {
- $p4 = $p4chk;
- }
- }
-
- # A little bit of sanity checking, just in case we didn't find
- # what we wanted..
- #
- if (!$p4) {
- print (STDERR "Perforce wrapper unable to find Perforce executable.\n");
- }
-
- # Parse all of the command line parameters
- getopts('GVsc:C:d:H:L:p:P:u:x:', \%opts);
-
- # Get our command...
- #
- $command = $ARGV[0];
-
- # And all the parameters to it. There's probably a prettier way to
- # do this, but let's see you write one at 3am! ;)
- #
- @params = @ARGV;
- @params = reverse(@params);
- pop(@params);
- @params = reverse(@params);
-
- # Okay. We now have all our data, so we can check for specific types
- # of things. Really, what we most care about are 'add', 'submit',
- # 'fstat' and 'sync' calls.
- #
- # All of these require changes to the call we make to the p4 client
- # command, but sync will also require some post-processing when
- # the command is done. Ugh!
- #
- if (($command eq 'add') || ($command eq 'submit')) {
- # Add and submit are similar because both will need to actually
- # tarball any files matching our patterns.
- #
- for ($count = 0; $count < @params; $count++) {
- $basefile = $params[$count];
- $revision = "";
- $file = $basefile;
-
- if ($basefile =~ /(.*)#([0123456789]+)/) {
- $file = $1;
- $revision = $2;
- }
-
- if (($file =~ /(.*)\.nib/i) || ($file =~ /(.*)\.framework/i)) {
- # We have a bundle. Let's build a tarball.
- #
- $dir = $opts{'d'};
- if ($dir eq "") {
- $dir = '.';
- }
- $tarball = "${file}.tar";
- $cmd = "cd $dir ; tar -cf $tarball $file";
-
- system("$cmd");
-
- if ($revision ne "") {
- $tarball .= "#${revision}";
- }
-
- $params[$count] = $tarball;
- }
- }
- }
-
- if (($command eq 'fstat') || ($command eq 'sync') || ($command eq 'filelog')
- || ($command eq 'revert') || ($command eq 'print') || ($command eq 'edit')) {
- # These are all similar because we need to modify
- # the filename parameters
- #
- for ($count = 0; $count < @params; $count++) {
- $basefile = $params[$count];
- $revision = '';
- $file = $basefile;
-
- if ($basefile =~ /(.*)#([0123456789]+)/) {
- $file = $1;
- $revision = $2;
- }
-
- if (($file =~ /(.*)\.nib/i) || ($file =~ /(.*).framework/i)) {
- $tarball = "${file}.tar";
-
- if ($revision ne '') {
- $tarball .= "#${revision}";
- }
- $params[$count] = $tarball;
- }
- }
- }
-
- # Assemble the command again with all of our options,
- # and any modifications we've made to the parameters.
- # This command will then be passed on to the real p4 command,
- # transparently. By this point, we have all our tarball conversions
- # which we need going INTO p4.
- #
- $p4cmd = $p4;
-
- foreach $opt (sort(keys %opts)) {
- $p4cmd .= " -$opt";
- if ($opts{$opt} != 1) {
- $p4cmd .= " $opts{$opt}";
- }
- }
-
- $p4cmd .= " $command";
- $p4cmd .= " @params";
-
- # FOR DEBUG PURPOSES FOR NOW:
- # open (P4LOG, ">> /tmp/p4wrap.log");
- # print (P4LOG "$p4cmd\n");
- # close (P4LOG);
-
- # Now comes the fun part; we have to run the p4 command, and
- # /if/ it's a sync, we have to watch for our .nib.tar files,
- # in order to unpack them /and/ toss the proper .nib filename
- # out for Xcode to parse. Fun stuff.
- #
- open (P4PIPE, "$p4cmd |") || die ("Couldn't execute Perforce command!");
- while ($line = <P4PIPE>) {
-
- chomp ($line);
-
- if ($command eq "sync") {
- # Watch for .nib.tar files in the sync, untar, and output
- # the line with just a .nib instead.
- #
- if ($line =~ /(.*)#([0123456789]+) - ([^ ]*)(.*)/) {
- $depotfile = $1;
- $revision = $2;
- $action = $3;
- $clientfile = $4;
-
- if ($clientfile =~ /(.*)\.nib\.tar/) {
- # It's a nib. We'll map the filename back, and we
- # want to untar it as well.
- #
- $dir = $opts{'d'};
- if ($dir eq '') {
- $dir = '.';
- }
-
- $tarcmd = "cd $dir ; tar xf $clientfile";
- system($tarcmd);
- $clientfile = "${1}.nib";
- $modcmd = "cd $dir ; chmod -R a-w $clientfile";
- system($modcmd);
- }
- elsif ($clientfile =~ /(.*)\.framework\.tar/) {
- # It's a framework. We'll map the filename back, and we
- # want to untar it as well.
- #
- $dir = $opts{'d'};
- if ($dir eq '') {
- $dir = '.';
- }
-
- $tarcmd = "cd $dir ; tar xf $clientfile";
- system($tarcmd);
- $clientfile = "${1}.framework";
- $modcmd = "cd $dir ; chmod -R a-w $clientfile";
- system($modcmd);
- }
-
- $line = "${depotfile}#${revision} - ${action} ${clientfile}";
- }
- }
- elsif ($command eq "edit") {
- # Watch for .nib.tar files in the edit, untar, and output
- # the line with just a .nib instead.
- #
- if ($line =~ /(.*)#([0123456789]+) - (.*)/) {
- $depotfile = $1;
- $revision = $2;
- $action = $3;
-
- if ($depotfile =~ /(.*)\.nib\.tar/) {
- # It's a nib. We'll map the filename back, and we
- # want to untar it as well.
- #
- $dir = $opts{'d'};
- if ($dir eq '') {
- $dir = '.';
- }
-
- $depotfile = "${1}.nib";
-
- $clientfile = "";
- if ($depotfile =~ /\/\/(.*)\/([^\/]*)/) {
- $clientfile = $2;
- }
-
- $tarcmd = "cd $dir ; chmod -R u+w $clientfile ; tar xf ${clientfile}.tar";
-
- system($tarcmd);
- }
- elsif ($depotfile =~ /(.*)\.framework\.tar/) {
- # It's a nib. We'll map the filename back, and we
- # want to untar it as well.
- #
- $dir = $opts{'d'};
- if ($dir eq '') {
- $dir = '.';
- }
-
- $depotfile = "${1}.framework";
-
- $clientfile = "";
- if ($depotfile =~ /\/\/(.*)\/([^\/]+)/) {
- $clientfile = $2;
- }
-
- $tarcmd = "cd $dir ; chmod -R u+w $clientfile ; tar xf ${clientfile}.tar";
-
- system($tarcmd);
- }
-
- $line = "${depotfile}#${revision} - ${action}";
- }
- }
- elsif ($command eq "fstat") {
- # Watch for .nib.tar files in the fstat output, and change
- # the line to just have a .nib file for the name.
- #
- if ($line =~ /^\.\.\. clientFile (.*)/) {
- $file = $1;
-
- # It's a nib. We map the filename back to what Xcode expects
- #
- if ($file =~ /(.*)\.nib\.tar/) {
- $file = "${1}.nib";
- }
- elsif ($file =~ /(.*)\.framework\.tar/) {
- $file = "${1}.framework";
- }
-
- $line = "... clientFile $file";
- }
- elsif ($line =~ /^\.\.\. depotFile (.*)/) {
- $file = $1;
-
- # It's a nib. We map the filename back to what Xcode expects
- #
- if ($file =~ /(.*)\.nib\.tar/) {
- $file = "${1}.nib";
- }
- elsif ($file =~ /(.*)\.framework\.tar/) {
- $file = "${1}.framework";
- }
-
- $line = "... depotFile $file";
- }
- }
- elsif (($command eq "filelog") || ($command eq "print")) {
- if ($line =~ /^\/\/(.*)/) {
- $file = $1;
- if ($file =~ /(.*).nib.tar/) {
- $file = "${1}.nib";
- }
- elsif ($file =~ /(.*).framework.tar/) {
- $file = "${1}.framework";
- }
-
- $line = "//$file";
- }
- }
- elsif ($command eq "revert") {
- if ($line =~ /^\/\/(.*)#([0123456789]+) - (.*)/) {
- $file = $1;
- $revision = $2;
- $action = $3;
-
- if ($file =~ /(.*).nib.tar/) {
- $file = "${1}.nib";
-
- $clientfile = "";
- if ($file =~ /(.*)\/([^\/]*)/) {
- $clientfile = $2;
- }
-
- $dir = $opts{'d'};
- if ($dir eq '') {
- $dir = '.';
- }
-
- # Make our .nib write-only so that Xcode will properly
- # attempt to check it out... we hope?
-
- $modcmd = "cd $dir ; chmod -R a-w $clientfile ; chmod u+w ${clientfile}.tar";
- system($modcmd);
- }
- elsif ($file =~ /(.*).framework.tar/) {
- $file = "${1}.framework";
-
- $clientfile = "";
- if ($file =~ /(.*)\/([^\/]*)/) {
- $clientfile = $2;
- }
-
- $dir = $opts{'d'};
- if ($dir eq '') {
- $dir = '.';
- }
-
- # Make our .nib write-only so that Xcode will properly
- # attempt to check it out... we hope?
-
- $modcmd = "cd $dir ; chmod -R a-w $clientfile ; chmod u+w ${clientfile}.tar";
- system($modcmd);
- }
-
- $line = "//${file}#${revision} - ${action}";
- }
- }
- elsif ($command eq "add") {
- if ($line =~ /^\/\/(.*)#([0123456789]+) - (.*)/) {
- $file = $1;
- $revision = $2;
- $action = $3;
-
- if ($file =~ /(.*).nib.tar/) {
- $file = "${1}.nib";
- }
- elsif ($file =~ /(.*).framework.tar/) {
- $file = "${1}.framework";
- }
-
- $line = "//${file}#${revision} - ${action}";
- }
- }
-
-
- # Print our finalized line.
- #
- print "$line\n";
-
- }
-
- # ...all done! Xcode should be happy.