CPAN vs. dot-underscore

I was so keen on the idea of pushing a module to CPAN that I almost didn’t catch a fatal flaw in my scheme. Mac OS X (motto: It Just Works) was doing something odd to my tarball as it was created: it was adding a whole host of files that weren’t there originally. Not just the .DS_Store files we all know and love (and learn to remove), but odd shadow files like ._foo.txt for foo.txt and ._Makefile.PL for Makefile.PL.

Yep. Now you might see the problem.

At first I wasn’t worried about them, because tar made the files vanish when I extracted the tarball to another location on my Mac. Assuming this was some quirk in tar, I posted the tarball to CPAN and went on my merry way. As it turned out, extracting the tarball on any non-Mac system retained all the shadow files, including ._Makefile.PL. That one caused the install process to fail and made the module essentially useless.

The only mention of tar’s new behavior I found was in a blog post about the same problem I was having. Turns out that it’s a relatively new twist on an old problem, new because tar itself had been updated to create and consume the dot-underscore files. There were other notes about these files (aka AppleDouble files) around the Web, but the proposed remedy was always the same: delete the files once they’re on the target system. Unfortunately, that’s not good enough when the target is CPAN.

tar’s own ability to –exclude input files was no use, either, because it creates the files after the exclude filter is processed. There is a –delete option, but it requires a specific filename so I’d have to remove the files one at a time. Now that the task had moved from impossibility to drudgery, however, the solution was obvious: a Perl script!


use Archive::Tar;
use IO::Zlib;
use File::Copy;

my $USAGE = ” Usage: $0 FILENAME\n”;

my $tar_file = $ARGV[0]
or die $USAGE;

my $tar = Archive::Tar->new($tar_file);

my @files_to_remove;
foreach my $file ( sort $tar->list_files( ['name'] ) )
print ” $file”;
if ($file =~ ‘/\._’ or $file =~ ‘.DS_Store’)
print ” …will be removed.”;
push @files_to_remove, $file;
print “\n”;

unless ( scalar(@files_to_remove) )
print “No files were found to remove. The archive is unchanged.\n”;

my $backup_file = $tar_file . “.bak”;
copy($tar_file, $backup_file);
print “Backup file written to $backup_file.\n”;


print “Clean archive:\n”;
foreach my $file ( sort $tar->list_files( ['name'] ) )
print ” $file”;
print “\n”;

$tar->write($tar_file, 1);

print “Clean file has been written.\n\n”;

I’ll probably clean that up and submit it to CPAN as well, but hopefully Apple will add a –dont-be-evil option to tar before I get the chance.

4 thoughts on “CPAN vs. dot-underscore

  1. Setting DITTONORSRC had no effect. A quick Google for that variable only turns up the ditto man page… have you seen it in another context?

  2. You could use the ‘manifest’ and ‘dist’ targets to Makefile.PL to create the CPAN tarball based on your MANIFEST file. This will only put the files you have in your manifest file in the tarball.

  3. That’s a great idea. I’ll have to try it with the next release of EVDB::API. I should be able to specify the .DS_Store files in MANIFEST.SKIP, run ‘make manifest’ to generate the MANIFEST file, and then ‘make dist’ to create the tarball. MakeMaker should be using Archive::Tar (instead of command-line tar) to do the heavy lifting, so hopefully it won’t generat the dot-underscore files.

    Huh. Learn something new every day. Of course, I would have learned it before if I had taken the time to read a MakeMaker tutorial, but creating all that stuff by hand was good practice. Or something.

Comments are closed.