dm.pl 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use File::Find;
  5. use File::Spec;
  6. use Cwd;
  7. use Getopt::Long;
  8. use Pod::Usage;
  9. use Archive::Tar;
  10. use IO::Compress::Gzip;
  11. use IO::Compress::Bzip2;
  12. package NIC::Archive::Tar::File;
  13. use parent "Archive::Tar::File";
  14. sub new {
  15. my $class = shift;
  16. my $self = Archive::Tar::File->new(@_);
  17. bless($self, $class);
  18. return $self;
  19. }
  20. sub full_path {
  21. my $self = shift;
  22. my $full_path = $self->SUPER::full_path(); $full_path = '' unless defined $full_path;
  23. $full_path =~ s#^#./# if $full_path ne "" && $full_path ne "." && $full_path !~ m#^\./#;
  24. return $full_path;
  25. }
  26. 1;
  27. package main;
  28. our $VERSION = '2.0';
  29. our $_PROGNAME = "dm.pl";
  30. my $ADMINARCHIVENAME = "control.tar.gz";
  31. my $DATAARCHIVENAME = "data.tar";
  32. my $ARCHIVEVERSION = "2.0";
  33. $Archive::Tar::DO_NOT_USE_PREFIX = 1; # use GNU extensions (not POSIX prefix)
  34. our $compression = "gzip";
  35. Getopt::Long::Configure("bundling", "auto_version");
  36. GetOptions('compression|Z=s' => \$compression,
  37. 'build|b' => sub { },
  38. 'help|?' => sub { pod2usage(1); },
  39. 'man' => sub { pod2usage(-exitstatus => 0, -verbose => 2); })
  40. or pod2usage(2);
  41. pod2usage(1) if(@ARGV < 2);
  42. my $pwd = Cwd::cwd();
  43. my $indir = File::Spec->rel2abs($ARGV[0]);
  44. my $outfile = $ARGV[1];
  45. die "ERROR: '$indir' is not a directory or does not exist.\n" unless -d $indir;
  46. my $controldir = File::Spec->catpath("", $indir, "DEBIAN");
  47. die "ERROR: control directory '$controldir' is not a directory or does not exist.\n" unless -d $controldir;
  48. my $mode = (lstat($controldir))[2];
  49. die sprintf("ERROR: control directory has bad permissions %03lo (must be >=0755 and <=0775)\n", $mode & 07777) if(($mode & 07757) != 0755);
  50. my $controlfile = File::Spec->catfile($controldir, "control");
  51. die "ERROR: control file '$controlfile' is not a plain file\n" unless -f $controlfile;
  52. my %control_data = read_control_file($controlfile);
  53. die "ERROR: package name has characters that aren't alphanumueric or '-+.'.\n" if($control_data{"package"} =~ m/[^a-zA-Z0-9+-.]/);
  54. die "ERROR: package version ".$control_data{"version"}." doesn't contain any digits.\n" if($control_data{"version"} !~ m/[0-9]/);
  55. foreach my $m ("preinst", "postinst", "prerm", "postrm", "extrainst_") {
  56. $_ = File::Spec->catfile($controldir, $m);
  57. next unless -e $_;
  58. die "ERROR: maintainer script '$m' is not a plain file or symlink\n" unless(-f $_ || -l $_);
  59. $mode = (lstat)[2];
  60. die sprintf("ERROR: maintainer script '$m' has bad permissions %03lo (must be >=0555 and <=0775)\n", $mode & 07777) if(($mode & 07557) != 0555)
  61. }
  62. print "$_PROGNAME: building package `".$control_data{"package"}.":".$control_data{"architecture"}."' in `$outfile'\n";
  63. open(my $ar, '>', $outfile) or die $!;
  64. print $ar "!<arch>\n";
  65. print_ar_record($ar, "debian-binary", time, 0, 0, 0100644, 4);
  66. print_ar_file($ar, "$ARCHIVEVERSION\n", 4);
  67. {
  68. my $tar = Archive::Tar->new();
  69. $tar->add_files(tar_filelist($controldir));
  70. my $comp;
  71. my $zFd = IO::Compress::Gzip->new(\$comp, -Level => 9);
  72. $tar->write($zFd);
  73. $zFd->close();
  74. print_ar_record($ar, $ADMINARCHIVENAME, time, 0, 0, 0100644, length($comp));
  75. print_ar_file($ar, $comp, length($comp));
  76. } {
  77. my $tar = Archive::Tar->new();
  78. $tar->add_files(tar_filelist($indir));
  79. my $comp;
  80. my $zFd = compressed_fd(\$comp);
  81. $tar->write($zFd);
  82. $zFd->close();
  83. print_ar_record($ar, compressed_filename($DATAARCHIVENAME), time, 0, 0, 0100644, length($comp));
  84. print_ar_file($ar, $comp, length($comp));
  85. }
  86. close $ar;
  87. sub print_ar_record {
  88. my ($fh, $filename, $timestamp, $uid, $gid, $mode, $size) = @_;
  89. printf $fh "%-16s%-12lu%-6lu%-6lu%-8lo%-10ld`\n", $filename, $timestamp, $uid, $gid, $mode, $size;
  90. $fh->flush();
  91. }
  92. sub print_ar_file {
  93. my ($fh, $data, $size) = @_;
  94. syswrite $fh, $data;
  95. print $fh "\n" if($size % 2 == 1);
  96. $fh->flush();
  97. }
  98. sub tar_filelist {
  99. chdir(shift);
  100. my @filelist;
  101. my @symlinks;
  102. find({wanted => sub {
  103. return if m#^./DEBIAN#;
  104. my $tf = NIC::Archive::Tar::File->new(file=>$_);
  105. push @symlinks, $tf if -l;
  106. push @filelist, $tf if ! -l;
  107. }, no_chdir => 1}, ".");
  108. return (@filelist, @symlinks);
  109. }
  110. sub read_control_file {
  111. my $filename = shift;
  112. open(my $fh, '<', $filename) or die "ERROR: can't open control file '$filename'\n";
  113. my %data;
  114. while(<$fh>) {
  115. if(m/^(.*?): (.*)/) {
  116. $data{lc($1)} = $2;
  117. }
  118. }
  119. close $fh;
  120. return %data;
  121. }
  122. sub compressed_fd {
  123. my $sref = shift;
  124. return IO::Compress::Gzip->new($sref, -Level => 9) if $::compression eq "gzip";
  125. return IO::Compress::Bzip2->new($sref) if $::compression eq "bzip2";
  126. open my $fh, ">", $sref;
  127. return $fh;
  128. }
  129. sub compressed_filename {
  130. my $fn = shift;
  131. my $suffix = "";
  132. $suffix = ".gz" if $::compression eq "gzip";
  133. $suffix = ".bz2" if $::compression eq "bzip2";
  134. return $fn.$suffix;
  135. }
  136. __END__
  137. =head1 NAME
  138. dm.pl
  139. =head1 SYNOPSIS
  140. dm.pl [options] <directory> <package>
  141. =head1 OPTIONS
  142. =over 8
  143. =item B<-b>
  144. This option exists solely for compatibility with dpkg-deb.
  145. =item B<-ZE<lt>compressionE<gt>>
  146. Specify the package compression type. Valid values are gzip (default), bzip2 and cat (no compression.)
  147. =item B<--help>, B<-?>
  148. Print a brief help message and exit.
  149. =item B<--man>
  150. Print a manual page and exit.
  151. =back
  152. =head1 DESCRIPTION
  153. B<This program> creates Debian software packages (.deb files) and is a drop-in replacement for dpkg-deb.
  154. =cut