dmg2mar 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/perl -w
  2. #
  3. # This script converts all dmg files from the current directory and
  4. # listed in the sha256sums-unsigned-build.txt file to full update
  5. # mar files. After code signing the dmg files, this script can be used
  6. # to update the mar files.
  7. #
  8. # A recent version of p7zip is required to extract the dmg files, such
  9. # as 15.14. The version in Debian Jessie (9.20) is not recent enough.
  10. # It is possible to install the p7zip-full package from Debian testing,
  11. # or build p7zip from sources:
  12. # $ p7zipdir=/some_directory/p7zip
  13. # $ mkdir $p7zipdir
  14. # $ cd $p7zipdir
  15. # $ wget http://snapshot.debian.org/archive/debian/20160417T044336Z/pool/main/p/p7zip/p7zip_15.14.1%2Bdfsg.orig.tar.xz
  16. # $ echo 'e9e696e2fa77b00445a4d85fa07506debeae01943fdc1bee1472152d7d1386af p7zip_15.14.1+dfsg.orig.tar.xz' | sha256sum -c
  17. # $ wget http://snapshot.debian.org/archive/debian/20160515T161830Z/pool/main/p/p7zip/p7zip_15.14.1%2Bdfsg-2.debian.tar.xz
  18. # $ echo 'f4db6803535fc30b6ae9db5aabfd9f57a851c6773d72073847ec5e3731b7af37 p7zip_15.14.1+dfsg-2.debian.tar.xz' | sha256sum -c
  19. # $ tar xvf p7zip_15.14.1+dfsg-2.debian.tar.xz
  20. # $ tar xvf p7zip_15.14.1+dfsg.orig.tar.xz
  21. # $ cd p7zip_15.14.1/
  22. # $ for patch in $(cat ../debian/patches/series ); do patch -p1 < ../debian/patches/$patch; done
  23. # $ make 7z
  24. # $ mkdir $p7zipdir/bin
  25. # $ echo '#!/bin/sh' > $p7zipdir/bin/7z
  26. # $ echo "export LD_LIBRARY_PATH=$PWD/bin" >> $p7zipdir/bin/7z
  27. # $ echo "exec $PWD/bin/7z "'"$@"' >> $p7zipdir/bin/7z
  28. # $ chmod +x $p7zipdir/bin/7z
  29. # $ export "PATH=$p7zipdir/bin:$PATH"
  30. use strict;
  31. use IO::CaptureOutput qw(capture_exec);
  32. use File::Slurp;
  33. use File::Find;
  34. use Parallel::ForkManager;
  35. use Cwd;
  36. # If the application is not TorBrowser (for instance, TorMessenger)
  37. # set the application name in the TOR_APPNAME_BUNDLE_OSX and in
  38. # the TOR_APPNAME_MARFILE environment variables
  39. my $appname = $ENV{TOR_APPNAME_BUNDLE_OSX} // 'TorBrowser';
  40. my $appname_mar = $ENV{TOR_APPNAME_MARFILE} // 'tor-browser';
  41. sub exit_error {
  42. print STDERR "Error: ", $_[0], "\n";
  43. chdir '/';
  44. exit (exists $_[1] ? $_[1] : 1);
  45. }
  46. sub osname {
  47. my ($osname) = capture_exec('uname', '-s');
  48. my ($arch) = capture_exec('uname', '-m');
  49. chomp($osname, $arch);
  50. if ($osname eq 'Linux' && $arch eq 'x86_64') {
  51. return 'linux64';
  52. }
  53. if ($osname eq 'Linux' && $arch =~ m/^i.86$/) {
  54. return 'linux32';
  55. }
  56. exit_error 'Unknown OS';
  57. }
  58. my $martools_tmpdir;
  59. sub extract_martools {
  60. my $osname = osname;
  61. my $marzip = getcwd . "/mar-tools-$osname.zip";
  62. $martools_tmpdir = File::Temp->newdir();
  63. my $old_cwd = getcwd;
  64. chdir $martools_tmpdir;
  65. my (undef, undef, $success) = capture_exec('unzip', $marzip);
  66. chdir $old_cwd;
  67. exit_error "Error extracting $marzip" unless $success;
  68. $ENV{PATH} = "$martools_tmpdir/mar-tools:$ENV{PATH}";
  69. if ($ENV{LD_LIBRARY_PATH}) {
  70. $ENV{LD_LIBRARY_PATH} .= ":$martools_tmpdir/mar-tools";
  71. } else {
  72. $ENV{LD_LIBRARY_PATH} = "$martools_tmpdir/mar-tools";
  73. }
  74. $ENV{MAR} = "$martools_tmpdir/mar-tools/mar";
  75. $ENV{MSBDIFF} = "$martools_tmpdir/mar-tools/mbsdiff";
  76. }
  77. sub get_nbprocs {
  78. return $ENV{NUM_PROCS} if defined $ENV{NUM_PROCS};
  79. if (-f '/proc/cpuinfo') {
  80. return scalar grep { m/^processor\s+:\s/ } read_file '/proc/cpuinfo';
  81. }
  82. return 4;
  83. }
  84. sub get_dmg_files_from_sha256sums {
  85. exit_error "Missing sha256sums-unsigned-build.txt file"
  86. unless -f 'sha256sums-unsigned-build.txt';
  87. my @files;
  88. foreach my $line (read_file('sha256sums-unsigned-build.txt')) {
  89. my (undef, $filename) = split ' ', $line;
  90. chomp $filename;
  91. next unless $filename =~ m/^$appname-(.+)-osx64_(.+)\.dmg$/;
  92. push @files, { filename => $filename, version => $1, lang => $2 };
  93. }
  94. return @files;
  95. }
  96. sub convert_files {
  97. my $pm = Parallel::ForkManager->new(get_nbprocs);
  98. $pm->run_on_finish(sub { print "Finished $_[2]\n" });
  99. foreach my $file (get_dmg_files_from_sha256sums) {
  100. # The 'ja' locale is a special case: it is called 'ja-JP-mac'
  101. # internally on OSX, but the dmg file still uses 'ja' to avoid
  102. # confusing users.
  103. my $mar_lang = $file->{lang} eq 'ja' ? 'ja-JP-mac' : $file->{lang};
  104. my $output = "$appname_mar-osx64-$file->{version}_$mar_lang.mar";
  105. my $step_name = "$file->{filename} -> $output";
  106. print "Starting $step_name\n";
  107. $pm->start($step_name) and next;
  108. my $tmpdir = File::Temp->newdir();
  109. my (undef, $err, $success) = capture_exec('7z', 'x', "-o$tmpdir",
  110. $file->{filename});
  111. exit_error "Error extracting $file->{filename}: $err" unless $success;
  112. # 7z does not currently extract file permissions from the dmg files
  113. # so we also extract the old mar file to copy the permissions
  114. # https://trac.torproject.org/projects/tor/ticket/20210
  115. my $tmpdir_oldmar = File::Temp->newdir();
  116. my $oldmar = getcwd . '/' . $output;
  117. exit_error "Error extracting $output"
  118. unless system('mar', '-C', $tmpdir_oldmar, '-x', $oldmar) == 0;
  119. my $wanted = sub {
  120. my $file = $File::Find::name;
  121. $file =~ s{^$tmpdir/$appname\.app/}{};
  122. if (-f "$tmpdir_oldmar/$file") {
  123. my (undef, undef, $mode) = stat("$tmpdir_oldmar/$file");
  124. chmod $mode, $File::Find::name;
  125. return;
  126. }
  127. chmod 0644, $File::Find::name if -f $File::Find::name;
  128. chmod 0755, $File::Find::name if -d $File::Find::name;
  129. };
  130. find($wanted, "$tmpdir/$appname.app");
  131. unlink $output;
  132. (undef, $err, $success) = capture_exec('make_full_update.sh', '-q',
  133. $output, "$tmpdir/$appname.app");
  134. exit_error "Error updating $output: $err" unless $success;
  135. $pm->finish;
  136. }
  137. $pm->wait_all_children;
  138. }
  139. sub remove_incremental_mars {
  140. exit_error "Missing sha256sums-unsigned-build.incrementals.txt file"
  141. unless -f 'sha256sums-unsigned-build.incrementals.txt';
  142. foreach my $line (read_file('sha256sums-unsigned-build.incrementals.txt')) {
  143. my (undef, $filename) = split ' ', $line;
  144. chomp $filename;
  145. next unless $filename =~ m/^$appname_mar-osx64.+\.incremental\.mar$/;
  146. next unless -f $filename;
  147. print "Removing $filename\n";
  148. unlink $filename;
  149. }
  150. }
  151. # Set LC_ALL=C to avoid reproducibility issues when creating mar files
  152. $ENV{LC_ALL} = 'C';
  153. extract_martools;
  154. convert_files;
  155. remove_incremental_mars;