Ticket #2312: osx-packager.pl

File osx-packager.pl, 36.6 KB (added by pdbailey@…, 19 years ago)

2nd attempt at playback, after patch applied (patch also attached)

Line 
1#!/usr/bin/perl
2
3### osx-packager.pl $Id$
4### Tool for automating frontend builds on Mac OS X.
5### Run "osx-packager.pl -man" for full documentation.
6
7use strict;
8use Getopt::Long qw(:config auto_abbrev);
9use Pod::Usage ();
10use Cwd ();
11
12
13open(SVNVER, '>./ftp/revision.txt');
14
15
16### Configuration settings (stuff that might change more often)
17
18# We try to auto-locate the Subversion client binaries.
19# If they are not in your path, you should use the second line.
20#
21our $svn = '/usr/local/bin/svn';
22#our $svn = '/Volumes/Users/nigel/bin/svn';
23
24# This script used to always delete the installed include and lib dirs.
25# That probably ensures a safe build, but when rebuilding adds minutes to
26# the total build time, and prevents us skipping some parts of a full build
27#
28our $cleanLibs = 1;
29
30# By default, only the frontend is built (i.e. no backend or transcoding)
31#
32our $backend = 0;
33our $jobtools = 0;
34
35# Parallel makes?
36#
37#$ENV{'DISTCC_HOSTS'} = "localhost 192.168.0.6";
38#$ENV{'DISTCC_VERBOSE'} = '1';
39
40# For faster downloads, change this to a local mirror.
41#
42our $sourceforge = 'http://umn.dl.sourceforge.net';
43
44# At the moment, there is mythtv plus these two
45our @components = ( 'myththemes', 'mythplugins' );
46
47# The OS X programs that we are likely to be interested in.
48our @targetsJT = ( 'MythCommFlag', 'MythJobQueue');
49our @targetsBE = ( 'MythBackend', 'MythFillDatabase',
50 'MythTranscode', 'MythTV-Setup');
51
52# Patches for MythTV source
53our %patches = (
54);
55
56our %depend_order = (
57 'mythtv'
58 => [
59 'freetype',
60 'lame',
61 'mysqlclient',
62 'qt-mt',
63 # Not needed since SVN -r8965. Kept here for 0.19 builds
64 #'dvdnav'
65 ],
66 'mythplugins'
67 => [
68 'tiff',
69 'exif',
70 'dvdcss',
71 # MythDVD used to use this to build mtd.
72 # Not needed since SVN -r8965. Kept here for 0.19 builds
73 # 'dvdread',
74# MythMusic needs these seven things:
75# 'libmad',
76# 'libid3tag',
77# 'libogg',
78# 'vorbis',
79# 'flac',
80# These CD ones don't compile on OS X. I doubt that they ever will.
81# MythMusic probably needs to have native OS X code to assess CDs
82# 'cddaparanoia',
83# 'cdaudio'
84 ],
85);
86
87our %depend = (
88
89 'freetype'
90 =>
91 {
92 'url'
93 => "$sourceforge/sourceforge/freetype/freetype-2.1.10.tar.gz",
94 },
95
96 # SVN head doesn't need this, but 0.19-fixes and earlier do
97 'dvdnav' =>
98 {
99 'url' => "$sourceforge/sourceforge/dvd/libdvdnav-0.1.10.tar.gz",
100 },
101
102 'lame'
103 =>
104 {
105 'url'
106 => "$sourceforge/sourceforge/lame/lame-3.96.1.tar.gz",
107 'conf'
108 => [
109 '--disable-frontend',
110 ],
111 },
112
113 'cdaudio' =>
114 {
115 'url' => "$sourceforge/sourceforge/libcdaudio/libcdaudio-0.99.12.tar.gz"
116 },
117
118 'libmad' =>
119 {
120 'url' => 'ftp://ftp.mars.org/pub/mpeg/libmad-0.15.0b.tar.gz'
121 },
122
123 'libid3tag' =>
124 {
125 'url' => 'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.0b.tar.gz'
126 },
127
128 'cddaparanoia' =>
129 {
130 'url' => 'http://www.buserror.net/cdparanoia/cdparanoia-osx-5.tar.gz',
131 },
132
133 'libogg' =>
134 {
135 'url' => 'http://downloads.xiph.org/releases/ogg/libogg-1.1.2.tar.gz'
136 },
137
138 'vorbis' =>
139 {
140 'url' => 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.1.1.tar.gz'
141 },
142
143 'flac' =>
144 {
145 'url' => "$sourceforge/sourceforge/flac/flac-1.1.2.tar.gz"
146 },
147
148 'dvdcss'
149 =>
150 {
151 'url'
152 => 'ftp://ftp.fu-berlin.de/unix/linux/mirrors/gentoo/distfiles/libdvdcss-1.2.8.tar.bz2',
153 },
154
155 'dvdread'
156 =>
157 {
158 'url'
159 => 'http://www.dtek.chalmers.se/groups/dvd/dist/libdvdread-0.9.4.tar.gz',
160 'conf'
161 => [
162 '--with-libdvdcss',
163 ],
164 },
165
166
167 'mysqlclient'
168 =>
169 {
170 'url'
171 => 'http://mirrors.dedipower.com/www.mysql.com/Downloads/MySQL-4.1/mysql-4.1.21.tar.gz',
172 'conf'
173 => [
174 '--without-debug',
175 '--without-docs',
176 '--without-man',
177 '--without-bench',
178 '--without-server',
179 '--without-geometry',
180 '--without-extra-tools',
181 ],
182 },
183
184 'qt-mt'
185 =>
186# {
187# 'url'
188# => 'http://ftp.iasi.roedu.net/mirrors/ftp.trolltech.com/qt/sources/qt-mac-opensource-src-4.0.1.tar.gz',
189# 'conf-cmd'
190# => 'echo yes | ./configure',
191# 'conf'
192# => [
193# '-prefix', '"$PREFIX"',
194# '-system-zlib',
195# '-fast',
196# '-qt-sql-mysql',
197# '-qt-libpng',
198# '-qt-libjpeg',
199# '-qt-gif',
200# '-platform macx-g++',
201# '-no-tablet',
202# '-I"$PREFIX/include/mysql"',
203# '-L"$PREFIX/lib/mysql"',
204# ],
205# 'post-conf'
206# => 'echo "QMAKE_LFLAGS_SHLIB += -single_module" >> src/qt.pro',
207# 'make'
208# => [
209# 'sub-src'
210# ],
211# },
212 {
213 'url'
214 => 'http://ftp.iasi.roedu.net/mirrors/ftp.trolltech.com/qt/source/qt-mac-free-3.3.6.tar.gz',
215 'conf-cmd'
216 => 'echo yes | ./configure',
217 'conf'
218 => [
219 '-prefix', '"$PREFIX"',
220 '-thread',
221 '-system-zlib',
222 '-fast',
223 '-qt-sql-mysql',
224 '-no-style-cde',
225 '-no-style-compact',
226 '-no-style-motif',
227 '-no-style-motifplus',
228 '-no-style-platinum',
229 '-no-style-sgi',
230 '-no-ipv6',
231 '-qt-imgfmt-png',
232 '-qt-imgfmt-jpeg',
233 '-no-imgfmt-mng',
234 '-qt-gif',
235 '-no-tablet',
236 '-I"$PREFIX/include/mysql"',
237 '-L"$PREFIX/lib/mysql"',
238 ],
239 'post-conf'
240 => 'echo "QMAKE_LFLAGS_SHLIB += -single_module" >> src/qt.pro',
241 'make'
242 => [
243 'sub-src',
244 'qmake-install',
245 'moc-install'
246 ],
247 },
248
249 'tiff'
250 =>
251 {
252 'url' => 'http://dl.maptools.org/dl/libtiff/tiff-3.8.2.tar.gz'
253 },
254
255 'exif'
256 =>
257 {
258 'url' => "$sourceforge/sourceforge/libexif/libexif-0.6.13.tar.bz2",
259 'conf' => [ '--disable-nls' ],
260 'post-conf' => 'echo "install-apidocs:
261install-apidocs-internals:
262" >> doc/Makefile.in'
263 }
264);
265
266
267=head1 NAME
268
269osx-packager.pl - build OS X binary packages for MythTV
270
271=head1 SYNOPSIS
272
273 osx-packager.pl [options]
274
275 Options:
276 -help print the usage message
277 -man print full documentation
278 -verbose print informative messages during the process
279 -version <str> custom version suffix (defaults to "svnYYYYMMDD")
280 -noversion don't use any version (for building release versions)
281 -distclean throw away all intermediate files and exit
282 -thirdclean do a clean rebuild of third party packages
283 -thirdskip don't rebuild the third party packages
284 -mythtvskip don't rebuild/install mythtv
285 -pluginskip don't rebuild/install mythplugins
286 -themeskip don't install the extra themes from myththemes
287 -clean do a clean rebuild of MythTV
288 -svnbranch <str> build a specified Subversion branch, instead of HEAD
289 -svnrev <str> build a specified Subversion revision, instead of HEAD
290 -svntag <str> build a specified release, instead of Subversion HEAD
291 -nohead don't update to HEAD revision of MythTV before building
292 -usehdimage perform build inside of a case-sensitive disk image
293 -enable-backend build the backend server as well as the frontend
294 -enable-jobtools build commflag/jobqueue as well as the frontend
295 -plugins <str> comma-separated list of plugins to include
296 Available plugins:
297 mythbrowser mythcontrols mythdvd mythflix mythgallery mythgame
298 mythmusic mythnews mythphone mythvideo mythweather
299
300=head1 DESCRIPTION
301
302This script builds a MythTV frontend and all necessary dependencies, along
303with plugins as specified, as a standalone binary package for Mac OS X.
304
305It was designed for building daily CVS (now Subversion) snapshots,
306but can also be used to create release builds with the '-svntag' option.
307
308All intermediate files go into an '.osx-packager' directory in the current
309working directory. The finished application is named 'MythFrontend.app' and
310placed in the current working directory.
311
312=head1 EXAMPLES
313
314Building two snapshots, one with plugins and one without:
315
316 osx-packager.pl -clean -plugins mythvideo,mythweather
317 mv MythFrontend.app MythFrontend-plugins.app
318 osx-packager.pl -nohead
319 mv MythFrontend.app MythFrontend-noplugins.app
320
321Building a 0.17 release build:
322
323 osx-packager.pl -distclean
324 osx-packager.pl -svntag release-0-17 -noversion
325
326Building a "fixes" branch:
327
328 osx-packager.pl -distclean
329 osx-packager.pl -svnbranch release-0-18-fixes
330
331=head1 CREDITS
332
333Written by Jeremiah Morris (jm@whpress.com)
334
335Special thanks to Nigel Pearson, Jan Ornstedt, Angel Li, and Andre Pang
336for help, code, and advice.
337
338Small modifications made by Bas Hulsken (bhulsken@hotmail.com) to allow building current svn, and allow lirc (if installed properly on before running script). The modifications are crappy, and should probably be revised by someone who can actually code in perl. However it works for the moment, and I wanted to share with other mac frontend experimenters!
339
340=cut
341
342# Parse options
343our (%OPT);
344Getopt::Long::GetOptions(\%OPT,
345 'help|?',
346 'man',
347 'verbose',
348 'version=s',
349 'noversion',
350 'distclean',
351 'thirdclean',
352 'thirdskip',
353 'mythtvskip',
354 'pluginskip',
355 'themeskip',
356 'clean',
357 'svnbranch=s',
358 'svnrev=s',
359 'svntag=s',
360 'nocvs', # This is obsolete, but should stay a while
361 'nohead',
362 'usehdimage',
363 'enable-backend',
364 'enable-jobtools',
365 'plugins=s',
366 ) or Pod::Usage::pod2usage(2);
367Pod::Usage::pod2usage(1) if $OPT{'help'};
368Pod::Usage::pod2usage('-verbose' => 2) if $OPT{'man'};
369
370if ( $OPT{'enable-backend'} )
371{ $backend = 1 }
372
373if ( $OPT{'clean'} )
374{ $cleanLibs = 1 }
375
376if ( $OPT{'enable-jobtools'} )
377{ $jobtools = 1 }
378
379# Get version string sorted out
380if ($OPT{'svntag'} && !$OPT{'version'})
381{
382 $OPT{'version'} = $OPT{'svntag'};
383 $OPT{'version'} =~ s/-r *//;
384 $OPT{'version'} =~ s/--revision *//;
385}
386$OPT{'version'} = '' if $OPT{'noversion'};
387unless (defined $OPT{'version'})
388{
389 my @lt = gmtime(time);
390 $OPT{'version'} = sprintf('svn%04d%02d%02d',
391 $lt[5] + 1900,
392 $lt[4] + 1,
393 $lt[3]);
394}
395
396# Fold nocvs to nohead
397$OPT{'nohead'} = 1 if $OPT{'nocvs'};
398
399# Build our temp directories
400our $SCRIPTDIR = Cwd::abs_path(Cwd::getcwd());
401if ($SCRIPTDIR =~ /\s/)
402{
403 &Complain(<<END);
404Working directory contains spaces
405
406Error: Your current working path:
407
408 $SCRIPTDIR
409
410contains one or more spaces. This will break the compilation process,
411so the script cannot continue. Please re-run this script from a different
412directory (such as /tmp).
413
414The application produced will run from any directory, the no-spaces
415rule is only for the build process itself.
416
417END
418 die;
419}
420
421our $WORKDIR = "$SCRIPTDIR/.osx-packager";
422mkdir $WORKDIR;
423
424# Do we need to force a case-sensitive disk image?
425if (!$OPT{usehdimage} && !CaseSensitiveFilesystem())
426{
427 Verbose("Forcing -usehdimage due to case-insensitive filesystem");
428 $OPT{usehdimage} = 1;
429}
430
431if ($OPT{usehdimage})
432{
433 Verbose("Creating a case-sensitive device for the build");
434 MountHDImage();
435}
436
437our $PREFIX = "$WORKDIR/build";
438mkdir $PREFIX;
439
440our $SRCDIR = "$WORKDIR/src";
441mkdir $SRCDIR;
442
443# configure mythplugins, and mythtv, etc
444our %conf = (
445 'mythplugins'
446 => [
447 '--prefix=' . $PREFIX,
448 '--enable-opengl',
449 '--disable-mythbrowser',
450 '--enable-mythdvd',
451 '--enable-vcd',
452 '--disable-transcode',
453 '--enable-mythgallery',
454 '--enable-exif',
455 '--enable-new-exif',
456 '--disable-mythgame',
457 '--disable-mythmusic',
458 '--enable-mythnews',
459 '--disable-mythphone',
460 '--enable-mythweather',
461 '--disable-mytharchive',
462 ],
463 'myththemes'
464 => [
465 '--prefix=' . $PREFIX,
466 ],
467 'mythtv'
468 => [
469 '--disable-distcc',
470 '--disable-firewire',
471 '--enable-mmx',
472 '--arch=pentium-m',
473 '--enable-memalign-hack',
474 # '--enable-mac-accel',
475 '--extra-cxxflags=-msse -msse2 -msse3',
476 '--prefix=' . $PREFIX,
477 ],
478
479);
480
481# configure mythplugins, and mythtv, etc
482our %makecleanopt = (
483 'mythplugins'
484 => [
485 'distclean',
486 ],
487);
488
489# Source code version.pro needs to call subversion binary
490#
491use File::Basename;
492our $svnpath = dirname $svn;
493
494# Clean the environment
495$ENV{'PATH'} = "$PREFIX/bin:/sw/bin:/bin:/usr/bin:$svnpath";
496$ENV{'DYLD_LIBRARY_PATH'} = "$PREFIX/lib";
497$ENV{'LD_LIBRARY_PATH'} = "/usr/local/lib";
498$ENV{'PKG_CONFIG_PATH'} = "$PREFIX/lib/pkgconfig:";
499delete $ENV{'CC'};
500delete $ENV{'CXX'};
501delete $ENV{'CPP'};
502delete $ENV{'CXXCPP'};
503$ENV{'CFLAGS'} = $ENV{'CXXFLAGS'} = $ENV{'CPPFLAGS'} = "-I$PREFIX/include";
504$ENV{'LDFLAGS'} = "-Z -F/System/Library/Frameworks -L/usr/lib -L$PREFIX/lib";
505$ENV{'PREFIX'} = $PREFIX;
506
507# set up Qt environment
508my $qt_vers = $depend{'qt-mt'}{'url'};
509$qt_vers =~ s|^.*/([^/]+)\.tar\.gz$|$1|;
510$ENV{'QTDIR'} = "$SRCDIR/$qt_vers";
511
512# If environment is setup to use distcc, take advantage of it
513our $standard_make = '/usr/bin/make';
514our $parallel_make = $standard_make;
515
516if ( $ENV{'DISTCC_HOSTS'} )
517{
518 my @hosts = split m/\s+/, $ENV{'DISTCC_HOSTS'};
519 my $numhosts = $#hosts + 1;
520 &Verbose("Using $numhosts build hosts:", join ', ', @hosts);
521 $parallel_make .= ' -j' . $numhosts * 2;
522}
523
524
525### Distclean?
526if ($OPT{'distclean'})
527{
528 &Syscall([ '/bin/rm', '-f', '-r', $PREFIX ]);
529 &Syscall([ '/bin/rm', '-f', '-r', $SRCDIR ]);
530 exit;
531}
532
533### Check for app present in target location
534our $MFE = "$SCRIPTDIR/MythFrontend.app";
535if (-d $MFE)
536{
537 &Complain(<<END);
538$MFE already exists
539
540Error: a MythFrontend application exists where we were planning
541to build one. Please move this application away before running
542this script.
543
544END
545 exit;
546}
547
548### Third party packages
549my (@build_depends, %seen_depends);
550my @comps = ('mythtv', @components);
551&Verbose("Including components:", @comps);
552
553foreach my $comp (@comps)
554{
555 foreach my $dep (@{ $depend_order{$comp} })
556 {
557 unless (exists $seen_depends{$dep})
558 {
559 push(@build_depends, $dep);
560 $seen_depends{$dep} = 1;
561 }
562 }
563}
564foreach my $sw (@build_depends)
565{
566 # Get info about this package
567 my $pkg = $depend{$sw};
568 my $url = $pkg->{'url'};
569 my $filename = $url;
570 $filename =~ s|^.+/([^/]+)$|$1|;
571 my $dirname = $filename;
572 $dirname =~ s|\.tar\.gz$||;
573 $dirname =~ s|\.tar\.bz2$||;
574
575 chdir($SRCDIR);
576
577 # Download and decompress
578 unless (-e $filename)
579 {
580 &Verbose("Downloading $sw");
581 unless (&Syscall([ '/usr/bin/curl', $url, '>', $filename ],
582 'munge' => 1))
583 {
584 &Syscall([ '/bin/rm', $filename ]) if (-e $filename);
585 die;
586 }
587 }
588 else
589 {
590 &Verbose("Using previously downloaded $sw");
591 }
592
593 if ( -d $dirname )
594 {
595 if ( $OPT{'thirdclean'} )
596 {
597 &Verbose("Removing previous build of $sw");
598 &Syscall([ '/bin/rm', '-f', '-r', $dirname ]) or die;
599 }
600
601 if ( $OPT{'thirdskip'} )
602 {
603 &Verbose("Using previous build of $sw");
604 next;
605 }
606
607 &Verbose("Using previously unpacked $sw");
608 }
609 else
610 {
611 &Verbose("Unpacking $sw");
612 if ( substr($filename,-3) eq ".gz" )
613 {
614 &Syscall([ '/usr/bin/tar', '-xzf', $filename ]) or die;
615 }
616 elsif ( substr($filename,-4) eq ".bz2" )
617 {
618 &Syscall([ '/usr/bin/tar', '-xjf', $filename ]) or die;
619 }
620 }
621
622 # Configure
623 chdir($dirname);
624 unless (-e '.osx-config')
625 {
626 &Verbose("Configuring $sw");
627 my (@configure, $munge);
628
629 if ($pkg->{'conf-cmd'})
630 {
631 push(@configure, $pkg->{'conf-cmd'});
632 $munge = 1;
633 }
634 else
635 {
636 push(@configure, './configure',
637 '--prefix=$PREFIX',
638 '--disable-static',
639 '--enable-shared');
640 }
641 if ($pkg->{'conf'})
642 {
643 push(@configure, @{ $pkg->{'conf'} });
644 }
645 &Syscall(\@configure, 'interpolate' => 1, 'munge' => $munge) or die;
646 if ($pkg->{'post-conf'})
647 {
648 &Syscall([ $pkg->{'post-conf'} ], 'munge' => 1) or die;
649 }
650 &Syscall([ '/usr/bin/touch', '.osx-config' ]) or die;
651 }
652 else
653 {
654 &Verbose("Using previously configured $sw");
655 }
656
657 # Build and install
658 unless (-e '.osx-built')
659 {
660 &Verbose("Making $sw");
661 my (@make);
662
663 push(@make, $standard_make);
664 if ($pkg->{'make'})
665 {
666 push(@make, @{ $pkg->{'make'} });
667 }
668 else
669 {
670 push(@make, 'all', 'install');
671 }
672 &Syscall(\@make) or die;
673 &Syscall([ '/usr/bin/touch', '.osx-built' ]) or die;
674 }
675 else
676 {
677 &Verbose("Using previously built $sw");
678 }
679}
680
681
682### build MythTV
683
684# Clean any previously installed libraries
685if ( $cleanLibs )
686{
687 if ( $OPT{'mythtvskip'} )
688 {
689 &Complain("Cannot skip building mythtv src if also cleaning");
690 exit;
691 }
692&Verbose("Cleaning previous installs of MythTV");
693my @mythlibs = glob "$PREFIX/lib/libmyth*";
694if (scalar @mythlibs)
695{
696 &Syscall([ '/bin/rm', @mythlibs ]) or die;
697}
698foreach my $dir ('include', 'lib', 'share')
699{
700 if (-d "$PREFIX/$dir/mythtv")
701 {
702 &Syscall([ '/bin/rm', '-f', '-r', "$PREFIX/$dir/mythtv" ]) or die;
703 }
704}
705}
706
707my $svndir = "$SRCDIR/myth-svn";
708mkdir $svndir;
709
710# Deal with Subversion branches, revisions and tags:
711my $svnrepository = 'http://svn.mythtv.org/svn/';
712my @svnrevision = ();
713
714if ( $OPT{'svnbranch'} )
715{
716 $svnrepository .= 'branches/' . $OPT{'svnbranch'} . '/';
717}
718elsif ( $OPT{'svntag'} )
719{
720 $svnrepository .= 'tags/' . $OPT{'svntag'} . '/';
721}
722elsif ( $OPT{'svnrev'} )
723{
724 $svnrepository .= 'trunk/';
725
726 # This arg. could be '1234', '-r 1234', or '--revision 1234'
727 # If the user just specified a number, add appropriate flag:
728 if ( $OPT{'svnrev'} =~ m/^\d+$/ )
729 {
730 push @svnrevision, '--revision';
731 }
732
733 push @svnrevision, $OPT{'svnrev'};
734}
735elsif ( ! $OPT{'nohead'} )
736{
737 # Lookup and use the HEAD revision so we are guaranteed consistent source
738 my $cmd = "$svn log $svnrepository --revision HEAD --xml | grep revision";
739 &Verbose($cmd);
740 my $rev = `$cmd`;
741 $rev =~ s/[^[:digit:]]//gs;
742
743 #spit out the revision number
744 print SVNVER $rev;
745
746 $svnrepository .= 'trunk/';
747 @svnrevision = ('--revision', $rev);
748}
749
750# Retrieve source
751if (! $OPT{'nohead'})
752{
753 # Empty subdirectory 'config' sometimes causes checkout problems
754 &Syscall(['rm', '-fr', $svndir . '/mythtv/config']);
755 Verbose("Checking out source code");
756 &Syscall([ $svn, 'co', @svnrevision,
757 map($svnrepository . $_, @comps), $svndir ]) or die;
758}
759else
760{
761 &Syscall("mkdir -p $svndir/mythtv/config")
762}
763
764# Deal with user-supplied skip arguments
765if ( $OPT{'mythtvskip'} )
766{ @comps = grep(!m/mythtv/, @comps) }
767if ( $OPT{'pluginskip'} )
768{ @comps = grep(!m/mythplugins/, @comps) }
769if ( $OPT{'themeskip'} )
770{ @comps = grep(!m/myththemes/, @comps) }
771
772if ( ! @comps )
773{
774 &Complain("Nothing to build! Too many ...skip arguments?");
775 exit;
776}
777
778# Build MythTV and any plugins
779foreach my $comp (@comps)
780{
781 my $compdir = "$svndir/$comp/" ;
782
783 chdir $compdir;
784
785 if ( ! -e "$comp.pro" )
786 {
787 &Complain("$compdir/$comp.pro does not exist.",
788 'You must be building really old source?');
789 next;
790 }
791
792 if ($comp eq 'mythtv')
793 {
794 # MythTV has an empty subdirectory 'config' that causes problems for me:
795 &Syscall('touch config/config.pro');
796 }
797
798 if ($OPT{'clean'} && -e 'Makefile')
799 {
800 &Verbose("Cleaning $comp");
801 &Syscall([ $standard_make, 'distclean' ]) or die;
802 }
803 else
804 {
805 # clean the Makefiles, as process requires PREFIX hacking
806 &CleanMakefiles();
807 }
808
809 # Apply any nasty mac-specific patches
810 if ($patches{$comp})
811 {
812 &Syscall([ "echo '$patches{$comp}' | patch -p0 --forward" ]);
813 }
814
815 # configure and make
816 if ( $makecleanopt{$comp} && -e 'Makefile' )
817 {
818 my @makecleancom= $standard_make;
819 push(@makecleancom, @{ $makecleanopt{$comp} }) if $makecleanopt{$comp};
820 &Syscall([ @makecleancom ]) or die;
821 }
822 if (-e 'configure')
823 {
824 &Verbose("Configuring $comp");
825 my @config = './configure';
826 push(@config, @{ $conf{$comp} }) if $conf{$comp};
827 if ( $comp eq 'mythtv' && $backend )
828 {
829 push @config, '--enable-backend'
830 }
831 if ( $comp eq 'mythtv' && ! $ENV{'DISTCC_HOSTS'} )
832 {
833 push @config, '--disable-distcc'
834 }
835 &Syscall([ @config ]) or die;
836 }
837 &Verbose("Running qmake for $comp");
838 my @qmake_opts = (
839 'QMAKE_LFLAGS+=-Wl,-search_paths_first',
840 'INCLUDEPATH+="' . $PREFIX . '/include"',
841 'LIBS+=-L/usr/lib -L"' . $PREFIX . '/lib"'
842 );
843 &Syscall([ $PREFIX . '/bin/qmake',
844 'PREFIX=../Resources',
845 @qmake_opts,
846 "$comp.pro" ]) or die;
847
848 if ($comp eq 'mythtv')
849 {
850 # Remove/add Nigel's frontend building speedup hack
851 &DoSpeedupHacks('programs/programs.pro', 'mythfrontend mythtv');
852 }
853
854 &Verbose("Making $comp");
855 &Syscall([ $parallel_make ]) or die;
856 # install
857 # This requires a change from the compiled-in relative
858 # PREFIX to our absolute path of the temp install location.
859 &CleanMakefiles();
860 &Verbose("Running qmake for $comp install");
861 &Syscall([ $PREFIX . '/bin/qmake',
862 'PREFIX=' . $PREFIX,
863 @qmake_opts,
864 "$comp.pro" ]) or die;
865 &Verbose("Installing $comp");
866 &Syscall([ $standard_make,
867 'install' ]) or die;
868
869 if ($cleanLibs && $comp eq 'mythtv')
870 {
871 # If we cleaned the libs, make install will have recopied them,
872 # which means any dynamic libraries that the static libraries depend on
873 # are newer than the table of contents. Hence we need to regenerate it:
874 my @mythlibs = glob "$PREFIX/lib/libmyth*.a";
875 if (scalar @mythlibs)
876 {
877 &Verbose("Running ranlib on reinstalled static libraries");
878 foreach my $lib (@mythlibs)
879 { &Syscall("ranlib $lib") or die }
880 }
881 }
882}
883
884### Build version string
885our $VERS = `find $PREFIX/lib -name 'libmyth-[0-9].[0-9][0-9].[0-9].dylib'`;
886chomp $VERS;
887$VERS =~ s/^.*\-(.*)\.dylib$/$1/s;
888$VERS .= '.' . $OPT{'version'} if $OPT{'version'};
889
890### Create each package.
891### Note that this is a bit of a waste of disk space,
892### because there are now multiple copies of each library.
893my @targets = ('MythFrontend', 'MythTV');
894
895if ( $jobtools )
896{ push @targets, @targetsJT }
897
898if ( $backend )
899{ push @targets, @targetsBE }
900
901foreach my $target ( @targets )
902{
903 my $finalTarget = "$SCRIPTDIR/$target.app";
904 my $builtTarget = $target;
905 $builtTarget =~ tr/[A-Z]/[a-z]/;
906
907 # Get a fresh copy of the app
908 &Verbose("Building self-contained $target");
909 &Syscall([ 'rm', '-fr', $finalTarget ]) or die;
910 &RecursiveCopy("$PREFIX/bin/$builtTarget.app", $finalTarget);
911
912 # write a custom Info.plist
913 &GeneratePlist($target, $builtTarget, $finalTarget, $VERS);
914
915 # Make frameworks from Myth libraries
916 &Verbose("Installing frameworks into $target");
917 &PackagedExecutable($finalTarget, $builtTarget);
918
919 if ( $target eq "MythFrontend" or $target =~ m/^MythTV/ )
920 {
921 # Install themes, filters, etc.
922 &Verbose("Installing resources into $target");
923 mkdir "$finalTarget/Contents/Resources";
924 mkdir "$finalTarget/Contents/Resources/lib";
925 &RecursiveCopy("$PREFIX/lib/mythtv",
926 "$finalTarget/Contents/Resources/lib");
927 mkdir "$finalTarget/Contents/Resources/share";
928 &RecursiveCopy("$PREFIX/share/mythtv",
929 "$finalTarget/Contents/Resources/share");
930 }
931
932 if ( $target eq "MythFrontend" )
933 {
934 my $mtd = "$svndir/mythplugins/mythdvd/mtd/mtd.app/Contents/MacOS/mtd";
935 if ( -e $mtd )
936 {
937 &Verbose("Installing $mtd into $target");
938 &Syscall([ 'cp', $mtd, "$finalTarget/Contents/MacOS" ]) or die;
939 &AddFakeBinDir($finalTarget);
940 }
941 }
942}
943
944if ( $backend )
945{
946 my $BE = "$SCRIPTDIR/MythBackend.app";
947
948 # The backend gets all the useful binaries it might call:
949 foreach my $binary ( 'mythjobqueue', 'mythcommflag', 'mythtranscode' )
950 {
951 my $SRC = "$PREFIX/bin/$binary.app/Contents/MacOS/$binary";
952 if ( -e $SRC )
953 {
954 &Syscall([ '/bin/cp', $SRC, "$BE/Contents/MacOS" ]) or die;
955 &PackagedExecutable($BE, $binary);
956 &AddFakeBinDir($BE);
957 }
958 }
959}
960
961if ( $jobtools )
962{
963 # JobQueue also gets some binaries it might call:
964 my $JQ = "$SCRIPTDIR/MythJobQueue.app";
965 my $DEST = "$JQ/Contents/MacOS";
966 my $SRC = "$PREFIX/bin/mythcommflag.app/Contents/MacOS/mythcommflag";
967
968 &Syscall([ '/bin/cp', $SRC, $DEST ]) or die;
969 &PackagedExecutable($JQ, 'mythcommflag');
970
971 $SRC = "$PREFIX/bin/mythtranscode.app/Contents/MacOS/mythtranscode";
972 if ( -e $SRC )
973 {
974 &Syscall([ '/bin/cp', $SRC, $DEST ]) or die;
975 &PackagedExecutable($JQ, 'mythtranscode');
976 &AddFakeBinDir($JQ);
977 }
978}
979
980if ($OPT{usehdimage})
981{
982 Verbose("Dismounting case-sensitive build device");
983 UnmountHDImage();
984}
985
986&Verbose("Build complete. Self-contained package is at:\n\n $MFE\n");
987
988
989### end script
990exit 0;
991
992
993######################################
994## RecursiveCopy copies a directory tree, stripping out .svn
995## directories and properly managing static libraries.
996######################################
997
998sub RecursiveCopy
999{
1000 my ($src, $dst) = @_;
1001
1002 # First copy absolutely everything
1003 &Syscall([ '/bin/cp', '-R', "$src", "$dst"]) or die;
1004
1005 # Then strip out any .svn directories
1006 my @files = map { chomp $_; $_ } `find $dst -name .svn`;
1007 if (scalar @files)
1008 {
1009 &Syscall([ '/bin/rm', '-f', '-r', @files ]);
1010 }
1011
1012 # And make sure any static libraries are properly relocated.
1013 my @libs = map { chomp $_; $_ } `find $dst -name "lib*.a"`;
1014 if (scalar @libs)
1015 {
1016 &Syscall([ 'ranlib', '-s', @libs ]);
1017 }
1018}
1019
1020######################################
1021## Given an application package $finalTarget and an executable
1022## $builtTarget that has been copied into it, PackagedExecutable
1023## makes sure the package contains all the library dependencies as
1024## frameworks and that all the paths internal to the executable have
1025## been adjusted appropriately.
1026######################################
1027
1028sub PackagedExecutable($$)
1029{
1030 my ($finalTarget, $builtTarget) = @_;
1031
1032 my $fw_dir = "$finalTarget/Contents/Frameworks";
1033 mkdir $fw_dir;
1034
1035 my $dephash
1036 = &ProcessDependencies("$finalTarget/Contents/MacOS/$builtTarget",
1037 glob "$PREFIX/lib/mythtv/*/*.dylib");
1038 my @deps = values %$dephash;
1039 while (scalar @deps)
1040 {
1041 my $dep = shift @deps;
1042
1043 my $file = &MakeFramework(&FindLibraryFile($dep), $fw_dir);
1044 if ( $file )
1045 {
1046 my $newhash = &ProcessDependencies($file);
1047 foreach my $base (keys %$newhash)
1048 {
1049 next if exists $dephash->{$base};
1050 $dephash->{$base} = $newhash->{$base};
1051 push(@deps, $newhash->{$base});
1052 }
1053 }
1054 }
1055}
1056
1057
1058######################################
1059## MakeFramework copies a dylib into a
1060## framework bundle.
1061######################################
1062
1063sub MakeFramework
1064{
1065 my ($dylib, $dest) = @_;
1066
1067 my ($base, $vers) = &BaseVers($dylib);
1068 $vers .= '.' . $OPT{'version'} if ($OPT{'version'} && $base =~ /myth/);
1069 my $fw_dir = $dest . '/' . $base . '.framework';
1070
1071 return '' if ( -e $fw_dir );
1072
1073 &Verbose("Building $base framework");
1074
1075 &Syscall([ '/bin/mkdir',
1076 '-p',
1077 "$fw_dir/Versions/A/Resources" ]) or die;
1078 &Syscall([ '/bin/cp',
1079 $dylib,
1080 "$fw_dir/Versions/A/$base" ]) or die;
1081
1082 &Syscall([ '/usr/bin/install_name_tool',
1083 '-id',
1084 $base,
1085 "$fw_dir/Versions/A/$base" ]) or die;
1086
1087 symlink('A', "$fw_dir/Versions/Current") or die;
1088 symlink('Versions/Current/Resources', "$fw_dir/Resources") or die;
1089 symlink("Versions/A/$base", "$fw_dir/$base") or die;
1090
1091 &Verbose("Writing Info.plist for $base framework");
1092 my $plist;
1093 unless (open($plist, '>' . "$fw_dir/Versions/A/Resources/Info.plist"))
1094 {
1095 &Complain("Failed to open $base framework's plist for writing");
1096 die;
1097 }
1098 print $plist <<END;
1099<?xml version="1.0" encoding="UTF-8"?>
1100<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1101<plist version="1.0">
1102<dict>
1103 <key>CFBundleName</key>
1104 <string>$base</string>
1105 <key>CFBundleIdentifier</key>
1106 <string>org.mythtv.macx.$base</string>
1107 <key>CFBundleVersion</key>
1108 <string>$vers</string>
1109 <key>CFBundleSignature</key>
1110 <string>Myth</string>
1111 <key>CFBundlePackageType</key>
1112 <string>FMWK</string>
1113 <key>NSHumanReadableCopyright</key>
1114 <string>Packaged for the MythTV project, www.mythtv.org</string>
1115 <key>CFBundleGetInfoString</key>
1116 <string>lib$base-$vers.dylib, packaged for the MythTV project, www.mythtv.org</string>
1117</dict>
1118</plist>
1119END
1120 close($plist);
1121
1122 return "$fw_dir/Versions/A/$base";
1123} # end MakeFramework
1124
1125
1126######################################
1127## GeneratePlist .
1128######################################
1129
1130sub GeneratePlist
1131{
1132 my ($name, $binary, $path, $vers) = @_;
1133
1134 &Verbose("Writing Info.plist for $name");
1135 my $plist;
1136 $path .= '/Contents/Info.plist';
1137 unless (open($plist, ">$path"))
1138 {
1139 &Complain("Could not open $path for writing");
1140 die;
1141 }
1142 print $plist <<END;
1143<?xml version="1.0" encoding="UTF-8"?>
1144<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1145<plist version="1.0">
1146<dict>
1147 <key>CFBundleExecutable</key>
1148 <string>$binary</string>
1149 <key>CFBundleIconFile</key>
1150 <string>application.icns</string>
1151 <key>CFBundleIdentifier</key>
1152 <string>org.mythtv.macx.$binary</string>
1153 <key>CFBundleInfoDictionaryVersion</key>
1154 <string>6.0</string>
1155 <key>CFBundlePackageType</key>
1156 <string>APPL</string>
1157 <key>CFBundleShortVersionString</key>
1158 <string>$vers</string>
1159 <key>CFBundleSignature</key>
1160 <string>Myth</string>
1161 <key>CFBundleVersion</key>
1162 <string>$vers</string>
1163 <key>NSAppleScriptEnabled</key>
1164 <string>NO</string>
1165 <key>CFBundleGetInfoString</key>
1166 <string>$vers, MythTV project, www.mythtv.org</string>
1167 <key>CFBundleName</key>
1168 <string>$name</string>
1169 <key>NSHumanReadableCopyright</key>
1170 <string>MythTV project, www.mythtv.org</string>
1171</dict>
1172</plist>
1173END
1174 close($plist);
1175
1176 $path =~ s/Info\.plist$/PkgInfo/;
1177 unless (open($plist, ">$path"))
1178 {
1179 &Complain("Could not open $path for writing");
1180 die;
1181 }
1182 print $plist <<END;
1183APPLMyth
1184END
1185 close($plist);
1186}
1187
1188######################################
1189## FindLibraryFile locates a dylib.
1190######################################
1191
1192sub FindLibraryFile
1193{
1194 my ($dylib) = @_;
1195
1196 return Cwd::abs_path($dylib) if (-e $dylib);
1197 return Cwd::abs_path("$PREFIX/lib/$dylib") if (-e "$PREFIX/lib/$dylib");
1198 return Cwd::abs_path("$ENV{'QTDIR'}/lib/$dylib") if (-e "$ENV{'QTDIR'}/lib/$dylib");
1199 my @sublibs = glob("$PREFIX/lib/*/$dylib");
1200 return Cwd::abs_path($sublibs[0]) if (scalar @sublibs);
1201 &Complain("Could not find $dylib");
1202 die;
1203} # end FindLibraryFile
1204
1205
1206######################################
1207## ProcessDependencies catalogs and
1208## rewrites dependencies that will be
1209## packaged into our app bundle.
1210######################################
1211
1212sub ProcessDependencies
1213{
1214 my (%depfiles);
1215
1216 foreach my $file (@_)
1217 {
1218 &Verbose("Processing shared library dependencies for $file");
1219 my ($filebase) = &BaseVers($file);
1220
1221 my $cmd = "otool -L $file";
1222 &Verbose($cmd);
1223 my @deps = `$cmd`;
1224 shift @deps; # first line just repeats filename
1225 &Verbose("Dependencies for $file = @deps");
1226 foreach my $dep (@deps)
1227 {
1228 chomp $dep;
1229
1230 # otool returns lines like:
1231 # libblah-7.dylib (compatibility version 7, current version 7)
1232 # but we only want the file part
1233 $dep =~ s/\s+(.*) \(.*\)$/$1/;
1234
1235 # Paths like /usr/lib/libstdc++ contain quantifiers that must be escaped
1236 $dep =~ s/([+*?])/\\$1/;
1237
1238 # otool sometimes lists the framework as depending on itself
1239 next if ($file =~ m,/Versions/A/$dep,);
1240
1241 # Any dependency which is already package relative can be ignored
1242 next if $dep =~ m/\@executable_path/;
1243
1244 # skip system library locations
1245 next if ($dep =~ m|^/System| ||
1246 $dep =~ m|^/usr/lib|);
1247
1248 my ($base) = &BaseVers($dep);
1249 $depfiles{$base} = $dep;
1250
1251 # Change references while we're here
1252 &Syscall([ '/usr/bin/install_name_tool',
1253 '-change',
1254 $dep,
1255 "\@executable_path/../Frameworks/$base.framework/$base",
1256 $file ]) or die;
1257 }
1258 }
1259 return \%depfiles;
1260} # end ProcessDependencies
1261
1262
1263######################################
1264## BaseVers splits up a dylib file
1265## name for framework naming.
1266######################################
1267
1268sub BaseVers
1269{
1270 my ($filename) = @_;
1271
1272 if ($filename =~ m|^(?:.*/)?lib(.*)\-(\d.*)\.dylib$|)
1273 {
1274 return ($1, $2);
1275 }
1276 elsif ($filename =~ m|^(?:.*/)?lib(.*?)\.(\d.*)\.dylib$|)
1277 {
1278 return ($1, $2);
1279 }
1280 elsif ($filename =~ m|^(?:.*/)?lib(.*?)\.dylib$|)
1281 {
1282 return ($1, undef);
1283 }
1284
1285 &Verbose("Not a library file: $filename");
1286 return $filename;
1287} # end BaseVers
1288
1289
1290######################################
1291## CleanMakefiles removes every
1292## Makefile from our MythTV build.
1293## Necessary when we change the
1294## PREFIX variable.
1295######################################
1296
1297sub CleanMakefiles
1298{
1299 &Verbose("Cleaning MythTV makefiles");
1300 my @files = map { chomp $_; $_ } `find . -name Makefile`;
1301 if (scalar @files)
1302 {
1303 &Syscall([ '/bin/rm', @files ]) or die;
1304 }
1305} # end CleanMakefiles
1306
1307
1308######################################
1309## Syscall wrappers the Perl "system"
1310## routine with verbosity and error
1311## checking.
1312######################################
1313
1314sub Syscall
1315{
1316 my ($arglist, %opts) = @_;
1317
1318 unless (ref $arglist)
1319 {
1320 $arglist = [ $arglist ];
1321 }
1322 if ($opts{'interpolate'})
1323 {
1324 my @args;
1325 foreach my $arg (@$arglist)
1326 {
1327 $arg =~ s/\$PREFIX/$PREFIX/ge;
1328 push(@args, $arg);
1329 }
1330 $arglist = \@args;
1331 }
1332 if ($opts{'munge'})
1333 {
1334 $arglist = [ join(' ', @$arglist) ];
1335 }
1336 # clean out any null arguments
1337 $arglist = [ map $_, @$arglist ];
1338 &Verbose(@$arglist);
1339 my $ret = system(@$arglist);
1340 if ($ret)
1341 {
1342 &Complain('Failed system call: "', @$arglist,
1343 '" with error code', $ret >> 8);
1344 }
1345 return ($ret == 0);
1346} # end Syscall
1347
1348
1349######################################
1350## Verbose prints messages in verbose
1351## mode.
1352######################################
1353
1354sub Verbose
1355{
1356 print STDERR '[osx-pkg] ' . join(' ', @_) . "\n"
1357 if $OPT{'verbose'};
1358} # end Verbose
1359
1360
1361######################################
1362## Complain prints messages in any
1363## verbosity mode.
1364######################################
1365
1366sub Complain
1367{
1368 print STDERR '[osx-pkg] ' . join(' ', @_) . "\n";
1369} # end Complain
1370
1371
1372######################################
1373## Manage usehdimage disk image
1374######################################
1375
1376sub MountHDImage
1377{
1378 if (!HDImageDevice())
1379 {
1380 if (! -e "$SCRIPTDIR/.osx-packager.dmg")
1381 {
1382 Syscall(['hdiutil', 'create', '-size', '1300m',
1383 "$SCRIPTDIR/.osx-packager.dmg", '-volname',
1384 'MythTvPackagerHDImage', '-fs', 'UFS', '-quiet']);
1385 }
1386
1387 Syscall(['hdiutil', 'mount', "$SCRIPTDIR/.osx-packager.dmg",
1388 '-mountpoint', $WORKDIR, '-quiet']);
1389 }
1390
1391 # configure defaults to /tmp and OSX barfs when mv crosses
1392 # filesystems so tell configure to put temp files on the image
1393
1394 $ENV{TMPDIR} = $WORKDIR . "/tmp";
1395 mkdir $ENV{TMPDIR};
1396}
1397
1398sub UnmountHDImage
1399{
1400 my $device = HDImageDevice();
1401 if ($device)
1402 {
1403 &Syscall(['hdiutil', 'detach', $device, '-force']);
1404 }
1405}
1406
1407sub HDImageDevice
1408{
1409 my @dev = split ' ', `/sbin/mount | grep $WORKDIR`;
1410 $dev[0];
1411}
1412
1413sub CaseSensitiveFilesystem
1414{
1415 my $funky = $SCRIPTDIR . "/.osx-packager.FunkyStuff";
1416 my $unfunky = substr($funky, 0, -10) . "FUNKySTuFF";
1417
1418 unlink $funky if -e $funky;
1419 `touch $funky`;
1420 my $sensitivity = ! -e $unfunky;
1421 unlink $funky;
1422
1423 return $sensitivity;
1424}
1425
1426
1427######################################
1428## Remove or add Nigel's speedup hacks
1429######################################
1430
1431sub DoSpeedupHacks($$)
1432{
1433 my ($file, $subdirs) = @_;
1434
1435 &Verbose("Removing Nigel's hacks from file $file");
1436
1437 open(IN, $file) or die;
1438 open(OUT, ">$file.orig") or die;
1439 while ( <IN> )
1440 {
1441 if ( m/^# Nigel/ ) # Skip
1442 { last }
1443 print OUT;
1444 }
1445 if ( ! $backend && ! $jobtools )
1446 {
1447 # Nigel's hack to speedup building
1448 print OUT "# Nigel\'s speedup hack:\n";
1449 print OUT "SUBDIRS = $subdirs\n";
1450 }
1451 close IN; close OUT;
1452 rename("$file.orig", $file);
1453}
1454
1455#######################################################
1456## Parts of MythTV try to call helper apps like this:
1457## gContext->GetInstallPrefix() + "/bin/mythtranscode";
1458## which means we need a bin directory.
1459#######################################################
1460
1461sub AddFakeBinDir($)
1462{
1463 my ($target) = @_;
1464
1465 &Syscall(['ln', '-sf', '../MacOS', "$target/Contents/Resources/bin"]);
1466}
1467
1468### end file
14691;