Newer
Older
Merov Linden
committed
#!/usr/bin/env python
"""\
@file viewer_manifest.py
@author Ryan Williams
@brief Description of all installer viewer files, and methods for packaging
them into installers for all supported platforms.
$LicenseInfo:firstyear=2006&license=viewerlgpl$
Second Life Viewer Source Code
Copyright (C) 2006-2011, 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.
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.
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
Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
$/LicenseInfo$
"""
import sys
import os.path
import re
import tarfile
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 is_packaging_viewer(self):
brad kittenbrink
committed
# 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']
Bryan O'Sullivan
committed
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")
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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")
Bryan O'Sullivan
committed
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']
Bryan O'Sullivan
committed
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
Bryan O'Sullivan
committed
grid_flags = ''
if not self.default_grid():
grid_flags = "--grid %(grid)s "\
"--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\
{'grid':self.grid()}
Bryan O'Sullivan
committed
# set command line flags for channel
channel_flags = ''
if self.login_channel() and self.login_channel() != self.channel():
Bryan O'Sullivan
committed
# 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):
Bryan O'Sullivan
committed
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:
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
brad kittenbrink
committed
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")
brad kittenbrink
committed
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
brad kittenbrink
committed
### DISABLED MANIFEST CHECKING for vs2010. we may need to reenable this
# shortly. If this hasn't been reenabled by the 2.9 viewer release then it
# should be deleted -brad
#def enable_crt_manifest_check(self):
# if self.is_packaging_viewer():
# WindowsManifest.copy_action = WindowsManifest.test_msvcrt_and_copy_action
brad kittenbrink
committed
#def enable_no_crt_manifest_check(self):
# if self.is_packaging_viewer():
# WindowsManifest.copy_action = WindowsManifest.test_for_no_msvcrt_manifest_and_copy_action
brad kittenbrink
committed
#def disable_manifest_check(self):
# if self.is_packaging_viewer():
# del WindowsManifest.copy_action
Bryan O'Sullivan
committed
def construct(self):
super(WindowsManifest, self).construct()
Mark Palange (Mani)
committed
brad kittenbrink
committed
#self.enable_crt_manifest_check()
Mark Palange (Mani)
committed
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())
Monroe Williams
committed
# Plugin host application
self.path(os.path.join(os.pardir,
'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"),
"slplugin.exe")
brad kittenbrink
committed
#self.disable_manifest_check()
self.path(src="../viewer_components/updater/scripts/windows/update_install.bat", dst="update_install.bat")
# Get shared libs from the shared libs staging directory
if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
dst=""):
brad kittenbrink
committed
#self.enable_crt_manifest_check()
# 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)"
brad kittenbrink
committed
#self.disable_manifest_check()
Bryan O'Sullivan
committed
Merov Linden
committed
# 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':
Alain Linden
committed
self.path("msvcr100d.dll")
Alain Linden
committed
self.path("msvcp100d.dll")
Alain Linden
committed
self.path("msvcr100.dll")
self.path("msvcp100.dll")
# 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")
# Security
self.path("ssleay32.dll")
self.path("libeay32.dll")
# For use in crash reporting (generates minidumps)
if self.args['configuration'].lower() != 'debug':
self.path("dbghelp.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")
Bryan O'Sullivan
committed
self.path("featuretable.txt")
self.path("featuretable_xp.txt")
Bryan O'Sullivan
committed
brad kittenbrink
committed
#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()
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, 'packages', 'lib', 'debug'),
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("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()
callum
committed
# 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, 'packages', 'lib', 'release'),
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("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()
callum
committed
# 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()
brad kittenbrink
committed
#self.disable_manifest_check()
Bryan O'Sullivan
committed
# 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'],
Bryan O'Sullivan
committed
dst="win_crash_logger.exe")
self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'],
Bryan O'Sullivan
committed
dst="updater.exe")
if not self.is_packaging_viewer():
self.package_file = "copied_deps"
Bryan O'Sullivan
committed
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
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"
Mark Palange (Mani)
committed
!define INSTNAME "SecondLifeViewer2"
!define SHORTCUT "Second Life Viewer 2"
Bryan O'Sullivan
committed
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
!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')
Bryan O'Sullivan
committed
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'
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"
Bryan O'Sullivan
committed
self.created_path(self.dst_path_of(installer_file))
self.package_file = installer_file
class DarwinManifest(ViewerManifest):
def is_packaging_viewer(self):
# darwin requires full app bundle packaging even for debugging.
return True
Bryan O'Sullivan
committed
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="")
if self.prefix(src="", dst="Contents"): # everything goes in Contents
self.path("Info-SecondLife.plist", dst="Info.plist")
# copy additional libs in <bundle>/Contents/MacOS/
self.path("../packages/lib/release/libndofdev.dylib", dst="Resources/libndofdev.dylib")
self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install")
Bryan O'Sullivan
committed
# 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.nib")
# 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:
self.path("secondlife_firstlook.icns", "secondlife.icns")
Bryan O'Sullivan
committed
# 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")
Bryan O'Sullivan
committed
Andrew A. de Laix
committed
libdir = "../packages/lib/release"
# Need to get the llcommon dll from any of the build directories as well
lib = "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.dylib",
"libaprutil-1.0.dylib",
"libexpat.1.5.2.dylib",
"libexception_handler.dylib",
):
self.path(os.path.join(libdir, libfile), libfile)
# SLVoice and vivox lols
for libfile in ('libsndfile.dylib', 'libvivoxoal.dylib', 'libortp.dylib', \
'libvivoxsdk.dylib', 'libvivoxplatform.dylib', 'SLVoice') :
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")
Bryan O'Sullivan
committed
Monroe Linden
committed
# 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")
Monroe Linden
committed
slplugin_res_path = self.dst_path_of("SLPlugin.app/Contents/Resources")
for libfile in ("libllcommon.dylib",
"libapr-1.0.dylib",
"libaprutil-1.0.dylib",
"libexpat.1.5.2.dylib",
Andrew A. de Laix
committed
"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)}
)
Monroe Linden
committed
self.run_command("ln -sf %(target)r %(link)r" %
{'target': target_lib,
'link' : os.path.join(slplugin_res_path, libfile)}
)
Monroe Williams
committed
# 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")
Andrew A. de Laix
committed
self.path("../packages/lib/release/libllqtwebkit.dylib", "libllqtwebkit.dylib")
Bryan O'Sullivan
committed
# 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' %
Bryan O'Sullivan
committed
{ 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')})
def copy_finish(self):
# Force executable permissions to be set for scripts
# see CHOP-223 and http://mercurial.selenic.com/bts/issue1802
for script in 'Contents/MacOS/update_install',:
self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script))
Bryan O'Sullivan
committed
def package_finish(self):
channel_standin = 'Second Life Viewer 2' # hah, our default channel is not usable on its own
Bryan O'Sullivan
committed
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
Bryan O'Sullivan
committed
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' % {
Bryan O'Sullivan
committed
'sparse':sparsename,
Bryan O'Sullivan
committed
# 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()
Bryan O'Sullivan
committed
brad kittenbrink
committed
if devfile != '/dev/disk1':
# adding more debugging info based upon nat's hunches to the
# logs to help track down 'SetFile -a V' failures -brad
print "WARNING: 'SetFile -a V' command below is probably gonna fail"
# Copy everything in to the mounted .dmg
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
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)
Bryan O'Sullivan
committed
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})
Bryan O'Sullivan
committed
# get rid of the temp file
self.package_file = finalname
self.remove(sparsename)
class LinuxManifest(ViewerManifest):
Bryan O'Sullivan
committed
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")
Bryan O'Sullivan
committed
self.path("wrapper.sh","secondlife")
Adam Moss
committed
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")
Bryan O'Sullivan
committed
self.end_prefix("linux_tools")
# Create an appropriate gridargs.dat for this package, denoting required grid.
Adam Moss
committed
self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
Bryan O'Sullivan
committed
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")
self.path("../viewer_components/updater/scripts/linux/update_install", "bin/update_install")
# 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")
Bryan O'Sullivan
committed
def copy_finish(self):
# Force executable permissions to be set for scripts
# see CHOP-223 and http://mercurial.selenic.com/bts/issue1802
for script in 'secondlife', 'bin/update_install':
self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script))
Bryan O'Sullivan
committed
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"
Mark Palange (Mani)
committed
self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | 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() })
Bryan O'Sullivan
committed
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)})
else:
print "Skipping %s.tar.bz2 for non-Release build (%s)" % \
(installer_name, self.args['buildtype'])
Bryan O'Sullivan
committed
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):
Bryan O'Sullivan
committed
def construct(self):
super(Linux_i686Manifest, self).construct()
if self.prefix("../packages/lib/release", 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")
Bryan O'Sullivan
committed
self.path("libdb-4.2.so")
self.path("libcrypto.so.0.9.7")
Bryan O'Sullivan
committed
self.path("libssl.so.0.9.7")
self.path("libuuid.so.1")
Bryan O'Sullivan
committed
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")
Tofu Linden
committed
self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
try:
self.path("libfmod-3.75.so")
pass
except:
print "Skipping libfmod-3.75.so - not found"
Bryan O'Sullivan
committed
self.end_prefix("lib")
# Vivox runtimes
if self.prefix(src="../packages/lib/release", dst="bin"):
Bryan O'Sullivan
committed
self.path("SLVoice")
self.end_prefix()
if self.prefix(src="../packages/lib/release", dst="lib"):
Bryan O'Sullivan
committed
self.path("libortp.so")
#self.path("libvivoxoal.so.1") # no - we'll re-use the viewer's own OpenAL lib
Bryan O'Sullivan
committed
self.path("libvivoxsdk.so")
Bryan O'Sullivan
committed
self.end_prefix("lib")
Techwolf Lupindo
committed
class Linux_x86_64Manifest(LinuxManifest):
def construct(self):
super(Linux_x86_64Manifest, self).construct()
# support file for valgrind debug tool
self.path("secondlife-i686.supp")
################################################################
if __name__ == "__main__":