#!/usr/bin/perl -w # $Id$ # # this is the build script for the SqueezeCenter. # It currently builds a versioned directory containing: # A "server" containing the Perl server code and firmware # A tarred and gzip'd copy of this directory # A zipped copy of this directory # A RedHat RPM # A Mac OS X preference pane # A standalone Windows application shell, server executable and installer # Requires: # command-line zip tool # Macintosh Developer Tools (on MacOS systems only) # cygwin (including cygwin perl), ActiveState PerlDev, Windows Dev Studio .NET (windows) # rpm (if you've got it) use strict; use warnings; use Cwd; use File::Basename; use File::Copy; use File::Path; use File::Spec::Functions qw(:ALL); use Getopt::Long; use POSIX qw(strftime); # Set this to the explicit path of your windows perl installation my $perldirwin = 'C:\Perl'; my %osToBuildMap = ( 'MSWin32' => \&buildWin32Installer, 'cygwin' => \&buildWin32Installer, 'darwin' => \&buildOSXInstaller, 'linux' => \&buildLinux, ); my ($rpmVersion, $snapshot, $version, $title, $buildDir, $destDir, $sourceDir, $sourceCopy, $humanTitle); sub main { my $update = 0; my $checkout = 0; my $notar = 0; my $snapshot = 0; my $lastRevision = 0; my $branch = '7.0.1'; if (!GetOptions( 'version=s' => \$version, 'snapshot' => \$snapshot, 'checkout' => \$checkout, 'update' => \$update, 'branch=s' => \$branch, 'notar' => \$notar, 'builddir=s' => \$buildDir, 'destdir=s' => \$destDir, 'sourcedir=s' => \$sourceDir, )) { showUsage(); exit(1); }; if (!$buildDir || !$destDir || !$sourceDir) { print "Missing --builddir, --destdir or --sourcedir!\n"; showUsage(); } my $prefix = $^O eq 'linux' ? 'SqueezeCenter' : 'SqueezeCenter'; if ($snapshot) { $rpmVersion = $version; $version = strftime('%Y-%m-%d', localtime()); if ($branch) { $title = "${prefix}_${branch}_v$version"; $humanTitle = "$prefix $branch $version"; } else { $title = "${prefix}_v$version"; $humanTitle = "$prefix $version"; } } else { if (!defined($version)) { print "Version? --> "; chomp($version = ); } $version =~ s/[^0-9A-Za-z_.-]//g; $title = "${prefix}_v$version"; $humanTitle = "$prefix $version"; } print "Okay, this is $humanTitle\n"; print "Please don't forget to tag it in Subversion!\n"; for my $dir ($destDir, $buildDir) { if (-d $dir) { print "Deleting old dir: $dir\n"; rmtree($dir); } if (!-d $dir) { mkpath($dir) or die "Problem: couldn't make $dir!"; print "Made $dir\n"; } } if ($checkout) { print "Checking out a copy of the source from Subversion on branch $branch...\n"; my $options = '--quiet --force --non-interactive'; my $url = "http://svn.slimdevices.com/repos/slim/$branch"; if ($branch ne '7.0.1') { $url = "http://svn.slimdevices.com/repos/slim/branches/$branch"; } $lastRevision = lastRevisionForPath($url, $branch); print "Running: svn export $options $url $sourceDir\n"; system "svn export $options $url $sourceDir"; } else { if ($update) { print "Updating from Subversion...\n"; system "svn update"; } $lastRevision = lastRevisionFromLocalRepo($sourceDir); } $sourceCopy = catdir($buildDir, $title); if (-d $sourceCopy) { rmtree($sourceCopy); } # Create a copy of the SVN source directory without additional # directorys or .svn turd files for the build. my $baseSourceDir = basename($sourceDir); my $sourceExclude = join(' --exclude ', map { "$baseSourceDir/$_" } qw(tests slimp3 squeezebox softsqueeze tools)); print "Making copy of server source ($sourceDir -> $sourceCopy)\n"; system "rsync -av --quiet --exclude .svn --exclude $sourceExclude $sourceDir $sourceCopy"; system "mv $sourceCopy/$baseSourceDir/* $sourceCopy"; rmtree("$sourceCopy/$baseSourceDir"); if (!-d "$sourceCopy/server") { die "Problem: export of server files failed - $sourceCopy/server directory!"; } chmod 0755, "$sourceCopy/server/slimserver.pl"; chmod 0755, "$sourceCopy/server/scanner.pl"; # Write out the revision number if ($lastRevision) { print "Last Revision number is: $lastRevision\n"; open(REV, ">$sourceCopy/server/revision.txt") or die "Couldn't write out $sourceCopy/revision.txt : $!\n"; print REV $lastRevision; close(REV); } # Don't bother with the tarball on Win32 or OSX if (!$notar && $^O eq 'linux') { my $binExclude = join(' ', map { "--exclude $_" } qw( MSWin32-x86-multi-thread darwin i386-linux-thread-multi powerpc-hardhat-linux arch )); # Because we want a pretty name as the outdir, make a copy. system "rsync -avq $sourceCopy/server/ $sourceCopy/$title"; # make the tar file print "Making the tarball $title...\n"; system "cd $sourceCopy; tar --exclude MSWin32-x86-multi-thread -cf $title.tar $title; gzip -9 $title.tar"; print "tarball done...\n"; # make the tar file without the CPAN/arch dir - bug #755 print "Making the tarball $title.no-cpan-arch...\n"; system "cd $sourceCopy; tar $binExclude -cf $title.no-cpan-arch.tar $title; gzip -9 $title.no-cpan-arch.tar"; print "tarball done...\n"; rmtree("$sourceCopy/$title"); # And now move to the dist dir `mv $sourceCopy/*.gz $destDir/`; } # run the build method for the OS. { no strict 'refs'; $osToBuildMap{$^O}(); } print "Deleting copy ($sourceCopy)\n"; rmtree($sourceCopy); if ($checkout) { print "Deleting exported copy ($sourceDir)\n"; rmtree($sourceDir); } } sub buildOSXInstaller { print "Building MacOSX Project...\n"; # Remove bits that don't need to be in the DMG my $match = qr/(?:i386-linux|MSWin32|powerpc-hardhat-linux)/i; print "Building the openUp helper app\n"; `cc $sourceCopy/platforms/osx/openUp.c -o $buildDir/openUp`; mkpath("$buildDir/$humanTitle/Install Files"); print "Copying documentation & licenses\n"; copy("$sourceCopy/server/license.txt", "$buildDir/$humanTitle/License.txt"); copy("$sourceCopy/platforms/osx/Getting Started.html", "$buildDir/$humanTitle/Getting Started.html"); copy("$sourceCopy/platforms/osx/Getting Started.fr.html", "$buildDir/$humanTitle/Mise en route.html"); copy("$sourceCopy/platforms/osx/Getting Started.es.html", "$buildDir/$humanTitle/Guía rápida.html"); copy("$sourceCopy/platforms/osx/Getting Started.it.html", "$buildDir/$humanTitle/Per iniziare.html"); copy("$sourceCopy/platforms/osx/Getting Started.nl.html", "$buildDir/$humanTitle/Beginnen.html"); copy("$sourceCopy/platforms/osx/Getting Started.de.html", "$buildDir/$humanTitle/Erste Schritte.html"); copy("$sourceCopy/platforms/osx/Getting Started.he.html", "$buildDir/$humanTitle/תחילת השימוש.html"); # xcodebuild doesn't allow you to specify an output directory. my $xcodeBuildDir = "$sourceCopy/platforms/osx/Preference Pane/build/Deployment"; my $contentsDir = "$buildDir/$humanTitle/Install Files/SqueezeCenter.prefPane/Contents"; print "Building PreferencePane & Installer..\n"; `cd "$sourceCopy/platforms/osx/Preference Pane"; xcodebuild -project "SqueezeCenter.xcodeproj" -target "SqueezeCenter" -configuration Deployment`; print "Copying Preference Pane...\n"; `ditto "$xcodeBuildDir/SqueezeCenter.prefPane" "$buildDir/$humanTitle/Install Files/SqueezeCenter.prefPane"`; print "Copying MacOSX Installer...\n"; `ditto "$xcodeBuildDir/SqueezeCenter Installer.app" "$buildDir/$humanTitle/SqueezeCenter Installer.app"`; # .svn directories have already been removed. `mv "$sourceCopy/server" "$contentsDir/"`; # Remove files we won't ever use. removeDirectoriesMatching("$contentsDir/server/Bin", $match); removeDirectoriesNotMatching("$contentsDir/server/CPAN/arch/5.8", $^O); print "Copying MacOSX Launcher App...\n"; `ditto "$xcodeBuildDir/Launcher.app" "$contentsDir/server/SqueezeCenter.app"`; # Unmount any previously mounted DMG if (-d "/Volumes/$humanTitle") { print "Unmonting previously mounted DMG Volume\n"; `hdiutil unmount "/Volumes/$humanTitle"`; } print "Building MacOSX DMG image...\n"; `hdiutil create -fs HFS+ -volname "$humanTitle" -size 100m "$destDir/SqueezeCenter.dmg"`; print "Mounting MacOSX DMG image...\n"; `hdiutil mount "$destDir/SqueezeCenter.dmg"`; print "Copying MacOSX files...\n"; `ditto -rsrc "$buildDir/$humanTitle" "/Volumes/$humanTitle"`; print "Making auto open\n"; `$buildDir/openUp "/Volumes/$humanTitle"`; print "Unmounting MacOSX DMG image...\n"; `hdiutil unmount "/Volumes/$humanTitle"`; print "Converting MacOSX DMG image...\n"; `hdiutil convert -format UDZO "$destDir/SqueezeCenter.dmg" -o "$destDir/$title.dmg"`; print "Removing old MacOSX DMG image...\n"; unlink("$destDir/SqueezeCenter.dmg"); } sub buildWin32Installer { print "Building Windows directory...\n"; my $winserverdir = "build"; my $perlpath = 'C:\\perl\\bin\\perl.exe'; if (-d $winserverdir) { print "Deleting original win build dir: $winserverdir\n"; rmtree($winserverdir); } mkpath($winserverdir); # We don't need unixy bits. removeDirectoriesNotMatching("$sourceCopy/server/Bin", 'MSWin32'); removeDirectoriesNotMatching("$sourceCopy/server/CPAN/arch/5.8", 'MSWin32'); `cp -R $sourceCopy/server "$winserverdir/server"`; copy("$sourceCopy/platforms/win32/Getting Started.html", "$winserverdir"); copy("$sourceCopy/server/CHANGELOG.html", "$winserverdir/Release Notes.html"); copy("$sourceCopy/server/license.txt", "$winserverdir/License.txt"); copy("$sourceCopy/platforms/win32/Getting Started.html", "$winserverdir"); copy("$sourceCopy/platforms/win32/Getting Started.de.html", "$winserverdir"); copy("$sourceCopy/platforms/win32/Getting Started.es.html", "$winserverdir"); copy("$sourceCopy/platforms/win32/Getting Started.fr.html", "$winserverdir"); copy("$sourceCopy/platforms/win32/Getting Started.he.html", "$winserverdir"); copy("$sourceCopy/platforms/win32/Getting Started.it.html", "$winserverdir"); copy("$sourceCopy/platforms/win32/Getting Started.nl.html", "$winserverdir"); copy("$sourceCopy/docs/squeezebox3/Squeezebox-v3-Owners-Guide.pdf", "$winserverdir/Squeezebox-Owners-Guide.pdf"); copy("$sourceCopy/server/License.txt", "$winserverdir"); copy("$sourceCopy/server/License.de.txt", "$winserverdir"); copy("$sourceCopy/server/License.es.txt", "$winserverdir"); copy("$sourceCopy/server/License.fr.txt", "$winserverdir"); copy("$sourceCopy/server/License.he.txt", "$winserverdir"); copy("$sourceCopy/server/License.it.txt", "$winserverdir"); copy("$sourceCopy/server/License.nl.txt", "$winserverdir"); print "Building perltray executable...\n"; my $libpaths = join(';', "$perldirwin/lib", qw( . ../../server/CPAN ../../server/CPAN/arch/5.8/MSWin32-x86-multi-thread ../../server/CPAN/arch/5.8/MSWin32-x86-multi-thread/auto )); print `cd $sourceCopy/platforms/win32; perltray --perl "$perlpath" --force --singleton --lib "$libpaths" --icon "res/SqueezeCenter.ico;res/SqueezeCenterOff.ico" SqueezeTray.pl; cd ../..`; move("$sourceCopy/platforms/win32/SqueezeTray.exe", "$winserverdir/SqueezeTray.exe"); copy("$sourceCopy/platforms/win32/strings.txt", "$winserverdir/strings.txt"); rmsvn($winserverdir); print "Building perlsvc executable for server...\n"; $libpaths = join(';', "$perldirwin/lib", qw( . CPAN CPAN/arch/5.8/MSWin32-x86-multi-thread CPAN/arch/5.8/MSWin32-x86-multi-thread/auto lib )); print `cd $sourceDir/server; perlsvc --perl "$perlpath" --lib "$libpaths" --add Compress::Zlib --add I18N::LangTags --force slimserver.pl; cd ..`; move("$sourceDir/server/slimserver.exe", "$winserverdir/server/squeezecenter.exe"); print "Making scanner executable...\n"; print `cd $sourceDir/server; perlapp --perl "$perlpath" --lib "$libpaths" --force scanner.pl; cd ..`; move("$sourceDir/server/scanner.exe", "$winserverdir/server/scanner.exe"); print "Making ServiceEnabler...\n"; copy("$sourceDir/platforms/win32/installer/ServiceManager.iss", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/StartupModeWizardPage.iss", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/ServiceEnabler.iss", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/strings.iss", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/psvince.dll", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/Dutch.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/English.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/French.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/German.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/Hebrew.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/Italian.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/Spanish.isl", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/logitech.bmp", "$winserverdir"); copy("$sourceDir/platforms/win32/installer/squeezebox.bmp", "$winserverdir"); print `"$sourceDir/platforms/win32/InnoSetup/ISCC.exe" "$winserverdir/ServiceEnabler.iss" 2>&1`; move("$winserverdir/Output/ServiceEnabler.exe", "$winserverdir/"); print "Making installer...\n"; copy("$sourceDir/platforms/win32/installer/SqueezeCenter.iss", "$winserverdir"); # Uncomment this exit to leave with an installer script ready to build in the "build" directory # exit(); print `"$sourceDir/platforms/win32/InnoSetup/ISCC.exe" "$winserverdir/SqueezeCenter.iss" 2>&1`; unlink("$winserverdir/SqueezeCenter.iss"); unlink("$winserverdir/ServiceManager.iss"); unlink("$winserverdir/StartupModeWizardPage.iss"); unlink("$winserverdir/ServiceEnabler.iss"); unlink("$winserverdir/slim.bmp"); unlink("$winserverdir/strings.iss"); unlink("$winserverdir/Dutch.isl"); unlink("$winserverdir/English.isl"); unlink("$winserverdir/French.isl"); unlink("$winserverdir/German.isl"); unlink("$winserverdir/Hebrew.isl"); unlink("$winserverdir/Italian.isl"); unlink("$winserverdir/Spanish.isl"); move($winserverdir, "$destDir/SqueezeCenter for Windows"); $winserverdir = "$destDir/SqueezeCenter for Windows"; move("$winserverdir/Output/SqueezeSetup.exe", "$destDir/$title.exe"); rmtree("$winserverdir/Output"); # make the zip file, if we can system("which zip >/dev/null 2>&1"); # > 0 indicates failure...freaking shell if ($? >> 8) { print "zip not installed, skipping zipping...\n"; } else { # make the zip file print "Making the zip file...\n"; `cd $destDir; zip -r $title.ZIP "SqueezeCenter for Windows"`; } rmtree($winserverdir); } sub buildLinux { print "No longer supporting RPM or Debian Builds from this file. Use buildme.pl\n"; # buildRPM(); # buildDebian(); } sub buildDebian { print "Building Debian Package - version: $version in $sourceCopy\n"; print `cd $sourceCopy/platforms; fakeroot dpkg-buildpackage -b;`; `mv $sourceCopy/*.deb $destDir`; } sub buildRPM { require File::Which; print "Building Linux RPM...\n"; # this must be a unix system # make the RPM if appropriate if (!File::Which::which('rpmbuild')) { print "rpmbuild not detected on this system. Not making an RPM"; return; } print "Making version $rpmVersion RPM in $buildDir..\n"; # make a destination dir for the RPM for my $path (qw(SPECS SOURCES BUILD RPMS SRPMS)) { mkpath("$buildDir/rpm/$path"); } # Use the tarball we just made copy("$destDir/$title.tar.gz", "$buildDir/rpm/SOURCES"); copy("$sourceDir/platforms/redhat/squeezecenter.config", "$buildDir/rpm/SOURCES"); copy("$sourceDir/platforms/redhat/squeezecenter.init", "$buildDir/rpm/SOURCES"); copy("$sourceDir/platforms/redhat/squeezecenter.logrotate", "$buildDir/rpm/SOURCES"); copy("$sourceDir/platforms/redhat/squeezecenter.spec", "$buildDir/rpm/SPECS"); # Do it my $date = strftime('%Y-%m-%d', localtime()); my $shortdate = strftime('%Y%m%d', localtime()); `rpmbuild -bb --with trunk --define="_version $rpmVersion" --define="_src_date $date" --define="_rpm_date $shortdate" --define='_topdir $buildDir/rpm' $buildDir/rpm/SPECS/squeezecenter.spec`; ## We used to rename the file here... instead, just move it to the right location. ## We leave the file un-renamed for the Yum repository to continue to be valid. `mv $buildDir/rpm/RPMS/noarch/*.rpm $destDir`; rmtree("$buildDir/rpm"); } sub showUsage { print "$0 --sourcedir= --builddir= --destdir= [--checkout] [--update] [--snapshot] [--version VERSION] [--notar]\n"; } sub rmsvn { my $root = shift; print "Removing .svn directories from root path: [$root]\n"; system "find '$root' -name .svn -print0 | xargs -0 rm -rf" || die; } sub lastRevisionForPath { my $path = shift; my $match = shift; my $revision; # We want one level up from the path we're fetching. $path = dirname($path); open(SVN, "svn ls -v $path |") or die "Couldn't run svn ls -v $path : $!\n"; # 2590 dsully Mar 19 00:33 trunk while () { s/^\s*//g; my ($rev, $user, $month, $day, $time, $dir) = split /\s+/; if ($dir =~ /^$match/) { $revision = $rev; last; } } close(SVN); return $revision; } sub lastRevisionFromLocalRepo { my $path = shift; my $revision; open(SVN, "svn info $path |") or die "Couldn't run svn info $path : $!\n"; # Revision: 2590 while () { if (/Revision: (\d+)/) { $revision = $1; } } close(SVN); return $revision; } sub removeDirectoriesMatching { _removeDirectories(@_); } sub removeDirectoriesNotMatching { _removeDirectories(@_, 'not'); } sub _removeDirectories { my $path = shift; my $match = shift; my $type = shift; # Remove files we won't ever use. opendir(DIR, $path) or die "Couldn't open directory: [$path] : $!\n"; while (my $dir = readdir(DIR)) { next if $dir =~ /^\./; if ($type && $type eq 'not') { next if $dir =~ /$match/; } else { next if $dir !~ /$match/; } print "Removing $path/$dir\n"; rmtree("$path/$dir"); } close DIR; } main(); __END__