diff options
Diffstat (limited to 'indra/newview/viewer_manifest.py')
| -rw-r--r--[-rwxr-xr-x] | indra/newview/viewer_manifest.py | 1444 |
1 files changed, 926 insertions, 518 deletions
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index e87fe8d8ff..6861f02bfb 100755..100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -4,558 +4,966 @@ # @brief Description of all installer viewer files, and methods for packaging # them into installers for all supported platforms. # -# $LicenseInfo:firstyear=2006&license=viewergpl$ -# -# Copyright (c) 2006-2007, Linden Research, Inc. -# +# $LicenseInfo:firstyear=2006&license=viewerlgpl$ # Second Life Viewer Source Code -# The source code in this file ("Source Code") is provided by Linden Lab -# to you under the terms of the GNU General Public License, version 2.0 -# ("GPL"), unless you have obtained a separate licensing agreement -# ("Other License"), formally executed by you and Linden Lab. Terms of -# the GPL can be found in doc/GPL-license.txt in this distribution, or -# online at http://secondlife.com/developers/opensource/gplv2 +# Copyright (C) 2010, Linden Research, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License only. # -# There are special exceptions to the terms and conditions of the GPL as -# it is applied to this Source Code. View the full text of the exception -# in the file doc/FLOSS-exception.txt in this software distribution, or -# online at http://secondlife.com/developers/opensource/flossexception +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # -# By copying, modifying or distributing this software, you acknowledge -# that you have read and understood your obligations described above, -# and agree to abide by those obligations. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. +# Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA # $/LicenseInfo$ import sys import os.path import re import tarfile +import time viewer_dir = os.path.dirname(__file__) # add llmanifest library to our path so we don't have to muck with PYTHONPATH sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util')) from llmanifest import LLManifest, main, proper_windows_path, path_ancestors class ViewerManifest(LLManifest): - def construct(self): - super(ViewerManifest, self).construct() - self.exclude("*.svn*") - self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") - self.path(src="../../etc/message.xml", dst="app_settings/message.xml") - - if self.prefix(src="app_settings"): - self.exclude("logcontrol.xml") - self.exclude("logcontrol-dev.xml") - self.path("*.pem") - self.path("*.ini") - self.path("*.xml") - self.path("*.vp") - self.path("*.db2") - - # include the entire shaders directory recursively - self.path("shaders") - # ... and the entire windlight directory - self.path("windlight") - self.end_prefix("app_settings") - - if self.prefix(src="character"): - self.path("*.llm") - self.path("*.xml") - self.path("*.tga") - self.end_prefix("character") - - - # Include our fonts - if self.prefix(src="fonts"): - self.path("*.ttf") - self.path("*.txt") - self.end_prefix("fonts") - - # XUI - if self.prefix(src="skins"): - # include the entire textures directory recursively - self.path("textures") - self.path("paths.xml") - self.path("xui/*/*.xml") - self.path('words.*.txt') - - # Local HTML files (e.g. loading screen) - if self.prefix(src="html"): - self.path("*.png") - self.path("*/*/*.html") - self.path("*/*/*.gif") - self.end_prefix("html") - self.end_prefix("skins") - - self.path("releasenotes.txt") - self.path("lsl_guide.html") - self.path("gpu_table.txt") - - def login_channel(self): - """Channel reported for login and upgrade purposes ONLY; used for A/B testing""" - # NOTE: Do not return the normal channel if login_channel is not specified, as - # some code may branch depending on whether or not this is present - return self.args.get('login_channel') - - def channel(self): - return self.args['channel'] - def channel_unique(self): - return self.channel().replace("Second Life", "").strip() - def channel_oneword(self): - return "".join(self.channel_unique().split()) - def channel_lowerword(self): - return self.channel_oneword().lower() - - def flags_list(self): - """ Convenience function that returns the command-line flags for the grid""" - channel_flags = '' - grid_flags = '' - if not self.default_grid(): - if self.default_channel(): - # beta grid viewer - channel_flags = '--settings settings_beta.xml' - grid_flags = "--helperuri http://preview-%(grid)s.secondlife.com/helpers/ --loginuri https://login.%(grid)s.lindenlab.com/cgi-bin/login.cgi" % {'grid':self.args['grid']} - - if not self.default_channel(): - # some channel on some grid - channel_flags = '--settings settings_%s.xml --channel "%s"' % (self.channel_lowerword(), self.channel()) - elif self.login_channel(): - # Report a special channel during login, but use default channel elsewhere - channel_flags = '--channel "%s"' % (self.login_channel()) - - return " ".join((channel_flags, grid_flags)).strip() - - def login_url(self): - """ Convenience function that returns the appropriate login url for the grid""" - if(self.args.get('login_url')): - return self.args['login_url'] - else: - if(self.default_grid()): - if(self.default_channel()): - # agni release - return 'http://secondlife.com/app/login/' - else: - # first look (or other) on agni - return 'http://secondlife.com/app/login/%s/' % self.channel_lowerword() - else: - # beta grid - return 'http://secondlife.com/app/login/beta/' - - def replace_login_url(self): - # set the login page to point to a url appropriate for the type of client - self.replace_in("skins/xui/en-us/panel_login.xml", searchdict={'http://secondlife.com/app/login/':self.login_url()}) + def is_packaging_viewer(self): + # Some commands, files will only be included + # if we are packaging the viewer on windows. + # This manifest is also used to copy + # files during the build (see copy_w_viewer_manifest + # and copy_l_viewer_manifest targets) + return 'package' in self.args['actions'] + + def construct(self): + super(ViewerManifest, self).construct() + self.exclude("*.svn*") + self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") + self.path(src="../../etc/message.xml", dst="app_settings/message.xml") + + if self.is_packaging_viewer(): + if self.prefix(src="app_settings"): + self.exclude("logcontrol.xml") + self.exclude("logcontrol-dev.xml") + self.path("*.pem") + self.path("*.ini") + self.path("*.xml") + self.path("*.db2") + + # include the entire shaders directory recursively + self.path("shaders") + # ... and the entire windlight directory + self.path("windlight") + self.end_prefix("app_settings") + + if self.prefix(src="character"): + self.path("*.llm") + self.path("*.xml") + self.path("*.tga") + self.end_prefix("character") + + # Include our fonts + if self.prefix(src="fonts"): + self.path("*.ttf") + self.path("*.txt") + self.end_prefix("fonts") + + # skins + if self.prefix(src="skins"): + self.path("paths.xml") + # include the entire textures directory recursively + if self.prefix(src="*/textures"): + self.path("*/*.tga") + self.path("*/*.j2c") + self.path("*/*.jpg") + self.path("*/*.png") + self.path("*.tga") + self.path("*.j2c") + self.path("*.jpg") + self.path("*.png") + self.path("textures.xml") + self.end_prefix("*/textures") + self.path("*/xui/*/*.xml") + self.path("*/xui/*/widgets/*.xml") + self.path("*/*.xml") + + # Local HTML files (e.g. loading screen) + if self.prefix(src="*/html"): + self.path("*.png") + self.path("*/*/*.html") + self.path("*/*/*.gif") + self.end_prefix("*/html") + self.end_prefix("skins") + + # local_assets dir (for pre-cached textures) + if self.prefix(src="local_assets"): + self.path("*.j2c") + self.path("*.tga") + self.end_prefix("local_assets") + + # Files in the newview/ directory + self.path("gpu_table.txt") + + def login_channel(self): + """Channel reported for login and upgrade purposes ONLY; + used for A/B testing""" + # NOTE: Do not return the normal channel if login_channel + # is not specified, as some code may branch depending on + # whether or not this is present + return self.args.get('login_channel') + + def grid(self): + return self.args['grid'] + def channel(self): + return self.args['channel'] + def channel_unique(self): + return self.channel().replace("Second Life", "").strip() + def channel_oneword(self): + return "".join(self.channel_unique().split()) + def channel_lowerword(self): + return self.channel_oneword().lower() + + def flags_list(self): + """ Convenience function that returns the command-line flags + for the grid""" + + # Set command line flags relating to the target grid + grid_flags = '' + if not self.default_grid(): + grid_flags = "--grid %(grid)s "\ + "--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\ + {'grid':self.grid()} + + # set command line flags for channel + channel_flags = '' + if self.login_channel() and self.login_channel() != self.channel(): + # Report a special channel during login, but use default + channel_flags = '--channel "%s"' % (self.login_channel()) + elif not self.default_channel(): + channel_flags = '--channel "%s"' % self.channel() + + # Deal with settings + setting_flags = '' + if not self.default_channel() or not self.default_grid(): + if self.default_grid(): + setting_flags = '--settings settings_%s.xml'\ + % self.channel_lowerword() + else: + setting_flags = '--settings settings_%s_%s.xml'\ + % (self.grid(), self.channel_lowerword()) + + return " ".join((channel_flags, grid_flags, setting_flags)).strip() class WindowsManifest(ViewerManifest): - def final_exe(self): - if self.default_channel(): - if self.default_grid(): - return "SecondLife.exe" - else: - return "SecondLifePreview.exe" + def final_exe(self): + if self.default_channel(): + if self.default_grid(): + return "SecondLife.exe" + else: + return "SecondLifePreview.exe" + else: + return ''.join(self.channel().split()) + '.exe' + + def test_msvcrt_and_copy_action(self, src, dst): + # This is used to test a dll manifest. + # It is used as a temporary override during the construct method + from test_win32_manifest import test_assembly_binding + if src and (os.path.exists(src) or os.path.islink(src)): + # ensure that destination path exists + self.cmakedirs(os.path.dirname(dst)) + self.created_paths.append(dst) + if not os.path.isdir(src): + if(self.args['configuration'].lower() == 'debug'): + test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "8.0.50727.4053") else: - return ''.join(self.channel().split()) + '.exe' + test_assembly_binding(src, "Microsoft.VC80.CRT", "8.0.50727.4053") + self.ccopy(src,dst) + else: + raise Exception("Directories are not supported by test_CRT_and_copy_action()") + else: + print "Doesn't exist:", src + + def test_for_no_msvcrt_manifest_and_copy_action(self, src, dst): + # This is used to test that no manifest for the msvcrt exists. + # It is used as a temporary override during the construct method + from test_win32_manifest import test_assembly_binding + from test_win32_manifest import NoManifestException, NoMatchingAssemblyException + if src and (os.path.exists(src) or os.path.islink(src)): + # ensure that destination path exists + self.cmakedirs(os.path.dirname(dst)) + self.created_paths.append(dst) + if not os.path.isdir(src): + try: + if(self.args['configuration'].lower() == 'debug'): + test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "") + else: + test_assembly_binding(src, "Microsoft.VC80.CRT", "") + raise Exception("Unknown condition") + except NoManifestException, err: + pass + except NoMatchingAssemblyException, err: + pass + + self.ccopy(src,dst) + else: + raise Exception("Directories are not supported by test_CRT_and_copy_action()") + else: + print "Doesn't exist:", src + + def enable_crt_manifest_check(self): + if self.is_packaging_viewer(): + WindowsManifest.copy_action = WindowsManifest.test_msvcrt_and_copy_action + + def enable_no_crt_manifest_check(self): + if self.is_packaging_viewer(): + WindowsManifest.copy_action = WindowsManifest.test_for_no_msvcrt_manifest_and_copy_action + + def disable_manifest_check(self): + if self.is_packaging_viewer(): + del WindowsManifest.copy_action + + def construct(self): + super(WindowsManifest, self).construct() + + self.enable_crt_manifest_check() + + if self.is_packaging_viewer(): + # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. + self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) + + # Plugin host application + self.path(os.path.join(os.pardir, + 'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"), + "slplugin.exe") + + self.disable_manifest_check() + + # Get shared libs from the shared libs staging directory + if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), + dst=""): + + self.enable_crt_manifest_check() + + # Get kdu dll, continue if missing. + try: + self.path('llkdu.dll', dst='llkdu.dll') + except RuntimeError: + print "Skipping llkdu.dll" + + # Get llcommon and deps. If missing assume static linkage and continue. + try: + self.path('llcommon.dll') + self.path('libapr-1.dll') + self.path('libaprutil-1.dll') + self.path('libapriconv-1.dll') + except RuntimeError, err: + print err.message + print "Skipping llcommon.dll (assuming llcommon was linked statically)" + + self.disable_manifest_check() + + # Get fmod dll, continue if missing + try: + self.path("fmod.dll") + except: + print "Skipping fmod.dll" + + # For textures + if self.args['configuration'].lower() == 'debug': + self.path("openjpegd.dll") + else: + self.path("openjpeg.dll") + + # These need to be installed as a SxS assembly, currently a 'private' assembly. + # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx + if self.args['configuration'].lower() == 'debug': + self.path("msvcr80d.dll") + self.path("msvcp80d.dll") + self.path("Microsoft.VC80.DebugCRT.manifest") + else: + self.path("msvcr80.dll") + self.path("msvcp80.dll") + self.path("Microsoft.VC80.CRT.manifest") + + # Vivox runtimes + self.path("SLVoice.exe") + self.path("vivoxsdk.dll") + self.path("ortp.dll") + self.path("libsndfile-1.dll") + self.path("zlib1.dll") + self.path("vivoxplatform.dll") + self.path("vivoxoal.dll") + + # For google-perftools tcmalloc allocator. + try: + if self.args['configuration'].lower() == 'debug': + self.path('libtcmalloc_minimal-debug.dll') + else: + self.path('libtcmalloc_minimal.dll') + except: + print "Skipping libtcmalloc_minimal.dll" + + self.end_prefix() + + self.path(src="licenses-win32.txt", dst="licenses.txt") + self.path("featuretable.txt") + self.path("featuretable_xp.txt") + + # For use in crash reporting (generates minidumps) + self.path("dbghelp.dll") + + self.enable_no_crt_manifest_check() + + # Media plugins - QuickTime + if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"): + self.path("media_plugin_quicktime.dll") + self.end_prefix() + + # Media plugins - WebKit/Qt + if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"): + self.path("media_plugin_webkit.dll") + self.end_prefix() + + # winmm.dll shim + if self.prefix(src='../media_plugins/winmmshim/%s' % self.args['configuration'], dst="llplugin"): + self.path("winmm.dll") + self.end_prefix() + + + if self.args['configuration'].lower() == 'debug': + if self.prefix(src=os.path.join(os.pardir, os.pardir, 'libraries', 'i686-win32', 'lib', 'debug'), + dst="llplugin"): + self.path("libeay32.dll") + self.path("qtcored4.dll") + self.path("qtguid4.dll") + self.path("qtnetworkd4.dll") + self.path("qtopengld4.dll") + self.path("qtwebkitd4.dll") + self.path("qtxmlpatternsd4.dll") + self.path("ssleay32.dll") + + # For WebKit/Qt plugin runtimes (image format plugins) + if self.prefix(src="imageformats", dst="imageformats"): + self.path("qgifd4.dll") + self.path("qicod4.dll") + self.path("qjpegd4.dll") + self.path("qmngd4.dll") + self.path("qsvgd4.dll") + self.path("qtiffd4.dll") + self.end_prefix() + + # For WebKit/Qt plugin runtimes (codec/character encoding plugins) + if self.prefix(src="codecs", dst="codecs"): + self.path("qcncodecsd4.dll") + self.path("qjpcodecsd4.dll") + self.path("qkrcodecsd4.dll") + self.path("qtwcodecsd4.dll") + self.end_prefix() + + self.end_prefix() + else: + if self.prefix(src=os.path.join(os.pardir, os.pardir, 'libraries', 'i686-win32', 'lib', 'release'), + dst="llplugin"): + self.path("libeay32.dll") + self.path("qtcore4.dll") + self.path("qtgui4.dll") + self.path("qtnetwork4.dll") + self.path("qtopengl4.dll") + self.path("qtwebkit4.dll") + self.path("qtxmlpatterns4.dll") + self.path("ssleay32.dll") + + # For WebKit/Qt plugin runtimes (image format plugins) + if self.prefix(src="imageformats", dst="imageformats"): + self.path("qgif4.dll") + self.path("qico4.dll") + self.path("qjpeg4.dll") + self.path("qmng4.dll") + self.path("qsvg4.dll") + self.path("qtiff4.dll") + self.end_prefix() + + # For WebKit/Qt plugin runtimes (codec/character encoding plugins) + if self.prefix(src="codecs", dst="codecs"): + self.path("qcncodecs4.dll") + self.path("qjpcodecs4.dll") + self.path("qkrcodecs4.dll") + self.path("qtwcodecs4.dll") + self.end_prefix() + + self.end_prefix() + + self.disable_manifest_check() + + # pull in the crash logger and updater from other projects + # tag:"crash-logger" here as a cue to the exporter + self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'], + dst="win_crash_logger.exe") + self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'], + dst="updater.exe") + + if not self.is_packaging_viewer(): + self.package_file = "copied_deps" + + def nsi_file_commands(self, install=True): + def wpath(path): + if path.endswith('/') or path.endswith(os.path.sep): + path = path[:-1] + path = path.replace('/', '\\') + return path + + result = "" + dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])] + # sort deepest hierarchy first + dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) + dest_files.reverse() + out_path = None + for pkg_file in dest_files: + rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,'')) + installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file))) + pkg_file = wpath(os.path.normpath(pkg_file)) + if installed_dir != out_path: + if install: + out_path = installed_dir + result += 'SetOutPath ' + out_path + '\n' + if install: + result += 'File ' + pkg_file + '\n' + else: + result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n' + # at the end of a delete, just rmdir all the directories + if not install: + deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list] + # find all ancestors so that we don't skip any dirs that happened to have no non-dir children + deleted_dirs = [] + for d in deleted_file_dirs: + deleted_dirs.extend(path_ancestors(d)) + # sort deepest hierarchy first + deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) + deleted_dirs.reverse() + prev = None + for d in deleted_dirs: + if d != prev: # skip duplicates + result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n' + prev = d + + return result + + def package_finish(self): + # a standard map of strings for replacing in the templates + substitution_strings = { + 'version' : '.'.join(self.args['version']), + 'version_short' : '.'.join(self.args['version'][:-1]), + 'version_dashes' : '-'.join(self.args['version']), + 'final_exe' : self.final_exe(), + 'grid':self.args['grid'], + 'grid_caps':self.args['grid'].upper(), + # escape quotes becase NSIS doesn't handle them well + 'flags':self.flags_list().replace('"', '$\\"'), + 'channel':self.channel(), + 'channel_oneword':self.channel_oneword(), + 'channel_unique':self.channel_unique(), + } + + version_vars = """ + !define INSTEXE "%(final_exe)s" + !define VERSION "%(version_short)s" + !define VERSION_LONG "%(version)s" + !define VERSION_DASHES "%(version_dashes)s" + """ % substitution_strings + if self.default_channel(): + if self.default_grid(): + # release viewer + installer_file = "Second_Life_%(version_dashes)s_Setup.exe" + grid_vars_template = """ + OutFile "%(installer_file)s" + !define INSTFLAGS "%(flags)s" + !define INSTNAME "SecondLifeViewer2" + !define SHORTCUT "Second Life Viewer 2" + !define URLNAME "secondlife" + Caption "Second Life ${VERSION}" + """ + else: + # beta grid viewer + installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe" + grid_vars_template = """ + OutFile "%(installer_file)s" + !define INSTFLAGS "%(flags)s" + !define INSTNAME "SecondLife%(grid_caps)s" + !define SHORTCUT "Second Life (%(grid_caps)s)" + !define URLNAME "secondlife%(grid)s" + !define UNINSTALL_SETTINGS 1 + Caption "Second Life %(grid)s ${VERSION}" + """ + else: + # some other channel on some grid + installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe" + grid_vars_template = """ + OutFile "%(installer_file)s" + !define INSTFLAGS "%(flags)s" + !define INSTNAME "SecondLife%(channel_oneword)s" + !define SHORTCUT "%(channel)s" + !define URLNAME "secondlife" + !define UNINSTALL_SETTINGS 1 + Caption "%(channel)s ${VERSION}" + """ + if 'installer_name' in self.args: + installer_file = self.args['installer_name'] + else: + installer_file = installer_file % substitution_strings + substitution_strings['installer_file'] = installer_file + + tempfile = "secondlife_setup_tmp.nsi" + # the following replaces strings in the nsi template + # it also does python-style % substitution + self.replace_in("installers/windows/installer_template.nsi", tempfile, { + "%%VERSION%%":version_vars, + "%%SOURCE%%":self.get_src_prefix(), + "%%GRID_VARS%%":grid_vars_template % substitution_strings, + "%%INSTALL_FILES%%":self.nsi_file_commands(True), + "%%DELETE_FILES%%":self.nsi_file_commands(False)}) + + # We use the Unicode version of NSIS, available from + # http://www.scratchpaper.com/ + # Check two paths, one for Program Files, and one for Program Files (x86). + # Yay 64bit windows. + NSIS_path = os.path.expandvars('${ProgramFiles}\\NSIS\\Unicode\\makensis.exe') + if not os.path.exists(NSIS_path): + NSIS_path = os.path.expandvars('${ProgramFiles(x86)}\\NSIS\\Unicode\\makensis.exe') + self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) + # self.remove(self.dst_path_of(tempfile)) + # If we're on a build machine, sign the code using our Authenticode certificate. JC + sign_py = os.path.expandvars("${SIGN}") + if not sign_py or sign_py == "${SIGN}": + sign_py = 'C:\\buildscripts\\code-signing\\sign.py' + else: + sign_py = sign_py.replace('\\', '\\\\\\\\') + python = os.path.expandvars("${PYTHON}") + if not python or python == "${PYTHON}": + python = 'python' + if os.path.exists(sign_py): + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\\', '\\\\\\\\'))) + else: + print "Skipping code signing,", sign_py, "does not exist" + self.created_path(self.dst_path_of(installer_file)) + self.package_file = installer_file - def construct(self): - super(WindowsManifest, self).construct() - # the final exe is complicated because we're not sure where it's coming from, - # nor do we have a fixed name for the executable - self.path(self.find_existing_file('ReleaseForDownload/Secondlife.exe', 'Secondlife.exe', 'ReleaseNoOpt/newview_noopt.exe'), dst=self.final_exe()) - # need to get the kdu dll from any of the build directories as well - self.path(self.find_existing_file('ReleaseForDownload/llkdu.dll', 'llkdu.dll', '../../libraries/i686-win32/lib_release/llkdu.dll'), dst='llkdu.dll') - self.path(src="licenses-win32.txt", dst="licenses.txt") +class DarwinManifest(ViewerManifest): + def is_packaging_viewer(self): + # darwin requires full app bundle packaging even for debugging. + return True - self.path("featuretable.txt") + def construct(self): + # copy over the build result (this is a no-op if run within the xcode script) + self.path(self.args['configuration'] + "/Second Life.app", dst="") - # For use in crash reporting (generates minidumps) - self.path("dbghelp.dll") + if self.prefix(src="", dst="Contents"): # everything goes in Contents + self.path("Info-SecondLife.plist", dst="Info.plist") - # For using FMOD for sound... DJS - self.path("fmod.dll") + # copy additional libs in <bundle>/Contents/MacOS/ + self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib") - # For textures - if self.prefix(src="../../libraries/i686-win32/lib_release", dst=""): - self.path("openjpeg.dll") - self.end_prefix() - - # Mozilla appears to force a dependency on these files so we need to ship it (CP) - self.path("msvcr71.dll") - self.path("msvcp71.dll") - - # Mozilla runtime DLLs (CP) - if self.prefix(src="../../libraries/i686-win32/lib_release", dst=""): - self.path("freebl3.dll") - self.path("gksvggdiplus.dll") - self.path("js3250.dll") - self.path("nspr4.dll") - self.path("nss3.dll") - self.path("nssckbi.dll") - self.path("plc4.dll") - self.path("plds4.dll") - self.path("smime3.dll") - self.path("softokn3.dll") - self.path("ssl3.dll") - self.path("xpcom.dll") - self.path("xul.dll") - self.end_prefix() - - # Mozilla runtime misc files (CP) - if self.prefix(src="app_settings/mozilla"): - self.path("chrome/*.*") - self.path("components/*.*") - self.path("greprefs/*.*") - self.path("plugins/*.*") - self.path("res/*.*") - self.path("res/*/*") - self.end_prefix() - - # Vivox runtimes - if self.prefix(src="vivox-runtime/i686-win32", dst=""): - self.path("SLVoice.exe") - self.path("SLVoiceAgent.exe") - self.path("libeay32.dll") - self.path("srtp.dll") - self.path("ssleay32.dll") - self.path("tntk.dll") - self.path("alut.dll") - self.path("vivoxsdk.dll") - self.path("ortp.dll") - self.path("wrap_oal.dll") - self.end_prefix() - - # pull in the crash logger and updater from other projects - self.path(src="../win_crash_logger/win_crash_logger.exe", dst="win_crash_logger.exe") - self.path(src="../win_updater/updater.exe", dst="updater.exe") - self.replace_login_url() - - def nsi_file_commands(self, install=True): - def wpath(path): - if(path.endswith('/') or path.endswith(os.path.sep)): - path = path[:-1] - path = path.replace('/', '\\') - return path - - result = "" - dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])] - # sort deepest hierarchy first - dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) - dest_files.reverse() - out_path = None - for pkg_file in dest_files: - rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,'')) - installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file))) - pkg_file = wpath(os.path.normpath(pkg_file)) - if installed_dir != out_path: - if(install): - out_path = installed_dir - result += 'SetOutPath ' + out_path + '\n' - if(install): - result += 'File ' + pkg_file + '\n' - else: - result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n' - # at the end of a delete, just rmdir all the directories - if(not install): - deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list] - # find all ancestors so that we don't skip any dirs that happened to have no non-dir children - deleted_dirs = [] - for d in deleted_file_dirs: - deleted_dirs.extend(path_ancestors(d)) - # sort deepest hierarchy first - deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) - deleted_dirs.reverse() - prev = None - for d in deleted_dirs: - if d != prev: # skip duplicates - result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n' - prev = d - - return result - - def package_finish(self): - # a standard map of strings for replacing in the templates - substitution_strings = { - 'version' : '.'.join(self.args['version']), - 'version_short' : '.'.join(self.args['version'][:-1]), - 'version_dashes' : '-'.join(self.args['version']), - 'final_exe' : self.final_exe(), - 'grid':self.args['grid'], - 'grid_caps':self.args['grid'].upper(), - # escape quotes becase NSIS doesn't handle them well - 'flags':self.flags_list().replace('"', '$\\"'), - 'channel':self.channel(), - 'channel_oneword':self.channel_oneword(), - 'channel_unique':self.channel_unique(), - } - - version_vars = """ - !define INSTEXE "%(final_exe)s" - !define VERSION "%(version_short)s" - !define VERSION_LONG "%(version)s" - !define VERSION_DASHES "%(version_dashes)s" - """ % substitution_strings - if self.default_channel(): - if self.default_grid(): - # release viewer - installer_file = "Second_Life_%(version_dashes)s_Setup.exe" - grid_vars_template = """ - OutFile "%(installer_file)s" - !define INSTFLAGS "%(flags)s" - !define INSTNAME "SecondLife" - !define SHORTCUT "Second Life" - !define URLNAME "secondlife" - Caption "Second Life ${VERSION}" - """ - else: - # beta grid viewer - installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe" - grid_vars_template = """ - OutFile "%(installer_file)s" - !define INSTFLAGS "%(flags)s" - !define INSTNAME "SecondLife%(grid_caps)s" - !define SHORTCUT "Second Life (%(grid_caps)s)" - !define URLNAME "secondlife%(grid)s" - !define UNINSTALL_SETTINGS 1 - Caption "Second Life %(grid)s ${VERSION}" - """ - else: - # some other channel on some grid - installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe" - grid_vars_template = """ - OutFile "%(installer_file)s" - !define INSTFLAGS "%(flags)s" - !define INSTNAME "SecondLife%(channel_oneword)s" - !define SHORTCUT "%(channel)s" - !define URLNAME "secondlife" - !define UNINSTALL_SETTINGS 1 - Caption "%(channel)s ${VERSION}" - """ - if(self.args.has_key('installer_name')): - installer_file = self.args['installer_name'] - else: - installer_file = installer_file % substitution_strings - substitution_strings['installer_file'] = installer_file - - tempfile = "../secondlife_setup_tmp.nsi" - # the following replaces strings in the nsi template - # it also does python-style % substitution - self.replace_in("installers/windows/installer_template.nsi", tempfile, { - "%%VERSION%%":version_vars, - "%%GRID_VARS%%":grid_vars_template % substitution_strings, - "%%INSTALL_FILES%%":self.nsi_file_commands(True), - "%%DELETE_FILES%%":self.nsi_file_commands(False)}) + # most everything goes in the Resources directory + if self.prefix(src="", dst="Resources"): + super(DarwinManifest, self).construct() - NSIS_path = 'C:\\Program Files\\NSIS\\makensis.exe' - self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) - # self.remove(self.dst_path_of(tempfile)) - self.created_path(installer_file) + if self.prefix("cursors_mac"): + self.path("*.tif") + self.end_prefix("cursors_mac") + self.path("licenses-mac.txt", dst="licenses.txt") + self.path("featuretable_mac.txt") + self.path("SecondLife.nib") -class DarwinManifest(ViewerManifest): - def construct(self): - # copy over the build result (this is a no-op if run within the xcode script) - self.path("build/" + self.args['configuration'] + "/Second Life.app", dst="") - - if self.prefix(src="", dst="Contents"): # everything goes in Contents - # Expand the tar file containing the assorted mozilla bits into - # <bundle>/Contents/MacOS/ - self.contents_of_tar('mozilla-universal-darwin.tgz', 'MacOS') - - # replace the default theme with our custom theme (so scrollbars work). - if self.prefix(src="mozilla-theme", dst="MacOS/chrome"): - self.path("classic.jar") - self.path("classic.manifest") - self.end_prefix("MacOS/chrome") - - # most everything goes in the Resources directory - if self.prefix(src="", dst="Resources"): - super(DarwinManifest, self).construct() - - if self.prefix("cursors_mac"): - self.path("*.tif") - self.end_prefix("cursors_mac") - - self.path("licenses-mac.txt", dst="licenses.txt") - self.path("featuretable_mac.txt") - self.path("secondlife.icns") - - # llkdu dynamic library - self.path("../../libraries/universal-darwin/lib_release/libllkdu.dylib", "libllkdu.dylib") - - # command line arguments for connecting to the proper grid - self.put_in_file(self.flags_list(), 'arguments.txt') - - # set the proper login url - self.replace_login_url() - - self.end_prefix("Resources") - - self.end_prefix("Contents") - - # NOTE: the -S argument to strip causes it to keep enough info for - # annotated backtraces (i.e. function names in the crash log). 'strip' with no - # arguments yields a slightly smaller binary but makes crash logs mostly useless. - # This may be desirable for the final release. Or not. - if("package" in self.args['actions'] or - "unpacked" in self.args['actions']): - self.run_command('strip -S "%(viewer_binary)s"' % - { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')}) - - - def package_finish(self): - channel_standin = 'Second Life' # hah, our default channel is not usable on its own - if not self.default_channel(): - channel_standin = self.channel() - - imagename="SecondLife_" + '_'.join(self.args['version']) - if self.default_channel(): - if not self.default_grid(): - # beta case - imagename = imagename + '_' + self.args['grid'].upper() + # If we are not using the default channel, use the 'Firstlook + # icon' to show that it isn't a stable release. + if self.default_channel() and self.default_grid(): + self.path("secondlife.icns") else: - # first look, etc - imagename = imagename + '_' + self.channel_oneword().upper() - - sparsename = imagename + ".sparseimage" - finalname = imagename + ".dmg" - # make sure we don't have stale files laying about - self.remove(sparsename, finalname) - - self.run_command('hdiutil create "%(sparse)s" -volname "%(channel)s" -fs HFS+ -type SPARSE -megabytes 300 -layout SPUD' % { - 'sparse':sparsename, - 'channel':channel_standin}) - - # mount the image and get the name of the mount point and device node - hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"') - devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() - volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() - - # Copy everything in to the mounted .dmg - if self.default_channel() and not self.default_grid(): - app_name = "Second Life " + self.args['grid'] - else: - app_name = channel_standin.strip() - - for s,d in {self.get_dst_prefix():app_name + ".app", - "lsl_guide.html":"Linden Scripting Language Guide.html", - "releasenotes.txt":"Release Notes.txt", - "installers/darwin/mac_image_hidden":".hidden", - "installers/darwin/mac_image_background.tga":"background.tga", - "installers/darwin/mac_image_DS_Store":".DS_Store"}.items(): - print "Copying to dmg", s, d - self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) - - # Unmount the image - self.run_command('hdiutil detach -force "' + devfile + '"') - - print "Converting temp disk image to final disk image" - self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname}) - # get rid of the temp file - self.remove(sparsename) + self.path("secondlife_firstlook.icns", "secondlife.icns") + self.path("SecondLife.nib") + + # Translations + self.path("English.lproj") + self.path("German.lproj") + self.path("Japanese.lproj") + self.path("Korean.lproj") + self.path("da.lproj") + self.path("es.lproj") + self.path("fr.lproj") + self.path("hu.lproj") + self.path("it.lproj") + self.path("nl.lproj") + self.path("pl.lproj") + self.path("pt.lproj") + self.path("ru.lproj") + self.path("tr.lproj") + self.path("uk.lproj") + self.path("zh-Hans.lproj") + + # SLVoice and vivox lols + self.path("vivox-runtime/universal-darwin/libsndfile.dylib", "libsndfile.dylib") + self.path("vivox-runtime/universal-darwin/libvivoxoal.dylib", "libvivoxoal.dylib") + self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib") + self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib") + self.path("vivox-runtime/universal-darwin/libvivoxplatform.dylib", "libvivoxplatform.dylib") + self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice") + + libdir = "../../libraries/universal-darwin/lib_release" + dylibs = {} + + # need to get the kdu dll from any of the build directories as well + for lib in "llkdu", "llcommon": + libfile = "lib%s.dylib" % lib + try: + self.path(self.find_existing_file(os.path.join(os.pardir, + lib, + self.args['configuration'], + libfile), + os.path.join(libdir, libfile)), + dst=libfile) + except RuntimeError: + print "Skipping %s" % libfile + dylibs[lib] = False + else: + dylibs[lib] = True + + if dylibs["llcommon"]: + for libfile in ("libapr-1.0.3.7.dylib", + "libaprutil-1.0.3.8.dylib", + "libexpat.0.5.0.dylib", + "libexception_handler.dylib", + ): + self.path(os.path.join(libdir, libfile), libfile) + + try: + # FMOD for sound + self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib") + except: + print "Skipping FMOD - not found" + + # our apps + self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app") + self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app") + + # plugin launcher + self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin.app", "SLPlugin.app") + + # our apps dependencies on shared libs + if dylibs["llcommon"]: + mac_crash_logger_res_path = self.dst_path_of("mac-crash-logger.app/Contents/Resources") + mac_updater_res_path = self.dst_path_of("mac-updater.app/Contents/Resources") + slplugin_res_path = self.dst_path_of("SLPlugin.app/Contents/Resources") + for libfile in ("libllcommon.dylib", + "libapr-1.0.3.7.dylib", + "libaprutil-1.0.3.8.dylib", + "libexpat.0.5.0.dylib", + "libexception_handler.dylib", + ): + target_lib = os.path.join('../../..', libfile) + self.run_command("ln -sf %(target)r %(link)r" % + {'target': target_lib, + 'link' : os.path.join(mac_crash_logger_res_path, libfile)} + ) + self.run_command("ln -sf %(target)r %(link)r" % + {'target': target_lib, + 'link' : os.path.join(mac_updater_res_path, libfile)} + ) + self.run_command("ln -sf %(target)r %(link)r" % + {'target': target_lib, + 'link' : os.path.join(slplugin_res_path, libfile)} + ) + + # plugins + if self.prefix(src="", dst="llplugin"): + self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") + self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") + self.path("../../libraries/universal-darwin/lib_release/libllqtwebkit.dylib", "libllqtwebkit.dylib") + + self.end_prefix("llplugin") + + # command line arguments for connecting to the proper grid + self.put_in_file(self.flags_list(), 'arguments.txt') + + self.end_prefix("Resources") + + self.end_prefix("Contents") + + # NOTE: the -S argument to strip causes it to keep enough info for + # annotated backtraces (i.e. function names in the crash log). 'strip' with no + # arguments yields a slightly smaller binary but makes crash logs mostly useless. + # This may be desirable for the final release. Or not. + if ("package" in self.args['actions'] or + "unpacked" in self.args['actions']): + self.run_command('strip -S %(viewer_binary)r' % + { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')}) + + + def package_finish(self): + channel_standin = 'Second Life Viewer 2' # hah, our default channel is not usable on its own + if not self.default_channel(): + channel_standin = self.channel() + + imagename="SecondLife_" + '_'.join(self.args['version']) + + # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning. + # If we really need differently named volumes, we'll need to create multiple DS_Store file images, or use some other trick. + + volname="Second Life Installer" # DO NOT CHANGE without understanding comment above + + if self.default_channel(): + if not self.default_grid(): + # beta case + imagename = imagename + '_' + self.args['grid'].upper() + else: + # first look, etc + imagename = imagename + '_' + self.channel_oneword().upper() + + sparsename = imagename + ".sparseimage" + finalname = imagename + ".dmg" + # make sure we don't have stale files laying about + self.remove(sparsename, finalname) + + self.run_command('hdiutil create %(sparse)r -volname %(vol)r -fs HFS+ -type SPARSE -megabytes 700 -layout SPUD' % { + 'sparse':sparsename, + 'vol':volname}) + + # mount the image and get the name of the mount point and device node + hdi_output = self.run_command('hdiutil attach -private %r' % sparsename) + try: + devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() + volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() + + # Copy everything in to the mounted .dmg + + if self.default_channel() and not self.default_grid(): + app_name = "Second Life " + self.args['grid'] + else: + app_name = channel_standin.strip() + + # Hack: + # Because there is no easy way to coerce the Finder into positioning + # the app bundle in the same place with different app names, we are + # adding multiple .DS_Store files to svn. There is one for release, + # one for release candidate and one for first look. Any other channels + # will use the release .DS_Store, and will look broken. + # - Ambroff 2008-08-20 + dmg_template = os.path.join( + 'installers', + 'darwin', + '%s-dmg' % "".join(self.channel_unique().split()).lower()) + + if not os.path.exists (self.src_path_of(dmg_template)): + dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') + + for s,d in {self.get_dst_prefix():app_name + ".app", + os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", + os.path.join(dmg_template, "background.jpg"): "background.jpg", + os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): + print "Copying to dmg", s, d + self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) + + # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) + for f in ".VolumeIcon.icns", "background.jpg", ".DS_Store": + pathname = os.path.join(volpath, f) + # We've observed mysterious "no such file" failures of the SetFile + # command, especially on the first file listed above -- yet + # subsequent inspection of the target directory confirms it's + # there. Timing problem with copy command? Try to handle. + for x in xrange(3): + if os.path.exists(pathname): + print "Confirmed existence: %r" % pathname + break + print "Waiting for %s copy command to complete (%s)..." % (f, x+1) + sys.stdout.flush() + time.sleep(1) + # If we fall out of the loop above without a successful break, oh + # well, possibly we've mistaken the nature of the problem. In any + # case, don't hang up the whole build looping indefinitely, let + # the original problem manifest by executing the desired command. + self.run_command('SetFile -a V %r' % pathname) + + # Create the alias file (which is a resource file) from the .r + self.run_command('rez %r -o %r' % + (self.src_path_of("installers/darwin/release-dmg/Applications-alias.r"), + os.path.join(volpath, "Applications"))) + + # Set the alias file's alias and custom icon bits + self.run_command('SetFile -a AC %r' % os.path.join(volpath, "Applications")) + + # Set the disk image root's custom icon bit + self.run_command('SetFile -a C %r' % volpath) + finally: + # Unmount the image even if exceptions from any of the above + self.run_command('hdiutil detach -force %r' % devfile) + + print "Converting temp disk image to final disk image" + self.run_command('hdiutil convert %(sparse)r -format UDZO -imagekey zlib-level=9 -o %(final)r' % {'sparse':sparsename, 'final':finalname}) + # get rid of the temp file + self.package_file = finalname + self.remove(sparsename) class LinuxManifest(ViewerManifest): - def construct(self): - super(LinuxManifest, self).construct() - self.path("licenses-linux.txt","licenses.txt") - #self.path("res/ll_icon.ico","secondlife.ico") - if self.prefix("linux_tools", ""): - self.path("client-readme.txt","README-linux.txt") - self.path("client-readme-voice.txt","README-linux-voice.txt") - self.path("wrapper.sh","secondlife") - self.path("handle_secondlifeprotocol.sh") - self.path("register_secondlifeprotocol.sh") - self.path("unicode.ttf","unicode.ttf") - self.end_prefix("linux_tools") - - # Create an appropriate gridargs.dat for this package, denoting required grid. - self.put_in_file(self.flags_list(), 'gridargs.dat') - # set proper login url - self.replace_login_url() - - # stripping all the libs removes a few megabytes from the end-user package - for s,d in self.file_list: - if re.search("lib/lib.+\.so.*", d): - self.run_command('strip -S %s' % d) - - - def package_finish(self): - if(self.args.has_key('installer_name')): - installer_name = self.args['installer_name'] - else: - installer_name = '_'.join('SecondLife_', self.args.get('arch'), *self.args['version']) - if self.default_channel(): - if not self.default_grid(): - installer_name += '_' + self.args['grid'].upper() - else: - installer_name += '_' + self.channel_oneword().upper() - - # temporarily move directory tree so that it has the right name in the tarfile - self.run_command("mv %(dst)s %(inst)s" % {'dst':self.get_dst_prefix(),'inst':self.src_path_of(installer_name)}) - # --numeric-owner hides the username of the builder for security etc. - self.run_command('tar -C %(dir)s --numeric-owner -cjf %(inst_path)s.tar.bz2 %(inst_name)s' % {'dir':self.get_src_prefix(), 'inst_name': installer_name, 'inst_path':self.src_path_of(installer_name)}) - self.run_command("mv %(inst)s %(dst)s" % {'dst':self.get_dst_prefix(),'inst':self.src_path_of(installer_name)}) + def construct(self): + super(LinuxManifest, self).construct() + self.path("licenses-linux.txt","licenses.txt") + self.path("res/ll_icon.png","secondlife_icon.png") + if self.prefix("linux_tools", dst=""): + self.path("client-readme.txt","README-linux.txt") + self.path("client-readme-voice.txt","README-linux-voice.txt") + self.path("client-readme-joystick.txt","README-linux-joystick.txt") + self.path("wrapper.sh","secondlife") + self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh") + self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh") + self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh") + self.path("launch_url.sh","etc/launch_url.sh") + self.path("install.sh") + self.end_prefix("linux_tools") + + # Create an appropriate gridargs.dat for this package, denoting required grid. + self.put_in_file(self.flags_list(), 'etc/gridargs.dat') + + self.path("secondlife-bin","bin/do-not-directly-run-secondlife-bin") + self.path("../linux_crash_logger/linux-crash-logger","bin/linux-crash-logger.bin") + self.path("../linux_updater/linux-updater", "bin/linux-updater.bin") + self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin") + + if self.prefix("res-sdl"): + self.path("*") + # recurse + self.end_prefix("res-sdl") + + # plugins + if self.prefix(src="", dst="bin/llplugin"): + self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") + self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") + self.end_prefix("bin/llplugin") + + try: + self.path("../llcommon/libllcommon.so", "lib/libllcommon.so") + except: + print "Skipping llcommon.so (assuming llcommon was linked statically)" + + self.path("featuretable_linux.txt") + + def package_finish(self): + if 'installer_name' in self.args: + installer_name = self.args['installer_name'] + else: + installer_name_components = ['SecondLife_', self.args.get('arch')] + installer_name_components.extend(self.args['version']) + installer_name = "_".join(installer_name_components) + if self.default_channel(): + if not self.default_grid(): + installer_name += '_' + self.args['grid'].upper() + else: + installer_name += '_' + self.channel_oneword().upper() + + if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer(): + print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" + self.run_command("find %(d)r/bin %(d)r/lib -type f | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure + + # Fix access permissions + self.run_command(""" + find %(dst)s -type d | xargs --no-run-if-empty chmod 755; + find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755; + find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555; + find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644; + find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444; + true""" % {'dst':self.get_dst_prefix() }) + self.package_file = installer_name + '.tar.bz2' + + # temporarily move directory tree so that it has the right + # name in the tarfile + self.run_command("mv %(dst)s %(inst)s" % { + 'dst': self.get_dst_prefix(), + 'inst': self.build_path_of(installer_name)}) + try: + # only create tarball if it's a release build. + if self.args['buildtype'].lower() == 'release': + # --numeric-owner hides the username of the builder for + # security etc. + self.run_command('tar -C %(dir)s --numeric-owner -cjf ' + '%(inst_path)s.tar.bz2 %(inst_name)s' % { + 'dir': self.get_build_prefix(), + 'inst_name': installer_name, + 'inst_path':self.build_path_of(installer_name)}) + finally: + self.run_command("mv %(inst)s %(dst)s" % { + 'dst': self.get_dst_prefix(), + 'inst': self.build_path_of(installer_name)}) class Linux_i686Manifest(LinuxManifest): - def construct(self): - super(Linux_i686Manifest, self).construct() - self.path("secondlife-i686-bin-stripped","bin/do-not-directly-run-secondlife-bin") - self.path("../linux_crash_logger/linux-crash-logger-i686-bin-stripped","linux-crash-logger.bin") - self.path("linux_tools/launch_url.sh","launch_url.sh") - if self.prefix("res-sdl"): - self.path("*") - # recurse - self.end_prefix("res-sdl") - - self.path("featuretable_linux.txt") - #self.path("secondlife-i686.supp") - - self.path("app_settings/mozilla-runtime-linux-i686") - - if self.prefix("../../libraries/i686-linux/lib_release_client", "lib"): - self.path("libkdu_v42R.so") - self.path("libfmod-3.75.so") - self.path("libapr-1.so.0") - self.path("libaprutil-1.so.0") - self.path("libdb-4.2.so") - self.path("libcrypto.so.0.9.7") - self.path("libssl.so.0.9.7") - self.path("libexpat.so.1") - self.path("libstdc++.so.6") - self.path("libuuid.so", "libuuid.so.1") - self.path("libSDL-1.2.so.0") - self.path("libELFIO.so") - self.path("libopenjpeg.so.2") - #self.path("libtcmalloc.so.0") - bugged - #self.path("libstacktrace.so.0") - probably bugged - self.path("libllkdu.so", "../bin/libllkdu.so") # llkdu goes in bin for some reason - self.end_prefix("lib") - - # Vivox runtimes - if self.prefix(src="vivox-runtime/i686-linux", dst=""): - self.path("SLVoice") - self.end_prefix() - if self.prefix(src="vivox-runtime/i686-linux", dst="lib"): - self.path("libopenal.so.1") - self.path("libortp.so") - self.path("libvivoxsdk.so") - self.path("libalut.so") - self.end_prefix("lib") + def construct(self): + super(Linux_i686Manifest, self).construct() + + # install either the libllkdu we just built, or a prebuilt one, in + # decreasing order of preference. for linux package, this goes to bin/ + try: + self.path(self.find_existing_file('../llkdu/libllkdu.so', + '../../libraries/i686-linux/lib_release_client/libllkdu.so'), + dst='bin/libllkdu.so') + except: + print "Skipping libllkdu.so - not found" + + if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"): + self.path("libapr-1.so.0") + self.path("libaprutil-1.so.0") + self.path("libbreakpad_client.so.0.0.0", "libbreakpad_client.so.0") + self.path("libdb-4.2.so") + self.path("libcrypto.so.0.9.7") + self.path("libexpat.so.1") + self.path("libssl.so.0.9.7") + self.path("libuuid.so.1") + self.path("libSDL-1.2.so.0") + self.path("libELFIO.so") + self.path("libopenjpeg.so.1.3.0", "libopenjpeg.so.1.3") + self.path("libalut.so") + self.path("libopenal.so", "libopenal.so.1") + self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname + try: + self.path("libkdu.so") + pass + except: + print "Skipping libkdu.so - not found" + pass + try: + self.path("libfmod-3.75.so") + pass + except: + print "Skipping libfmod-3.75.so - not found" + pass + self.end_prefix("lib") + + # Vivox runtimes + if self.prefix(src="vivox-runtime/i686-linux", dst="bin"): + self.path("SLVoice") + self.end_prefix() + if self.prefix(src="vivox-runtime/i686-linux", dst="lib"): + self.path("libortp.so") + self.path("libsndfile.so.1") + #self.path("libvivoxoal.so.1") # no - we'll re-use the viewer's own OpenAL lib + self.path("libvivoxsdk.so") + self.path("libvivoxplatform.so") + self.end_prefix("lib") class Linux_x86_64Manifest(LinuxManifest): - def construct(self): - super(Linux_x86_64Manifest, self).construct() - self.path("secondlife-x86_64-bin-stripped","bin/do-not-directly-run-secondlife-bin") - self.path("../linux_crash_logger/linux-crash-logger-x86_64-bin-stripped","linux-crash-logger.bin") - self.path("linux_tools/launch_url.sh","launch_url.sh") - if self.prefix("res-sdl"): - self.path("*") - # recurse - self.end_prefix("res-sdl") - - self.path("featuretable_linux.txt") - self.path("secondlife-i686.supp") + def construct(self): + super(Linux_x86_64Manifest, self).construct() + + # support file for valgrind debug tool + self.path("secondlife-i686.supp") + +################################################################ if __name__ == "__main__": - main(srctree=viewer_dir, dsttree=os.path.join(viewer_dir, "packaged")) + main() |
