viewer_manifest.py
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:45k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. #!/usr/bin/python
  2. # @file viewer_manifest.py
  3. # @author Ryan Williams
  4. # @brief Description of all installer viewer files, and methods for packaging
  5. #        them into installers for all supported platforms.
  6. #
  7. # $LicenseInfo:firstyear=2006&license=viewergpl$
  8. # Copyright (c) 2006-2010, Linden Research, Inc.
  9. # Second Life Viewer Source Code
  10. # The source code in this file ("Source Code") is provided by Linden Lab
  11. # to you under the terms of the GNU General Public License, version 2.0
  12. # ("GPL"), unless you have obtained a separate licensing agreement
  13. # ("Other License"), formally executed by you and Linden Lab.  Terms of
  14. # the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. # online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. # There are special exceptions to the terms and conditions of the GPL as
  17. # it is applied to this Source Code. View the full text of the exception
  18. # in the file doc/FLOSS-exception.txt in this software distribution, or
  19. # online at
  20. # http://secondlifegrid.net/programs/open_source/licensing/flossexception
  21. # By copying, modifying or distributing this software, you acknowledge
  22. # that you have read and understood your obligations described above,
  23. # and agree to abide by those obligations.
  24. # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  25. # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  26. # COMPLETENESS OR PERFORMANCE.
  27. # $/LicenseInfo$
  28. import sys
  29. import os.path
  30. import re
  31. import tarfile
  32. viewer_dir = os.path.dirname(__file__)
  33. # add llmanifest library to our path so we don't have to muck with PYTHONPATH
  34. sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util'))
  35. from llmanifest import LLManifest, main, proper_windows_path, path_ancestors
  36. class ViewerManifest(LLManifest):
  37.     def is_packaging_viewer(self):
  38.         # This is overridden by the WindowsManifest sub-class,
  39.         # which has different behavior if it is not packaging the viewer.
  40.         return True
  41.     
  42.     def construct(self):
  43.         super(ViewerManifest, self).construct()
  44.         self.exclude("*.svn*")
  45.         self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg")
  46.         self.path(src="../../etc/message.xml", dst="app_settings/message.xml")
  47.         if self.is_packaging_viewer():
  48.             if self.prefix(src="app_settings"):
  49.                 self.exclude("logcontrol.xml")
  50.                 self.exclude("logcontrol-dev.xml")
  51.                 self.path("*.pem")
  52.                 self.path("*.ini")
  53.                 self.path("*.xml")
  54.                 self.path("*.db2")
  55.                 # include the entire shaders directory recursively
  56.                 self.path("shaders")
  57.                 # ... and the entire windlight directory
  58.                 self.path("windlight")
  59.                 self.end_prefix("app_settings")
  60.             if self.prefix(src="character"):
  61.                 self.path("*.llm")
  62.                 self.path("*.xml")
  63.                 self.path("*.tga")
  64.                 self.end_prefix("character")
  65.             # Include our fonts
  66.             if self.prefix(src="fonts"):
  67.                 self.path("*.ttf")
  68.                 self.path("*.txt")
  69.                 self.end_prefix("fonts")
  70.             # skins
  71.             if self.prefix(src="skins"):
  72.                     self.path("paths.xml")
  73.                     # include the entire textures directory recursively
  74.                     if self.prefix(src="*/textures"):
  75.                             self.path("*/*.tga")
  76.                             self.path("*/*.j2c")
  77.                             self.path("*/*.jpg")
  78.                             self.path("*/*.png")
  79.                             self.path("*.tga")
  80.                             self.path("*.j2c")
  81.                             self.path("*.jpg")
  82.                             self.path("*.png")
  83.                             self.path("textures.xml")
  84.                             self.end_prefix("*/textures")
  85.                     self.path("*/xui/*/*.xml")
  86.                     self.path("*/xui/*/widgets/*.xml")
  87.                     self.path("*/*.xml")
  88.                     # Local HTML files (e.g. loading screen)
  89.                     if self.prefix(src="*/html"):
  90.                             self.path("*.png")
  91.                             self.path("*/*/*.html")
  92.                             self.path("*/*/*.gif")
  93.                             self.end_prefix("*/html")
  94.                     self.end_prefix("skins")
  95.             # Files in the newview/ directory
  96.             self.path("gpu_table.txt")
  97.     def login_channel(self):
  98.         """Channel reported for login and upgrade purposes ONLY;
  99.         used for A/B testing"""
  100.         # NOTE: Do not return the normal channel if login_channel
  101.         # is not specified, as some code may branch depending on
  102.         # whether or not this is present
  103.         return self.args.get('login_channel')
  104.     def buildtype(self):
  105.         return self.args['buildtype']
  106.     def grid(self):
  107.         return self.args['grid']
  108.     def channel(self):
  109.         return self.args['channel']
  110.     def channel_unique(self):
  111.         return self.channel().replace("Second Life", "").strip()
  112.     def channel_oneword(self):
  113.         return "".join(self.channel_unique().split())
  114.     def channel_lowerword(self):
  115.         return self.channel_oneword().lower()
  116.     def viewer_branding_id(self):
  117.         return self.args['branding_id']
  118.     def installer_prefix(self):
  119.         mapping={"secondlife":'SecondLife_',
  120.                  "snowglobe":'Snowglobe_'}
  121.         return mapping[self.viewer_branding_id()]
  122.     def flags_list(self):
  123.         """ Convenience function that returns the command-line flags
  124.         for the grid"""
  125.         # Set command line flags relating to the target grid
  126.         grid_flags = ''
  127.         if not self.default_grid():
  128.             grid_flags = "--grid %(grid)s "
  129.                          "--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %
  130.                            {'grid':self.grid()}
  131.         # set command line flags for channel
  132.         channel_flags = ''
  133.         if self.login_channel() and self.login_channel() != self.channel():
  134.             # Report a special channel during login, but use default
  135.             channel_flags = '--channel "%s"' % (self.login_channel())
  136.         elif not self.default_channel():
  137.             channel_flags = '--channel "%s"' % self.channel()
  138.         # Deal with settings 
  139.         setting_flags = ''
  140.         if not self.default_channel() or not self.default_grid():
  141.             if self.default_grid():
  142.                 setting_flags = '--settings settings_%s.xml'
  143.                                 % self.channel_lowerword()
  144.             else:
  145.                 setting_flags = '--settings settings_%s_%s.xml'
  146.                                 % (self.grid(), self.channel_lowerword())
  147.                                                 
  148.         return " ".join((channel_flags, grid_flags, setting_flags)).strip()
  149. class WindowsManifest(ViewerManifest):
  150.     def final_exe(self):
  151.         if self.default_channel() and self.viewer_branding_id()=="secondlife":
  152.             if self.default_grid():
  153.                 return "SecondLife.exe"
  154.             else:
  155.                 return "SecondLifePreview.exe"
  156.         elif(self.viewer_branding_id=="snowglobe"):
  157.             return "Snowglobe.exe"
  158.         else:
  159.             return ''.join(self.channel().split()) + '.exe'
  160.     def is_packaging_viewer(self):
  161.         # Some commands, files will only be included
  162.         # if we are packaging the viewer on windows.
  163.         # This manifest is also used to copy
  164.         # files during the build.
  165.         return 'package' in self.args['actions']
  166.     def test_msvcrt_and_copy_action(self, src, dst):
  167.         # This is used to test a dll manifest.
  168.         # It is used as a temporary override during the construct method
  169.         from test_win32_manifest import test_assembly_binding
  170.         if src and (os.path.exists(src) or os.path.islink(src)):
  171.             # ensure that destination path exists
  172.             self.cmakedirs(os.path.dirname(dst))
  173.             self.created_paths.append(dst)
  174.             if not os.path.isdir(src):
  175.                 if(self.args['configuration'].lower() == 'debug'):
  176.                     test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "8.0.50727.4053")
  177.                 else:
  178.                     test_assembly_binding(src, "Microsoft.VC80.CRT", "8.0.50727.4053")
  179.                 self.ccopy(src,dst)
  180.             else:
  181.                 raise Exception("Directories are not supported by test_CRT_and_copy_action()")
  182.         else:
  183.             print "Doesn't exist:", src
  184.     def test_for_no_msvcrt_manifest_and_copy_action(self, src, dst):
  185.         # This is used to test that no manifest for the msvcrt exists.
  186.         # It is used as a temporary override during the construct method
  187.         from test_win32_manifest import test_assembly_binding
  188.         from test_win32_manifest import NoManifestException, NoMatchingAssemblyException
  189.         if src and (os.path.exists(src) or os.path.islink(src)):
  190.             # ensure that destination path exists
  191.             self.cmakedirs(os.path.dirname(dst))
  192.             self.created_paths.append(dst)
  193.             if not os.path.isdir(src):
  194.                 try:
  195.                     if(self.args['configuration'].lower() == 'debug'):
  196.                         test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "")
  197.                     else:
  198.                         test_assembly_binding(src, "Microsoft.VC80.CRT", "")
  199.                     raise Exception("Unknown condition")
  200.                 except NoManifestException, err:
  201.                     pass
  202.                 except NoMatchingAssemblyException, err:
  203.                     pass
  204.                     
  205.                 self.ccopy(src,dst)
  206.             else:
  207.                 raise Exception("Directories are not supported by test_CRT_and_copy_action()")
  208.         else:
  209.             print "Doesn't exist:", src
  210.         
  211.     def enable_crt_manifest_check(self):
  212.         if self.is_packaging_viewer():
  213.            WindowsManifest.copy_action = WindowsManifest.test_msvcrt_and_copy_action
  214.     def enable_no_crt_manifest_check(self):
  215.         if self.is_packaging_viewer():
  216.             WindowsManifest.copy_action = WindowsManifest.test_for_no_msvcrt_manifest_and_copy_action
  217.     def disable_manifest_check(self):
  218.         if self.is_packaging_viewer():
  219.             del WindowsManifest.copy_action
  220.     def construct(self):
  221.         super(WindowsManifest, self).construct()
  222.         self.enable_crt_manifest_check()
  223.         if self.is_packaging_viewer():
  224.             # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe.
  225.             self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe())
  226.         # Plugin host application
  227.         self.path(os.path.join(os.pardir,
  228.                                'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"),
  229.                   "slplugin.exe")
  230.         
  231.         self.disable_manifest_check()
  232.         # Get shared libs from the shared libs staging directory
  233.         if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
  234.                        dst=""):
  235.             self.enable_crt_manifest_check()
  236.             
  237.             # Get kdu dll, continue if missing.
  238.             try:
  239.                 self.path('llkdu.dll', dst='llkdu.dll')
  240.             except RuntimeError:
  241.                 print "Skipping llkdu.dll"
  242.             # Get llcommon and deps. If missing assume static linkage and continue.
  243.             try:
  244.                 self.path('llcommon.dll')
  245.                 self.path('libapr-1.dll')
  246.                 self.path('libaprutil-1.dll')
  247.                 self.path('libapriconv-1.dll')
  248.             except RuntimeError, err:
  249.                 print err.message
  250.                 print "Skipping llcommon.dll (assuming llcommon was linked statically)"
  251.             self.disable_manifest_check()
  252.             # For textures
  253.             if self.args['configuration'].lower() == 'debug':
  254.                 self.path("openjpegd.dll")
  255.             else:
  256.                 self.path("openjpeg.dll")
  257.             # These need to be installed as a SxS assembly, currently a 'private' assembly.
  258.             # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx
  259.             if self.args['configuration'].lower() == 'debug':
  260.                 self.path("msvcr80d.dll")
  261.                 self.path("msvcp80d.dll")
  262.                 self.path("Microsoft.VC80.DebugCRT.manifest")
  263.             else:
  264.                 self.path("msvcr80.dll")
  265.                 self.path("msvcp80.dll")
  266.                 self.path("Microsoft.VC80.CRT.manifest")
  267.             # Vivox runtimes
  268.             self.path("SLVoice.exe")
  269.             self.path("vivoxsdk.dll")
  270.             self.path("ortp.dll")
  271.             self.path("libsndfile-1.dll")
  272.             self.path("zlib1.dll")
  273.             self.path("vivoxplatform.dll")
  274.             self.path("vivoxoal.dll")
  275.             # For google-perftools tcmalloc allocator.
  276.             try:
  277.                 if self.args['configuration'].lower() == 'debug':
  278.                     self.path('libtcmalloc_minimal-debug.dll')
  279.                 else:
  280.                     self.path('libtcmalloc_minimal.dll')
  281.             except:
  282.                 print "Skipping libtcmalloc_minimal.dll"
  283.             self.end_prefix()
  284.         self.path(src="licenses-win32.txt", dst="licenses.txt")
  285.         self.path("featuretable.txt")
  286.         # For use in crash reporting (generates minidumps)
  287.         self.path("dbghelp.dll")
  288.         # For using FMOD for sound... DJS
  289.         self.path("fmod.dll")
  290.         self.enable_no_crt_manifest_check()
  291.         
  292.         # Media plugins - QuickTime
  293.         if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"):
  294.             self.path("media_plugin_quicktime.dll")
  295.             self.end_prefix()
  296.         # Media plugins - WebKit/Qt
  297.         if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"):
  298.             self.path("media_plugin_webkit.dll")
  299.             self.end_prefix()
  300.         if self.args['configuration'].lower() == 'debug':
  301.             if self.prefix(src=os.path.join(os.pardir, os.pardir, 'libraries', 'i686-win32', 'lib', 'debug'),
  302.                            dst="llplugin"):
  303.                 self.path("libeay32.dll")
  304.                 self.path("qtcored4.dll")
  305.                 self.path("qtguid4.dll")
  306.                 self.path("qtnetworkd4.dll")
  307.                 self.path("qtopengld4.dll")
  308.                 self.path("qtwebkitd4.dll")
  309.                 self.path("qtxmlpatternsd4.dll")
  310.                 self.path("ssleay32.dll")
  311.                 # For WebKit/Qt plugin runtimes (image format plugins)
  312.                 if self.prefix(src="imageformats", dst="imageformats"):
  313.                     self.path("qgifd4.dll")
  314.                     self.path("qicod4.dll")
  315.                     self.path("qjpegd4.dll")
  316.                     self.path("qmngd4.dll")
  317.                     self.path("qsvgd4.dll")
  318.                     self.path("qtiffd4.dll")
  319.                     self.end_prefix()
  320.                 # For WebKit/Qt plugin runtimes (codec/character encoding plugins)
  321.                 if self.prefix(src="codecs", dst="codecs"):
  322.                     self.path("qcncodecsd4.dll")
  323.                     self.path("qjpcodecsd4.dll")
  324.                     self.path("qkrcodecsd4.dll")
  325.                     self.path("qtwcodecsd4.dll")
  326.                     self.end_prefix()
  327.                 self.end_prefix()
  328.         else:
  329.             if self.prefix(src=os.path.join(os.pardir, os.pardir, 'libraries', 'i686-win32', 'lib', 'release'),
  330.                            dst="llplugin"):
  331.                 self.path("libeay32.dll")
  332.                 self.path("qtcore4.dll")
  333.                 self.path("qtgui4.dll")
  334.                 self.path("qtnetwork4.dll")
  335.                 self.path("qtopengl4.dll")
  336.                 self.path("qtwebkit4.dll")
  337.                 self.path("qtxmlpatterns4.dll")
  338.                 self.path("ssleay32.dll")
  339.                 # For WebKit/Qt plugin runtimes (image format plugins)
  340.                 if self.prefix(src="imageformats", dst="imageformats"):
  341.                     self.path("qgif4.dll")
  342.                     self.path("qico4.dll")
  343.                     self.path("qjpeg4.dll")
  344.                     self.path("qmng4.dll")
  345.                     self.path("qsvg4.dll")
  346.                     self.path("qtiff4.dll")
  347.                     self.end_prefix()
  348.                 # For WebKit/Qt plugin runtimes (codec/character encoding plugins)
  349.                 if self.prefix(src="codecs", dst="codecs"):
  350.                     self.path("qcncodecs4.dll")
  351.                     self.path("qjpcodecs4.dll")
  352.                     self.path("qkrcodecs4.dll")
  353.                     self.path("qtwcodecs4.dll")
  354.                     self.end_prefix()
  355.                 self.end_prefix()
  356.         self.disable_manifest_check()
  357.         # pull in the crash logger and updater from other projects
  358.         # tag:"crash-logger" here as a cue to the exporter
  359.         self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'],
  360.                   dst="win_crash_logger.exe")
  361.         self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'],
  362.                   dst="updater.exe")
  363.         if not self.is_packaging_viewer():
  364.             self.package_file = "copied_deps"    
  365.     def nsi_file_commands(self, install=True):
  366.         def wpath(path):
  367.             if path.endswith('/') or path.endswith(os.path.sep):
  368.                 path = path[:-1]
  369.             path = path.replace('/', '\')
  370.             return path
  371.         result = ""
  372.         dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])]
  373.         # sort deepest hierarchy first
  374.         dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
  375.         dest_files.reverse()
  376.         out_path = None
  377.         for pkg_file in dest_files:
  378.             rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,''))
  379.             installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file)))
  380.             pkg_file = wpath(os.path.normpath(pkg_file))
  381.             if installed_dir != out_path:
  382.                 if install:
  383.                     out_path = installed_dir
  384.                     result += 'SetOutPath ' + out_path + 'n'
  385.             if install:
  386.                 result += 'File ' + pkg_file + 'n'
  387.             else:
  388.                 result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + 'n'
  389.         # at the end of a delete, just rmdir all the directories
  390.         if not install:
  391.             deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list]
  392.             # find all ancestors so that we don't skip any dirs that happened to have no non-dir children
  393.             deleted_dirs = []
  394.             for d in deleted_file_dirs:
  395.                 deleted_dirs.extend(path_ancestors(d))
  396.             # sort deepest hierarchy first
  397.             deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
  398.             deleted_dirs.reverse()
  399.             prev = None
  400.             for d in deleted_dirs:
  401.                 if d != prev:   # skip duplicates
  402.                     result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + 'n'
  403.                 prev = d
  404.         return result
  405.     def package_finish(self):
  406.         # a standard map of strings for replacing in the templates
  407.         substitution_strings = {
  408.             'version' : '.'.join(self.args['version']),
  409.             'version_short' : '.'.join(self.args['version'][:-1]),
  410.             'version_dashes' : '-'.join(self.args['version']),
  411.             'final_exe' : self.final_exe(),
  412.             'grid':self.args['grid'],
  413.             'grid_caps':self.args['grid'].upper(),
  414.             # escape quotes becase NSIS doesn't handle them well
  415.             'flags':self.flags_list().replace('"', '$\"'),
  416.             'channel':self.channel(),
  417.             'channel_oneword':self.channel_oneword(),
  418.             'channel_unique':self.channel_unique(),
  419.             }
  420.         version_vars = """
  421.         !define INSTEXE  "%(final_exe)s"
  422.         !define VERSION "%(version_short)s"
  423.         !define VERSION_LONG "%(version)s"
  424.         !define VERSION_DASHES "%(version_dashes)s"
  425.         """ % substitution_strings
  426.         if self.default_channel() and self.viewer_branding_id()=="secondlife":
  427.             if self.default_grid():
  428.                 # release viewer
  429.                 installer_file = "Second_Life_%(version_dashes)s_Setup.exe"
  430.                 grid_vars_template = """
  431.                 OutFile "%(installer_file)s"
  432.                 !define VIEWERNAME "Second Life"
  433.                 !define INSTFLAGS "%(flags)s"
  434.                 !define INSTNAME   "SecondLife"
  435.                 !define SHORTCUT   "Second Life"
  436.                 !define URLNAME   "secondlife"
  437.                 !define INSTALL_ICON "install_icon.ico"
  438.                 !define UNINSTALL_ICON "uninstall_icon.ico"
  439.                 Caption "Second Life ${VERSION}"
  440.                 """
  441.             else:
  442.                 # beta grid viewer
  443.                 installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe"
  444.                 grid_vars_template = """
  445.                 OutFile "%(installer_file)s"
  446.                 !define VIEWERNAME "Second Life"
  447.                 !define INSTFLAGS "%(flags)s"
  448.                 !define INSTNAME   "SecondLife%(grid_caps)s"
  449.                 !define SHORTCUT   "Second Life (%(grid_caps)s)"
  450.                 !define URLNAME   "secondlife%(grid)s"
  451.                 !define INSTALL_ICON "install_icon.ico"
  452.                 !define UNINSTALL_ICON "uninstall_icon.ico"
  453.                 !define UNINSTALL_SETTINGS 1
  454.                 Caption "Second Life %(grid)s ${VERSION}"
  455.                 """
  456.         elif self.viewer_branding_id()=="snowglobe":
  457.                 installer_file = "Snowglobe_%(version_dashes)s_Setup.exe"
  458.                 grid_vars_template = """
  459.                 OutFile "%(installer_file)s"
  460.                 !define VIEWERNAME "Snowglobe"
  461.                 !define INSTFLAGS "%(flags)s"
  462.                 !define INSTNAME   "Snowglobe"
  463.                 !define SHORTCUT   "Snowglobe"
  464.                 !define URLNAME   "secondlife"
  465.                 !define INSTALL_ICON "install_icon_snowglobe.ico"
  466.                 !define UNINSTALL_ICON "uninstall_icon_snowglobe.ico"
  467.                 Caption "Snowglobe ${VERSION}"
  468.                 """
  469.         else:
  470.             # some other channel on some grid
  471.             installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe"
  472.             grid_vars_template = """
  473.             OutFile "%(installer_file)s"
  474.             !define INSTFLAGS "%(flags)s"
  475.             !define INSTNAME   "SecondLife%(channel_oneword)s"
  476.             !define SHORTCUT   "%(channel)s"
  477.             !define URLNAME   "secondlife"
  478.             !define UNINSTALL_SETTINGS 1
  479.             Caption "%(channel)s ${VERSION}"
  480.             """
  481.         if 'installer_name' in self.args:
  482.             installer_file = self.args['installer_name']
  483.         else:
  484.             installer_file = installer_file % substitution_strings
  485.         substitution_strings['installer_file'] = installer_file
  486.         tempfile = "secondlife_setup_tmp.nsi"
  487.         # the following replaces strings in the nsi template
  488.         # it also does python-style % substitution
  489.         self.replace_in("installers/windows/installer_template.nsi", tempfile, {
  490.                 "%%VERSION%%":version_vars,
  491.                 "%%SOURCE%%":self.get_src_prefix(),
  492.                 "%%GRID_VARS%%":grid_vars_template % substitution_strings,
  493.                 "%%INSTALL_FILES%%":self.nsi_file_commands(True),
  494.                 "%%DELETE_FILES%%":self.nsi_file_commands(False)})
  495.         # We use the Unicode version of NSIS, available from
  496.         # http://www.scratchpaper.com/
  497.         # Check two paths, one for Program Files, and one for Program Files (x86).
  498.         # Yay 64bit windows.
  499.         NSIS_path = os.path.expandvars('${ProgramFiles}\NSIS\Unicode\makensis.exe')
  500.         if not os.path.exists(NSIS_path):
  501.             NSIS_path = os.path.expandvars('${ProgramFiles(x86)}\NSIS\Unicode\makensis.exe')
  502.         self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile))
  503.         # self.remove(self.dst_path_of(tempfile))
  504.         # If we're on a build machine, sign the code using our Authenticode certificate. JC
  505.         sign_py = os.path.expandvars("${SIGN}")
  506.         if not sign_py or sign_py == "${SIGN}":
  507.             sign_py = 'C:\buildscripts\code-signing\sign.py'
  508.         else:
  509.             sign_py = sign_py.replace('\', '\\\\')
  510.         python = os.path.expandvars("${PYTHON}")
  511.         if not python or python == "${PYTHON}":
  512.             python = 'python'
  513.         if os.path.exists(sign_py):
  514.             self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\', '\\\\')))
  515.         else:
  516.             print "Skipping code signing,", sign_py, "does not exist"
  517.         self.created_path(self.dst_path_of(installer_file))
  518.         self.package_file = installer_file
  519. class DarwinManifest(ViewerManifest):
  520.     def construct(self):
  521.         # copy over the build result (this is a no-op if run within the xcode script)
  522.         self.path(self.args['configuration'] + "/" + self.app_name() + ".app", dst="")
  523.         if self.prefix(src="", dst="Contents"):  # everything goes in Contents
  524.             self.path(self.info_plist_name(), dst="Info.plist")
  525.             # copy additional libs in <bundle>/Contents/MacOS/
  526.             self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib")
  527.             # most everything goes in the Resources directory
  528.             if self.prefix(src="", dst="Resources"):
  529.                 super(DarwinManifest, self).construct()
  530.                 if self.prefix("cursors_mac"):
  531.                     self.path("*.tif")
  532.                     self.end_prefix("cursors_mac")
  533.                 self.path("licenses-darwin.txt", dst="licenses.txt")
  534.                 self.path("featuretable_mac.txt")
  535.                 self.path("SecondLife.nib")
  536.                 if self.viewer_branding_id()=='secondlife':
  537.                     # If we are not using the default channel, use the 'Firstlook
  538.                     # icon' to show that it isn't a stable release.
  539.                     if self.default_channel() and self.default_grid():
  540.                         self.path("secondlife.icns")
  541.                     else:
  542.                         self.path("secondlife_firstlook.icns", "secondlife.icns")
  543.                 elif self.viewer_branding_id()=="snowglobe":
  544.                     self.path("snowglobe.icns")
  545.                 self.path("SecondLife.nib")
  546.                 
  547.                 # Translations
  548.                 self.path("English.lproj")
  549.                 self.path("German.lproj")
  550.                 self.path("Japanese.lproj")
  551.                 self.path("Korean.lproj")
  552.                 self.path("da.lproj")
  553.                 self.path("es.lproj")
  554.                 self.path("fr.lproj")
  555.                 self.path("hu.lproj")
  556.                 self.path("it.lproj")
  557.                 self.path("nl.lproj")
  558.                 self.path("pl.lproj")
  559.                 self.path("pt.lproj")
  560.                 self.path("ru.lproj")
  561.                 self.path("tr.lproj")
  562.                 self.path("uk.lproj")
  563.                 self.path("zh-Hans.lproj")
  564.                 # SLVoice and vivox lols
  565.                 self.path("vivox-runtime/universal-darwin/libsndfile.dylib", "libsndfile.dylib")
  566.                 self.path("vivox-runtime/universal-darwin/libvivoxoal.dylib", "libvivoxoal.dylib")
  567.                 self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib")
  568.                 self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")
  569.                 self.path("vivox-runtime/universal-darwin/libvivoxplatform.dylib", "libvivoxplatform.dylib")
  570.                 self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice")
  571.                 libdir = "../../libraries/universal-darwin/lib_release"
  572.                 dylibs = {}
  573.                 # need to get the kdu dll from any of the build directories as well
  574.                 for lib in "llkdu", "llcommon":
  575.                     libfile = "lib%s.dylib" % lib
  576.                     try:
  577.                         self.path(self.find_existing_file(os.path.join(os.pardir,
  578.                                                                        lib,
  579.                                                                        self.args['configuration'],
  580.                                                                        libfile),
  581.                                                           os.path.join(libdir, libfile)),
  582.                                   dst=libfile)
  583.                     except RuntimeError:
  584.                         print "Skipping %s" % libfile
  585.                         dylibs[lib] = False
  586.                     else:
  587.                         dylibs[lib] = True
  588.                 if dylibs["llcommon"]:
  589.                     for libfile in ("libapr-1.0.3.7.dylib",
  590.                                     "libaprutil-1.0.3.8.dylib",
  591.                                     "libexpat.0.5.0.dylib"):
  592.                         self.path(os.path.join(libdir, libfile), libfile)
  593.                 #libfmodwrapper.dylib
  594.                 self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib")
  595.                 
  596.                 # our apps
  597.                 self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app")
  598.                 self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app")
  599.                 # our apps dependencies on shared libs
  600.                 if dylibs["llcommon"]:
  601.                     mac_crash_logger_res_path = self.dst_path_of("mac-crash-logger.app/Contents/Resources")
  602.                     mac_updater_res_path = self.dst_path_of("mac-updater.app/Contents/Resources")
  603.                     for libfile in ("libllcommon.dylib",
  604.                                     "libapr-1.0.3.7.dylib",
  605.                                     "libaprutil-1.0.3.8.dylib",
  606.                                     "libexpat.0.5.0.dylib"):
  607.                         target_lib = os.path.join('../../..', libfile)
  608.                         self.run_command("ln -sf %(target)r %(link)r" % 
  609.                                          {'target': target_lib,
  610.                                           'link' : os.path.join(mac_crash_logger_res_path, libfile)}
  611.                                          )
  612.                         self.run_command("ln -sf %(target)r %(link)r" % 
  613.                                          {'target': target_lib,
  614.                                           'link' : os.path.join(mac_updater_res_path, libfile)}
  615.                                          )
  616.                 # plugin launcher
  617.                 self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin", "SLPlugin")
  618.                 # plugins
  619.                 if self.prefix(src="", dst="llplugin"):
  620.                     self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib")
  621.                     self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib")
  622.                     self.path("../../libraries/universal-darwin/lib_release/libllqtwebkit.dylib", "libllqtwebkit.dylib")
  623.                     self.end_prefix("llplugin")
  624.                 # command line arguments for connecting to the proper grid
  625.                 self.put_in_file(self.flags_list(), 'arguments.txt')
  626.                 self.end_prefix("Resources")
  627.             self.end_prefix("Contents")
  628.         # NOTE: the -S argument to strip causes it to keep enough info for
  629.         # annotated backtraces (i.e. function names in the crash log).  'strip' with no
  630.         # arguments yields a slightly smaller binary but makes crash logs mostly useless.
  631.         # This may be desirable for the final release.  Or not.
  632.         if self.buildtype().lower()=='release':
  633.             if ("package" in self.args['actions'] or 
  634.                 "unpacked" in self.args['actions']):
  635.                 self.run_command('strip -S "%(viewer_binary)s"' %
  636.                                  { 'viewer_binary' : self.dst_path_of('Contents/MacOS/'+self.app_name())})
  637.     def app_name(self):
  638.         mapping={"secondlife":"Second Life",
  639.                  "snowglobe":"Snowglobe"}
  640.         return mapping[self.viewer_branding_id()]
  641.         
  642.     def info_plist_name(self):
  643.         mapping={"secondlife":"Info-SecondLife.plist",
  644.                  "snowglobe":"Info-Snowglobe.plist"}
  645.         return mapping[self.viewer_branding_id()]
  646.     def package_finish(self):
  647.         channel_standin = self.app_name()
  648.         if not self.default_channel_for_brand():
  649.             channel_standin = self.channel()
  650.         imagename=self.installer_prefix() + '_'.join(self.args['version'])
  651.         # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning.
  652.         #  If we really need differently named volumes, we'll need to create multiple DS_Store file images, or use some other trick.
  653.         volname=self.app_name() + " Installer"  # DO NOT CHANGE without understanding comment above
  654.         if self.default_channel_for_brand():
  655.             if not self.default_grid():
  656.                 # beta case
  657.                 imagename = imagename + '_' + self.args['grid'].upper()
  658.         else:
  659.             # first look, etc
  660.             imagename = imagename + '_' + self.channel_oneword().upper()
  661.         sparsename = imagename + ".sparseimage"
  662.         finalname = imagename + ".dmg"
  663.         # make sure we don't have stale files laying about
  664.         self.remove(sparsename, finalname)
  665.         self.run_command('hdiutil create %(sparse)r -volname %(vol)r -fs HFS+ -type SPARSE -megabytes 700 -layout SPUD' % {
  666.                 'sparse':sparsename,
  667.                 'vol':volname})
  668.         # mount the image and get the name of the mount point and device node
  669.         hdi_output = self.run_command('hdiutil attach -private %r' % sparsename)
  670.         devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip()
  671.         volpath = re.search('HFSs+(.+)', hdi_output).group(1).strip()
  672.         # Copy everything in to the mounted .dmg
  673.         if self.default_channel_for_brand() and not self.default_grid():
  674.             app_name = self.app_name() + " " + self.args['grid']
  675.         else:
  676.             app_name = channel_standin.strip()
  677.         # Hack:
  678.         # Because there is no easy way to coerce the Finder into positioning
  679.         # the app bundle in the same place with different app names, we are
  680.         # adding multiple .DS_Store files to svn. There is one for release,
  681.         # one for release candidate and one for first look. Any other channels
  682.         # will use the release .DS_Store, and will look broken.
  683.         # - Ambroff 2008-08-20
  684.         # Added a .DS_Store for snowglobe - Merov 2009-06-17
  685.         # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different
  686.         if (self.app_name()=="Snowglobe"):
  687.             dmg_template = os.path.join ('installers', 'darwin', 'snowglobe-dmg')
  688.         else:
  689.             dmg_template = os.path.join(
  690.                 'installers', 
  691.                 'darwin',
  692.                 '%s-dmg' % "".join(self.channel_unique().split()).lower())
  693.         if not os.path.exists (self.src_path_of(dmg_template)):
  694.             dmg_template = os.path.join ('installers', 'darwin', 'release-dmg')
  695.         for s,d in {self.get_dst_prefix():app_name + ".app",
  696.                     os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns",
  697.                     os.path.join(dmg_template, "background.jpg"): "background.jpg",
  698.                     os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items():
  699.             print "Copying to dmg", s, d
  700.             self.copy_action(self.src_path_of(s), os.path.join(volpath, d))
  701.         # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit)
  702.         for f in ".VolumeIcon.icns", "background.jpg", ".DS_Store":
  703.             self.run_command('SetFile -a V %r' % os.path.join(volpath, f))
  704.         # Create the alias file (which is a resource file) from the .r
  705.         self.run_command('rez %r -o %r' %
  706.                          (self.src_path_of("installers/darwin/release-dmg/Applications-alias.r"),
  707.                           os.path.join(volpath, "Applications")))
  708.         # Set the alias file's alias and custom icon bits
  709.         self.run_command('SetFile -a AC %r' % os.path.join(volpath, "Applications"))
  710.         # Set the disk image root's custom icon bit
  711.         self.run_command('SetFile -a C %r' % volpath)
  712.         # Unmount the image
  713.         self.run_command('hdiutil detach -force %r' % devfile)
  714.         print "Converting temp disk image to final disk image"
  715.         self.run_command('hdiutil convert %(sparse)r -format UDZO -imagekey zlib-level=9 -o %(final)r' % {'sparse':sparsename, 'final':finalname})
  716.         # get rid of the temp file
  717.         self.package_file = finalname
  718.         self.remove(sparsename)
  719. class LinuxManifest(ViewerManifest):
  720.     def construct(self):
  721.         super(LinuxManifest, self).construct()
  722.         self.path("licenses-linux.txt","licenses.txt")
  723.         
  724.         self.path("res/"+self.icon_name(),self.icon_name())
  725.         if self.prefix("linux_tools", dst=""):
  726.             self.path("client-readme.txt","README-linux.txt")
  727.             self.path("client-readme-voice.txt","README-linux-voice.txt")
  728.             self.path("client-readme-joystick.txt","README-linux-joystick.txt")
  729.             self.path("wrapper.sh",self.wrapper_name())
  730.             self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh")
  731.             self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh")
  732.             self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh")
  733.             self.path("launch_url.sh","etc/launch_url.sh")
  734.             self.path("install.sh")
  735.             self.end_prefix("linux_tools")
  736.         # Create an appropriate gridargs.dat for this package, denoting required grid.
  737.         self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
  738.         if self.buildtype().lower()=='release':
  739.             self.path("secondlife-stripped","bin/"+self.binary_name())
  740.             self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
  741.         else:
  742.             self.path("secondlife-bin","bin/"+self.binary_name())
  743.             self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin")
  744.     def wrapper_name(self):
  745.         mapping={"secondlife":"secondlife",
  746.                  "snowglobe":"snowglobe"}
  747.         return mapping[self.viewer_branding_id()]
  748.     def binary_name(self):
  749.         mapping={"secondlife":"do-not-directly-run-secondlife-bin",
  750.                  "snowglobe":"snowglobe-do-not-run-directly"}
  751.         return mapping[self.viewer_branding_id()]
  752.     
  753.     def icon_name(self):
  754.         mapping={"secondlife":"secondlife_icon.png",
  755.                  "snowglobe":"snowglobe_icon.png"}
  756.         return mapping[self.viewer_branding_id()]
  757.     def package_finish(self):
  758.         if 'installer_name' in self.args:
  759.             installer_name = self.args['installer_name']
  760.         else:
  761.             installer_name_components = [self.installer_prefix(), self.args.get('arch')]
  762.             installer_name_components.extend(self.args['version'])
  763.             installer_name = "_".join(installer_name_components)
  764.             if self.default_channel():
  765.                 if not self.default_grid():
  766.                     installer_name += '_' + self.args['grid'].upper()
  767.             else:
  768.                 installer_name += '_' + self.channel_oneword().upper()
  769.         # Fix access permissions
  770.         self.run_command("""
  771.                 find %(dst)s -type d | xargs --no-run-if-empty chmod 755;
  772.                 find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755;
  773.                 find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555;
  774.                 find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644;
  775.                 find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444;
  776.                 true""" %  {'dst':self.get_dst_prefix() })
  777.         self.package_file = installer_name + '.tar.bz2'
  778.         # temporarily move directory tree so that it has the right
  779.         # name in the tarfile
  780.         self.run_command("mv %(dst)s %(inst)s" % {
  781.             'dst': self.get_dst_prefix(),
  782.             'inst': self.build_path_of(installer_name)})
  783.         try:
  784.             # only create tarball if it's a release build.
  785.             if self.args['buildtype'].lower() == 'release':
  786.                 # --numeric-owner hides the username of the builder for
  787.                 # security etc.
  788.                 self.run_command('tar -C %(dir)s --numeric-owner -cjf '
  789.                                  '%(inst_path)s.tar.bz2 %(inst_name)s' % {
  790.                         'dir': self.get_build_prefix(),
  791.                         'inst_name': installer_name,
  792.                         'inst_path':self.build_path_of(installer_name)})
  793.         finally:
  794.             self.run_command("mv %(inst)s %(dst)s" % {
  795.                 'dst': self.get_dst_prefix(),
  796.                 'inst': self.build_path_of(installer_name)})
  797. class Linux_i686Manifest(LinuxManifest):
  798.     def construct(self):
  799.         super(Linux_i686Manifest, self).construct()
  800.         # install either the libllkdu we just built, or a prebuilt one, in
  801.         # decreasing order of preference.  for linux package, this goes to bin/
  802.         for lib, destdir in ("llkdu", "bin"), ("llcommon", "lib"):
  803.             libfile = "lib%s.so" % lib
  804.             try:
  805.                 self.path(self.find_existing_file(os.path.join(os.pardir, lib, libfile),
  806.                     '../../libraries/i686-linux/lib_release_client/%s' % libfile), 
  807.                       dst=os.path.join(destdir, libfile))
  808.                 # keep this one to preserve syntax, open source mangling removes previous lines
  809.                 pass
  810.             except RuntimeError:
  811.                 print "Skipping %s - not found" % libfile
  812.                 pass
  813.         self.path("../linux_crash_logger/linux-crash-logger","bin/linux-crash-logger.bin")
  814.         self.path("../linux_updater/linux-updater", "bin/linux-updater.bin")
  815.         self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin")
  816.         if self.prefix("res-sdl"):
  817.             self.path("*")
  818.             # recurse
  819.             self.end_prefix("res-sdl")
  820.         # plugins
  821.         if self.prefix(src="", dst="bin/llplugin"):
  822.             self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so")
  823.             self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so")
  824.             self.end_prefix("bin/llplugin")
  825.         self.path("featuretable_linux.txt")
  826.         #self.path("secondlife-i686.supp")
  827.         if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
  828.             self.path("libapr-1.so.0")
  829.             self.path("libaprutil-1.so.0")
  830.             self.path("libdb-4.2.so")
  831.             self.path("libcrypto.so.0.9.7")
  832.             self.path("libexpat.so.1")
  833.             self.path("libssl.so.0.9.7")
  834.             self.path("libuuid.so.1")
  835.             self.path("libSDL-1.2.so.0")
  836.             self.path("libELFIO.so")
  837.             self.path("libopenjpeg.so.1.3.0", "libopenjpeg.so.1.3")
  838.             self.path("libalut.so")
  839.             self.path("libopenal.so", "libopenal.so.1")
  840.             self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
  841.             try:
  842.                     self.path("libkdu_v42R.so", "libkdu.so")
  843.                     pass
  844.             except:
  845.                     print "Skipping libkdu_v42R.so - not found"
  846.                     pass
  847.             try:
  848.                     self.path("libfmod-3.75.so")
  849.                     pass
  850.             except:
  851.                     print "Skipping libfmod-3.75.so - not found"
  852.                     pass
  853.             self.end_prefix("lib")
  854.             # Vivox runtimes
  855.             if self.prefix(src="vivox-runtime/i686-linux", dst="bin"):
  856.                     self.path("SLVoice")
  857.                     self.end_prefix()
  858.             if self.prefix(src="vivox-runtime/i686-linux", dst="lib"):
  859.                     self.path("libortp.so")
  860.                     self.path("libsndfile.so.1")
  861.                     #self.path("libvivoxoal.so.1") # no - we'll re-use the viewer's own OpenAL lib
  862.                     self.path("libvivoxsdk.so")
  863.                     self.path("libvivoxplatform.so")
  864.                     self.end_prefix("lib")
  865.         if self.args['buildtype'].lower() == 'release':
  866.             print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
  867.             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
  868. class Linux_x86_64Manifest(LinuxManifest):
  869.     def construct(self):
  870.         super(Linux_x86_64Manifest, self).construct()
  871.         # support file for valgrind debug tool
  872.         self.path("secondlife-i686.supp")
  873. ################################################################
  874. if __name__ == "__main__":
  875.     main()