summaryrefslogtreecommitdiff
path: root/indra/newview/viewer_manifest.py
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2017-12-04 17:15:47 -0500
committerNat Goodspeed <nat@lindenlab.com>2017-12-04 17:15:47 -0500
commit488d165895db3c2ae2aebbb3b727e95b8a3de6bf (patch)
tree50b40a79d9fda92d01b58f2b4de34e13a3eec845 /indra/newview/viewer_manifest.py
parent5f0cc13273e9302bccc4f823b482bf48fdbb0d90 (diff)
MAINT-7751: Rework DarwinManifest to produce new app bundle structure.
Specifically, Second Life.app is now mostly just a wrapper. Its Contents/ Resources contains nested Launcher.app (the VMP) and Viewer.app (the viewer itself). Most of what used to be in the top-level Second Life.app has been relocated to the embedded Viewer.app. VMP stuff has of course been extracted to Launcher.app. The top-level Second Life.app executable is now a tiny script that runs Launcher.app. This structure permits different icons and different Dock flyover text for the launcher and the viewer, hopefully ameliorating a certain amount of user confusion about the dual icons. This requires a corresponding VMP change: on macOS, the VMP must now find both its resources and the viewer executable by walking up from Launcher.app and down again into its sibling Viewer.app. Since Dock flyover text is determined by the embedded app names, allow Product to change these at will. That means we should be able to tweak exactly one variable assignment to change either of those embedded app names, without having to chase down other references scattered throughout the source repo. For that reason, create top-level trampoline SL_Launcher script dynamically: it must reference the launcher app by name. That means we must also perform (the equivalent of) chmod +x on that generated script. The one mystery surrounding this restructuring is that without a top-level Frameworks symlink pointing to the embedded Viewer.app's Frameworks directory (where CEF lives), CEF refuses to start: no splash screen, no MoP. Perhaps we can fix that someday. Use Python's bundled plistlib to generate Info.plist files for the embedded applications. Reorganize stray code stanzas to try to help the structure of the code more or less resemble the structure of the desired result. Add ViewerManifest.relpath() method to determine the relative path from a specified base to the target path. If base omitted, assumes get_dst_prefix() -- handy for creating symlinks. Determining exactly the right number of os.pardir instances to concatenate into the relative pathname for a symlink (or an install_name_tool stamp) was tedious, fragile and unobvious, difficult to desk-check. Using relpath() should make all that more robust. Migrate symlinkf() from free function to ViewerManifest method, refactoring into _symlinkf_prep_dst() and _symlinkf(), adding relsymlinkf(). This lets us add convenience features such as prepending get_dst_prefix() to the dest (the place where we want to create the symlink), defaulting dest to the basename of target and ensuring that the parent of that dest already exists -- as with LLManifest.path(). Moreover, since it makes no sense whatsoever to create an absolute symlink to some path on the build machine, relsymlinkf() creates every symlink relative to dirname(dest). That, in turn, lets us eliminate a certain amount of boilerplate around existing calls. (Also, since we now ensure the parent directory exists, scrap the logic to diagnose "nonexistent parent directory.") Make llmanifest.LLManifest.run_command() not pass shell=True to subprocess, thereby permitting (requiring) the list form rather than the string form. Change all existing calls to list form. This makes calls more readable, for two reasons. First, many of the arguments are taken from script variables; these can simply be dropped into the list instead of indirecting through string interpolation. Second, it eliminates the need to manually escape individual arguments, since subprocess promises to honor the distinction between list elements. Also fix LLManifest.put_in_file() to ensure the containing directory exists. Consolidate some viewer_manifest.py redundancy, e.g. copying the same set of ten DLLs from either of two directories depending on Release vs. Debug.
Diffstat (limited to 'indra/newview/viewer_manifest.py')
-rwxr-xr-xindra/newview/viewer_manifest.py945
1 files changed, 566 insertions, 379 deletions
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 8048f4eb2f..cb5503eee0 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -32,11 +32,12 @@ import os.path
import shutil
import errno
import json
+import random
import re
+import stat
+import subprocess
import tarfile
import time
-import random
-import subprocess
viewer_dir = os.path.dirname(__file__)
# Add indra/lib/python to our path so we don't have to muck with PYTHONPATH.
@@ -290,6 +291,129 @@ class ViewerManifest(LLManifest):
random.shuffle(names)
return ', '.join(names)
+ def relsymlinkf(self, src, dst=None, catch=True):
+ """
+ relsymlinkf() is just like symlinkf(), but instead of requiring the
+ caller to pass 'src' as a relative pathname, this method expects 'src'
+ to be absolute, and creates a symlink whose target is the relative
+ path from 'src' to dirname(dst).
+ """
+ dstdir, dst = self._symlinkf_prep_dst(src, dst)
+
+ # Determine the relative path starting from the directory containing
+ # dst to the intended src.
+ src = self.relpath(src, dstdir)
+
+ self._symlinkf(src, dst, catch)
+ return dst
+
+ def symlinkf(self, src, dst=None, catch=True):
+ """
+ Like ln -sf, but uses os.symlink() instead of running ln. This creates
+ a symlink at 'dst' that points to 'src' -- see:
+ https://docs.python.org/2/library/os.html#os.symlink
+
+ If you omit 'dst', this creates a symlink with basename(src) at
+ get_dst_prefix() -- in other words: put a symlink to this pathname
+ here at the current dst prefix.
+
+ 'src' must specifically be a *relative* symlink. It makes no sense to
+ create an absolute symlink pointing to some path on the build machine!
+
+ Also:
+ - We prepend 'dst' with the current get_dst_prefix(), so it has similar
+ meaning to associated self.path() calls.
+ - We ensure that the containing directory os.path.dirname(dst) exists
+ before attempting the symlink.
+
+ If you pass catch=False, exceptions will be propagated instead of
+ caught.
+ """
+ dstdir, dst = self._symlinkf_prep_dst(src, dst)
+ self._symlinkf(src, dst, catch)
+ return dst
+
+ def _symlinkf_prep_dst(self, src, dst):
+ # helper for relsymlinkf() and symlinkf()
+ if dst is None:
+ dst = os.path.basename(src)
+ dst = os.path.join(self.get_dst_prefix(), dst)
+ # Seems silly to prepend get_dst_prefix() to dst only to call
+ # os.path.dirname() on it again, but this works even when the passed
+ # 'dst' is itself a pathname.
+ dstdir = os.path.dirname(dst)
+ self.cmakedirs(dstdir)
+ return dstdir, dst
+
+ def _symlinkf(self, src, dst, catch):
+ # helper for relsymlinkf() and symlinkf()
+ # the passed src must be relative
+ if os.path.isabs(src):
+ raise ManifestError("Do not symlinkf(absolute %r, asis=True)" % src)
+
+ # The outer catch is the one that reports failure even after attempted
+ # recovery.
+ try:
+ # At the inner layer, recovery may be possible.
+ try:
+ os.symlink(src, dst)
+ except OSError as err:
+ if err.errno != errno.EEXIST:
+ raise
+ # We could just blithely attempt to remove and recreate the target
+ # file, but that strategy doesn't work so well if we don't have
+ # permissions to remove it. Check to see if it's already the
+ # symlink we want, which is the usual reason for EEXIST.
+ elif os.path.islink(dst):
+ if os.readlink(dst) == src:
+ # the requested link already exists
+ pass
+ else:
+ # dst is the wrong symlink; attempt to remove and recreate it
+ os.remove(dst)
+ os.symlink(src, dst)
+ elif os.path.isdir(dst):
+ print "Requested symlink (%s) exists but is a directory; replacing" % dst
+ shutil.rmtree(dst)
+ os.symlink(src, dst)
+ elif os.path.exists(dst):
+ print "Requested symlink (%s) exists but is a file; replacing" % dst
+ os.remove(dst)
+ os.symlink(src, dst)
+ else:
+ # out of ideas
+ raise
+ except Exception as err:
+ # report
+ print "Can't symlink %r -> %r: %s: %s" % \
+ (dst, src, err.__class__.__name__, err)
+ # if caller asked us not to catch, re-raise this exception
+ if not catch:
+ raise
+
+ def relpath(self, path, base=None, symlink=False):
+ """
+ Return the relative path from 'base' to the passed 'path'. If base is
+ omitted, self.get_dst_prefix() is assumed. In other words: make a
+ same-name symlink to this path right here in the current dest prefix.
+
+ Normally we resolve symlinks. To retain symlinks, pass symlink=True.
+ """
+ if base is None:
+ base = self.get_dst_prefix()
+
+ # Since we use os.path.relpath() for this, which is purely textual, we
+ # must ensure that both pathnames are absolute.
+ if symlink:
+ # symlink=True means: we know path is (or indirects through) a
+ # symlink, don't resolve, we want to use the symlink.
+ abspath = os.path.abspath
+ else:
+ # symlink=False means to resolve any symlinks we may find
+ abspath = os.path.realpath
+
+ return os.path.relpath(abspath(path), abspath(base))
+
class WindowsManifest(ViewerManifest):
# We want the platform, per se, for every Windows build to be 'win'. The
@@ -481,31 +605,19 @@ class WindowsManifest(ViewerManifest):
self.path("media_plugin_example.dll")
# CEF runtime files - debug
- if self.args['configuration'].lower() == 'debug':
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'bin', 'debug'), dst="llplugin"):
- self.path("chrome_elf.dll")
- self.path("d3dcompiler_43.dll")
- self.path("d3dcompiler_47.dll")
- self.path("libcef.dll")
- self.path("libEGL.dll")
- self.path("libGLESv2.dll")
- self.path("dullahan_host.exe")
- self.path("natives_blob.bin")
- self.path("snapshot_blob.bin")
- self.path("widevinecdmadapter.dll")
- else:
# CEF runtime files - not debug (release, relwithdebinfo etc.)
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'bin', 'release'), dst="llplugin"):
- self.path("chrome_elf.dll")
- self.path("d3dcompiler_43.dll")
- self.path("d3dcompiler_47.dll")
- self.path("libcef.dll")
- self.path("libEGL.dll")
- self.path("libGLESv2.dll")
- self.path("dullahan_host.exe")
- self.path("natives_blob.bin")
- self.path("snapshot_blob.bin")
- self.path("widevinecdmadapter.dll")
+ config = 'debug' if self.args['configuration'].lower() == 'debug' else 'release'
+ with self.prefix(src=os.path.join(pkgdir, 'bin', config), dst="llplugin"):
+ self.path("chrome_elf.dll")
+ self.path("d3dcompiler_43.dll")
+ self.path("d3dcompiler_47.dll")
+ self.path("libcef.dll")
+ self.path("libEGL.dll")
+ self.path("libGLESv2.dll")
+ self.path("dullahan_host.exe")
+ self.path("natives_blob.bin")
+ self.path("snapshot_blob.bin")
+ self.path("widevinecdmadapter.dll")
# MSVC DLLs needed for CEF and have to be in same directory as plugin
with self.prefix(src=os.path.join(os.pardir, 'sharedlibs', 'Release'), dst="llplugin"):
@@ -513,7 +625,7 @@ class WindowsManifest(ViewerManifest):
self.path("msvcr120.dll")
# CEF files common to all configurations
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'resources'), dst="llplugin"):
+ with self.prefix(src=os.path.join(pkgdir, 'resources'), dst="llplugin"):
self.path("cef.pak")
self.path("cef_100_percent.pak")
self.path("cef_200_percent.pak")
@@ -521,7 +633,7 @@ class WindowsManifest(ViewerManifest):
self.path("devtools_resources.pak")
self.path("icudtl.dat")
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'resources', 'locales'), dst=os.path.join('llplugin', 'locales')):
+ with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('llplugin', 'locales')):
self.path("am.pak")
self.path("ar.pak")
self.path("bg.pak")
@@ -576,7 +688,7 @@ class WindowsManifest(ViewerManifest):
self.path("zh-CN.pak")
self.path("zh-TW.pak")
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'bin', 'release'), dst="llplugin"):
+ with self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="llplugin"):
self.path("libvlc.dll")
self.path("libvlccore.dll")
self.path("plugins/")
@@ -712,7 +824,7 @@ class WindowsManifest(ViewerManifest):
nsis_retry_wait=15
for attempt in xrange(nsis_attempts):
try:
- self.run_command('"' + NSIS_path + '" /V2 ' + self.dst_path_of(tempfile))
+ self.run_command([NSIS_path, '/V2', self.dst_path_of(tempfile)])
except ManifestError as err:
if attempt+1 < nsis_attempts:
print >> sys.stderr, "nsis failed, waiting %d seconds before retrying" % nsis_retry_wait
@@ -735,8 +847,7 @@ class WindowsManifest(ViewerManifest):
if os.path.exists(sign_py):
dst_path = self.dst_path_of(exe)
print "about to run signing of: ", dst_path
- self.run_command(' '.join((python, self.escape_slashes(sign_py),
- self.escape_slashes(dst_path))))
+ self.run_command([python, sign_py, dst_path])
else:
print "Skipping code signing of %s: %s not found" % (exe, sign_py)
@@ -764,282 +875,397 @@ class DarwinManifest(ViewerManifest):
return True
def construct(self):
+ global plistlib
+ import plistlib # only import for Darwin
+
+ # These are the names of the top-level application and the embedded
+ # applications for the VMP and for the actual viewer, respectively.
+ # These names, without the .app suffix, determine the flyover text for
+ # their corresponding Dock icons.
+ toplevel_app, toplevel_icon = "Second Life.app", "secondlife.icns"
+ launcher_app, launcher_icon = "Launcher.app", "secondlife.icns"
+ viewer_app, viewer_icon = "Viewer.app", "secondlife.icns"
+
# 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="")
+ self.path(os.path.join(self.args['configuration'], toplevel_app), dst="")
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
relpkgdir = os.path.join(pkgdir, "lib", "release")
debpkgdir = os.path.join(pkgdir, "lib", "debug")
+ # -------------------- top-level Second Life.app ---------------------
+ # top-level Second Life application is only a container
with self.prefix(src="", dst="Contents"): # everything goes in Contents
- self.path("Info.plist")
-
- with self.prefix(src=relpkgdir, dst="Resources"):
- self.path("libndofdev.dylib")
- self.path("libhunspell-1.3.0.dylib")
-
- # copy additional libs in <bundle>/Contents/MacOS/
- with self.prefix(dst="MacOS"):
- #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322, SL-323
- with self.prefix(src=os.path.join(pkgdir, "VMP"), dst=""):
- self.path("SL_Launcher")
- self.path("*.py")
- # certifi will be imported by requests; this is our custom version to get our ca-bundle.crt
- self.path("certifi")
- with self.prefix(src=os.path.join(pkgdir, "lib", "python"), dst=""):
- # llbase provides our llrest service layer and llsd decoding
- with self.prefix("llbase"):
- self.path("*.py")
- self.path("_cllsd.so")
- #requests module needed by llbase/llrest.py
- #this is only needed on POSIX, because in Windows we compile it into the EXE
- for pypkg in "chardet", "idna", "requests", "urllib3":
- self.path(pypkg)
-
- # most everything goes in the Resources directory
- with self.prefix(src="", dst="Resources"):
- super(DarwinManifest, self).construct()
-
- with self.prefix("cursors_mac"):
- self.path("*.tif")
-
- self.path("licenses-mac.txt", dst="licenses.txt")
- self.path("featuretable_mac.txt")
- self.path("SecondLife.nib")
- self.path("ca-bundle.crt")
-
- icon_path = self.icon_path()
- with self.prefix(src=icon_path, dst="") :
- self.path("secondlife.icns")
+ # top-level Info.plist is as generated by CMake
+ Info_plist = "Info.plist"
+ ## This self.path() call reports 0 files... skip?
+ self.path(Info_plist)
+ Info_plist = self.dst_path_of(Info_plist)
+
+ # the one file in top-level MacOS directory is the trampoline to
+ # our nested launcher_app
+ with self.prefix(dst="MacOS"):
+ toplevel_MacOS = self.get_dst_prefix()
+ trampoline = self.put_in_file(
+ 'open "%s"\n' %
+ # up one directory from MacOS to its sibling Resources directory
+ os.path.join('$(dirname "$0")', os.pardir, 'Resources', launcher_app),
+ "SL_Launcher", # write this file
+ "trampoline") # flag to add to list of copied files
+ # Script must be executable -- but os.chmod() is pretty low-level:
+ # if we just chmod(stat.S_IEXEC), we end up with NOTHING BUT x!
+ # Have to read the existing permissions bits, then supplement with
+ # x for (user, group, other).
+ os.chmod(trampoline, stat.S_IMODE(os.stat(trampoline).st_mode)
+ | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+
+ # Make a symlink to a nested app Frameworks directory that doesn't
+ # yet exist. We shouldn't need this; the only things that need
+ # Frameworks are nested apps under viewer_app, and they should
+ # simply find its Contents/Frameworks by relative pathnames. But
+ # empirically, we do: if we omit this symlink, CEF doesn't work --
+ # the login splash screen doesn't even display. SIIIIGH.
+ # We're passing a path that's already relative, hence symlinkf()
+ # rather than relsymlinkf().
+ self.symlinkf(os.path.join("Resources", viewer_app, "Contents", "Frameworks"))
- with self.prefix(src=icon_path, dst="vmp_icons"):
- self.path("secondlife.ico")
-
- #VMP Tkinter icons
- with self.prefix("vmp_icons"):
- self.path("*.png")
- self.path("*.gif")
-
- self.path("SecondLife.nib")
-
- # Translations
- self.path("English.lproj/language.txt")
- self.replace_in(src="English.lproj/InfoPlist.strings",
- dst="English.lproj/InfoPlist.strings",
- searchdict={'%%VERSION%%':'.'.join(self.args['version'])}
- )
- 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")
-
- def path_optional(src, dst):
- """
- For a number of our self.path() calls, not only do we want
- to deal with the absence of src, we also want to remember
- which were present. Return either an empty list (absent)
- or a list containing dst (present). Concatenate these
- return values to get a list of all libs that are present.
- """
- # This was simple before we started needing to pass
- # wildcards. Fortunately, self.path() ends up appending a
- # (source, dest) pair to self.file_list for every expanded
- # file processed. Remember its size before the call.
- oldlen = len(self.file_list)
- self.path(src, dst)
- # The dest appended to self.file_list has been prepended
- # with self.get_dst_prefix(). Strip it off again.
- added = [os.path.relpath(d, self.get_dst_prefix())
- for s, d in self.file_list[oldlen:]]
- if not added:
- print "Skipping %s" % dst
- return added
-
- # dylibs is a list of all the .dylib files we expect to need
- # in our bundled sub-apps. For each of these we'll create a
- # symlink from sub-app/Contents/Resources to the real .dylib.
- # Need to get the llcommon dll from any of the build directories as well.
- libfile = "libllcommon.dylib"
- dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir,
- "llcommon",
- self.args['configuration'],
- libfile),
- os.path.join(relpkgdir, libfile)),
- dst=libfile)
-
- for libfile in (
- "libapr-1.0.dylib",
- "libaprutil-1.0.dylib",
- "libcollada14dom.dylib",
- "libexpat.1.dylib",
- "libexception_handler.dylib",
- "libGLOD.dylib",
- # libnghttp2.dylib is a symlink to
- # libnghttp2.major.dylib, which is a symlink
- # to libnghttp2.version.dylib. Get all of them.
- "libnghttp2.*dylib",
- ):
- dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
-
- # SLVoice and vivox lols, no symlinks needed
- for libfile in (
- 'libortp.dylib',
- 'libsndfile.dylib',
- 'libvivoxoal.dylib',
- 'libvivoxsdk.dylib',
- 'libvivoxplatform.dylib',
- 'SLVoice',
- ):
- self.path2basename(relpkgdir, libfile)
-
- # dylibs that vary based on configuration
- if self.args['configuration'].lower() == 'debug':
- for libfile in (
- "libfmodexL.dylib",
- ):
- dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile)
- else:
- for libfile in (
- "libfmodex.dylib",
- ):
- dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
-
- # our apps
- for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"),
- # plugin launcher
- (os.path.join("llplugin", "slplugin"), "SLPlugin.app"),
- ):
- self.path2basename(os.path.join(os.pardir,
- app_bld_dir, self.args['configuration']),
- app)
-
- # our apps dependencies on shared libs
- # for each app, for each dylib we collected in dylibs,
- # create a symlink to the real copy of the dylib.
- resource_path = self.dst_path_of(os.path.join(app, "Contents", "Resources"))
- for libfile in dylibs:
- src = os.path.join(os.pardir, os.pardir, os.pardir, libfile)
- dst = os.path.join(resource_path, libfile)
- try:
- symlinkf(src, dst)
- except OSError as err:
- print "Can't symlink %s -> %s: %s" % (src, dst, err)
-
- # Dullahan helper apps go inside SLPlugin.app
- with self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"):
- helperappfile = 'DullahanHelper.app'
- self.path2basename(relpkgdir, helperappfile)
-
- pluginframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
- # Putting a Frameworks directory under Contents/MacOS
- # isn't canonical, but the path baked into Dullahan
- # Helper.app/Contents/MacOS/DullahanHelper is:
- # @executable_path/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework
- # (notice, not @executable_path/../Frameworks/etc.)
- # So we'll create a symlink (below) from there back to the
- # Frameworks directory nested under SLPlugin.app.
- helperframeworkpath = \
- self.dst_path_of('DullahanHelper.app/Contents/MacOS/'
- 'Frameworks/Chromium Embedded Framework.framework')
-
-
- helperexecutablepath = self.dst_path_of('SLPlugin.app/Contents/Frameworks/DullahanHelper.app/Contents/MacOS/DullahanHelper')
- self.run_command('install_name_tool -change '
- '"@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" '
- '"@executable_path/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" "%s"' % helperexecutablepath)
-
- # SLPlugin plugins
- with self.prefix(src="", dst="llplugin"):
- self.path2basename("../media_plugins/cef/" + self.args['configuration'],
- "media_plugin_cef.dylib")
-
- # copy LibVLC plugin itself
- self.path2basename("../media_plugins/libvlc/" + self.args['configuration'],
- "media_plugin_libvlc.dylib")
-
- # copy LibVLC dynamic libraries
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'release' ), dst="lib"):
- self.path( "libvlc*.dylib*" )
-
- # copy LibVLC plugins folder
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'release', 'plugins' ), dst="lib"):
- self.path( "*.dylib" )
- self.path( "plugins.dat" )
-
-
- # do this install_name_tool *after* media plugin is copied over
- dylibexecutablepath = self.dst_path_of('llplugin/media_plugin_cef.dylib')
- self.run_command('install_name_tool -change '
- '"@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" '
- '"@executable_path/../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" "%s"' % dylibexecutablepath)
-
-
- # CEF framework goes inside Second Life.app/Contents/Frameworks
- with self.prefix(src="", dst="Frameworks"):
- frameworkfile="Chromium Embedded Framework.framework"
- self.path2basename(relpkgdir, frameworkfile)
-
- # This code constructs a relative path from the
- # target framework folder back to the location of the symlink.
- # It needs to be relative so that the symlink still works when
- # (as is normal) the user moves the app bundle out of the DMG
- # and into the /Applications folder. Note we also call 'raise'
- # to terminate the process if we get an error since without
- # this symlink, Second Life web media can't possibly work.
- # Real Framework folder:
- # Second Life.app/Contents/Frameworks/Chromium Embedded Framework.framework/
- # Location of symlink and why it's relative
- # Second Life.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework/
- # Real Frameworks folder, with the symlink inside the bundled SLPlugin.app (and why it's relative)
- # <top level>.app/Contents/Frameworks/Chromium Embedded Framework.framework/
- # <top level>.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework ->
- # It might seem simpler just to create a symlink Frameworks to
- # the parent of Chromimum Embedded Framework.framework. But
- # that would create a symlink cycle, which breaks our
- # packaging step. So make a symlink from Chromium Embedded
- # Framework.framework to the directory of the same name, which
- # is NOT an ancestor of the symlink.
- frameworkpath = os.path.join(os.pardir, os.pardir, os.pardir,
- os.pardir, "Frameworks",
- "Chromium Embedded Framework.framework")
- try:
- # from SLPlugin.app/Contents/Frameworks/Chromium Embedded
- # Framework.framework back to Second
- # Life.app/Contents/Frameworks/Chromium Embedded Framework.framework
- origin, target = pluginframeworkpath, frameworkpath
- symlinkf(target, origin)
- # from SLPlugin.app/Contents/Frameworks/Dullahan
- # Helper.app/Contents/MacOS/Frameworks/Chromium Embedded
- # Framework.framework back to
- # SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework
- self.cmakedirs(os.path.dirname(helperframeworkpath))
- origin = helperframeworkpath
- target = os.path.join(os.pardir, frameworkpath)
- symlinkf(target, origin)
- except OSError as err:
- print "Can't symlink %s -> %s: %s" % (origin, target, err)
- raise
+ with self.prefix(src="", dst="Resources"):
+ # top-level Resources directory should be pretty sparse
+ # need .icns file referenced by top-level Info.plist
+ with self.prefix(src=self.icon_path(), dst="") :
+ self.path(toplevel_icon)
+
+ # ------------------- nested launcher_app --------------------
+ with self.prefix(dst=os.path.join(launcher_app, "Contents")):
+ # Info.plist is just like top-level one...
+ Info = plistlib.readPlist(Info_plist)
+ # except for these replacements:
+ Info["CFBundleExecutable"] = "SL_Launcher"
+ Info["CFBundleIconFile"] = launcher_icon
+ self.put_in_file(
+ plistlib.writePlistToString(Info),
+ os.path.basename(Info_plist),
+ "Info.plist")
+
+ # copy VMP libs to MacOS
+ with self.prefix(dst="MacOS"):
+ #this copies over the python wrapper script,
+ #associated utilities and required libraries, see
+ #SL-321, SL-322, SL-323
+ with self.prefix(src=os.path.join(pkgdir, "VMP"), dst=""):
+ self.path("SL_Launcher")
+ self.path("*.py")
+ # certifi will be imported by requests; this is
+ # our custom version to get our ca-bundle.crt
+ self.path("certifi")
+ with self.prefix(src=os.path.join(pkgdir, "lib", "python"), dst=""):
+ # llbase provides our llrest service layer and llsd decoding
+ with self.prefix("llbase"):
+ # (Why is llbase treated specially here? What
+ # DON'T we want to copy out of lib/python/llbase?)
+ self.path("*.py")
+ self.path("_cllsd.so")
+ #requests module needed by llbase/llrest.py
+ #this is only needed on POSIX, because in Windows
+ #we compile it into the EXE
+ for pypkg in "chardet", "idna", "requests", "urllib3":
+ self.path(pypkg)
+
+ # launcher_app/Contents/Resources
+ with self.prefix(dst="Resources"):
+ with self.prefix(src=self.icon_path(), dst="") :
+ self.path(launcher_icon)
+ with self.prefix(dst="vmp_icons"):
+ self.path("secondlife.ico")
+ #VMP Tkinter icons
+ with self.prefix("vmp_icons"):
+ self.path("*.png")
+ self.path("*.gif")
+
+ # -------------------- nested viewer_app ---------------------
+ with self.prefix(dst=os.path.join(viewer_app, "Contents")):
+ # Info.plist is just like top-level one...
+ Info = plistlib.readPlist(Info_plist)
+ # except for these replacements:
+ # (CFBundleExecutable may be moot: SL_Launcher directly
+ # runs the executable, instead of launching the app)
+ Info["CFBundleExecutable"] = "Second Life"
+ Info["CFBundleIconFile"] = viewer_icon
+ self.put_in_file(
+ plistlib.writePlistToString(Info),
+ os.path.basename(Info_plist),
+ "Info.plist")
+
+ # CEF framework goes inside viewer_app/Contents/Frameworks.
+ # Remember where we parked this car.
+ with self.prefix(src="", dst="Frameworks"):
+ CEF_framework = "Chromium Embedded Framework.framework"
+ self.path2basename(relpkgdir, CEF_framework)
+ CEF_framework = self.dst_path_of(CEF_framework)
+
+ with self.prefix(dst="MacOS"):
+ # CMake constructs the Second Life executable in the
+ # MacOS directory belonging to the top-level Second
+ # Life.app. Move it here.
+ here = self.get_dst_prefix()
+ relbase = os.path.realpath(os.path.dirname(Info_plist))
+ self.cmakedirs(here)
+ for f in os.listdir(toplevel_MacOS):
+ if f == os.path.basename(trampoline):
+ # don't move the trampoline script we just made!
+ continue
+ fromwhere = os.path.join(toplevel_MacOS, f)
+ towhere = os.path.join(here, f)
+ print "Moving %s => %s" % \
+ (self.relpath(fromwhere, relbase),
+ self.relpath(towhere, relbase))
+ # now do it, only without relativizing paths
+ os.rename(fromwhere, towhere)
+
+ # 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', self.dst_path_of('Second Life')])
+
+ with self.prefix(dst="Resources"):
+ # defer cross-platform file copies until we're in the right
+ # nested Resources directory
+ super(DarwinManifest, self).construct()
+
+ with self.prefix(src=self.icon_path(), dst="") :
+ self.path(viewer_icon)
+
+ with self.prefix(src=relpkgdir, dst=""):
+ self.path("libndofdev.dylib")
+ self.path("libhunspell-1.3.0.dylib")
+
+ with self.prefix("cursors_mac"):
+ self.path("*.tif")
+
+ self.path("licenses-mac.txt", dst="licenses.txt")
+ self.path("featuretable_mac.txt")
+ self.path("SecondLife.nib")
+ self.path("ca-bundle.crt")
+
+ self.path("SecondLife.nib")
+
+ # Translations
+ self.path("English.lproj/language.txt")
+ self.replace_in(src="English.lproj/InfoPlist.strings",
+ dst="English.lproj/InfoPlist.strings",
+ searchdict={'%%VERSION%%':'.'.join(self.args['version'])}
+ )
+ 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")
+
+ def path_optional(src, dst):
+ """
+ For a number of our self.path() calls, not only do we want
+ to deal with the absence of src, we also want to remember
+ which were present. Return either an empty list (absent)
+ or a list containing dst (present). Concatenate these
+ return values to get a list of all libs that are present.
+ """
+ # This was simple before we started needing to pass
+ # wildcards. Fortunately, self.path() ends up appending a
+ # (source, dest) pair to self.file_list for every expanded
+ # file processed. Remember its size before the call.
+ oldlen = len(self.file_list)
+ self.path(src, dst)
+ # The dest appended to self.file_list has been prepended
+ # with self.get_dst_prefix(). Strip it off again.
+ added = [os.path.relpath(d, self.get_dst_prefix())
+ for s, d in self.file_list[oldlen:]]
+ if not added:
+ print "Skipping %s" % dst
+ return added
+
+ # dylibs is a list of all the .dylib files we expect to need
+ # in our bundled sub-apps. For each of these we'll create a
+ # symlink from sub-app/Contents/Resources to the real .dylib.
+ # Need to get the llcommon dll from any of the build directories as well.
+ libfile_parent = self.get_dst_prefix()
+ libfile = "libllcommon.dylib"
+ dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir,
+ "llcommon",
+ self.args['configuration'],
+ libfile),
+ os.path.join(relpkgdir, libfile)),
+ dst=libfile)
+
+ for libfile in (
+ "libapr-1.0.dylib",
+ "libaprutil-1.0.dylib",
+ "libcollada14dom.dylib",
+ "libexpat.1.dylib",
+ "libexception_handler.dylib",
+ "libGLOD.dylib",
+ # libnghttp2.dylib is a symlink to
+ # libnghttp2.major.dylib, which is a symlink to
+ # libnghttp2.version.dylib. Get all of them.
+ "libnghttp2.*dylib",
+ ):
+ dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+
+ # SLVoice and vivox lols, no symlinks needed
+ for libfile in (
+ 'libortp.dylib',
+ 'libsndfile.dylib',
+ 'libvivoxoal.dylib',
+ 'libvivoxsdk.dylib',
+ 'libvivoxplatform.dylib',
+ 'SLVoice',
+ ):
+ self.path2basename(relpkgdir, libfile)
+
+ # dylibs that vary based on configuration
+ if self.args['configuration'].lower() == 'debug':
+ for libfile in (
+ "libfmodexL.dylib",
+ ):
+ dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile)
+ else:
+ for libfile in (
+ "libfmodex.dylib",
+ ):
+ dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+
+ # our apps
+ executable_path = {}
+ for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"),
+ # plugin launcher
+ (os.path.join("llplugin", "slplugin"), "SLPlugin.app"),
+ ):
+ self.path2basename(os.path.join(os.pardir,
+ app_bld_dir, self.args['configuration']),
+ app)
+ executable_path[app] = \
+ self.dst_path_of(os.path.join(app, "Contents", "MacOS"))
+
+ # our apps dependencies on shared libs
+ # for each app, for each dylib we collected in dylibs,
+ # create a symlink to the real copy of the dylib.
+ with self.prefix(dst=os.path.join(app, "Contents", "Resources")):
+ for libfile in dylibs:
+ self.relsymlinkf(os.path.join(libfile_parent, libfile))
+
+ # Dullahan helper apps go inside SLPlugin.app
+ with self.prefix(dst=os.path.join(
+ "SLPlugin.app", "Contents", "Frameworks")):
+
+ frameworkname = 'Chromium Embedded Framework'
+
+ # This code constructs a relative symlink from the
+ # target framework folder back to the real CEF framework.
+ # It needs to be relative so that the symlink still works when
+ # (as is normal) the user moves the app bundle out of the DMG
+ # and into the /Applications folder. Note we pass catch=False,
+ # letting the uncaught exception terminate the process, since
+ # without this symlink, Second Life web media can't possibly work.
+
+ # It might seem simpler just to symlink Frameworks back to
+ # the parent of Chromimum Embedded Framework.framework. But
+ # that would create a symlink cycle, which breaks our
+ # packaging step. So make a symlink from Chromium Embedded
+ # Framework.framework to the directory of the same name, which
+ # is NOT an ancestor of the symlink.
+
+ # from SLPlugin.app/Contents/Frameworks/Chromium Embedded
+ # Framework.framework back to
+ # $viewer_app/Contents/Frameworks/Chromium Embedded Framework.framework
+ SLPlugin_framework = self.relsymlinkf(CEF_framework, catch=False)
+
+ # copy DullahanHelper.app
+ self.path2basename(relpkgdir, 'DullahanHelper.app')
+
+ # and fix that up with a Frameworks/CEF symlink too
+ with self.prefix(dst=os.path.join(
+ 'DullahanHelper.app', 'Contents', 'Frameworks')):
+ # from Dullahan Helper.app/Contents/Frameworks/Chromium Embedded
+ # Framework.framework back to
+ # SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework
+ # Since SLPlugin_framework is itself a
+ # symlink, don't let relsymlinkf() resolve --
+ # explicitly call relpath(symlink=True) and
+ # create that symlink here.
+ DullahanHelper_framework = \
+ self.symlinkf(self.relpath(SLPlugin_framework, symlink=True),
+ catch=False)
+
+ # change_command includes install_name_tool, the
+ # -change subcommand and the old framework rpath
+ # stamped into the executable. To use it with
+ # run_command(), we must still append the new
+ # framework path and the pathname of the
+ # executable to change.
+ change_command = [
+ 'install_name_tool', '-change',
+ '@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework']
+
+ with self.prefix(dst=os.path.join(
+ 'DullahanHelper.app', 'Contents', 'MacOS')):
+ # Now self.get_dst_prefix() is, at runtime,
+ # @executable_path. Locate the helper app
+ # framework (which is a symlink) from here.
+ newpath = os.path.join(
+ '@executable_path',
+ self.relpath(DullahanHelper_framework, symlink=True),
+ frameworkname)
+ # and restamp the DullahanHelper executable
+ self.run_command(
+ change_command +
+ [newpath, self.dst_path_of('DullahanHelper')])
+
+ # SLPlugin plugins
+ with self.prefix(dst="llplugin"):
+ dylibexecutable = 'media_plugin_cef.dylib'
+ self.path2basename("../media_plugins/cef/" + self.args['configuration'],
+ dylibexecutable)
+
+ # Do this install_name_tool *after* media plugin is copied over.
+ # Locate the framework lib executable -- relative to
+ # SLPlugin.app/Contents/MacOS, which will be our
+ # @executable_path at runtime!
+ newpath = os.path.join(
+ '@executable_path',
+ self.relpath(SLPlugin_framework, executable_path["SLPlugin.app"],
+ symlink=True),
+ frameworkname)
+ # restamp media_plugin_cef.dylib
+ self.run_command(
+ change_command +
+ [newpath, self.dst_path_of(dylibexecutable)])
+ # copy LibVLC plugin itself
+ self.path2basename("../media_plugins/libvlc/" + self.args['configuration'],
+ "media_plugin_libvlc.dylib")
- # 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')})
+ # copy LibVLC dynamic libraries
+ with self.prefix(src=relpkgdir, dst="lib"):
+ self.path( "libvlc*.dylib*" )
+ # copy LibVLC plugins folder
+ with self.prefix(src='plugins', dst=""):
+ self.path( "*.dylib" )
+ self.path( "plugins.dat" )
def package_finish(self):
global CHANNEL_VENDOR_BASE
@@ -1055,9 +1281,10 @@ class DarwinManifest(ViewerManifest):
# 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 1300 -layout SPUD' % {
- 'sparse':sparsename,
- 'vol':volname})
+ self.run_command(['hdiutil', 'create', sparsename,
+ '-volname', volname, '-fs', 'HFS+',
+ '-type', 'SPARSE', '-megabytes', '1300',
+ '-layout', 'SPUD'])
# mount the image and get the name of the mount point and device node
try:
@@ -1116,18 +1343,18 @@ class DarwinManifest(ViewerManifest):
# 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)
+ self.run_command(['SetFile', '-a', 'V', 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")))
+ self.run_command(
+ ['Rez', self.src_path_of("installers/darwin/release-dmg/Applications-alias.r"),
+ '-o', 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"))
+ self.run_command(['SetFile', '-a', 'AC', os.path.join(volpath, "Applications")])
# Set the disk image root's custom icon bit
- self.run_command('SetFile -a C %r' % volpath)
+ self.run_command(['SetFile', '-a', 'C', volpath])
# Sign the app if requested;
# do this in the copy that's in the .dmg so that the extended attributes used by
@@ -1163,7 +1390,10 @@ class DarwinManifest(ViewerManifest):
# and that it contains the correct cert/key. If a build host is set up with a clean version of macOS Sierra (or later)
# then you will need to change this line (and the one for 'codesign' command below) to point to right place or else
# pull in the cert/key into the default viewer keychain 'viewer.keychain-db' and export it to 'viewer.keychain'
- self.run_command('security unlock-keychain -p "%s" "%s/Library/Keychains/viewer.keychain"' % ( keychain_pwd, home_path ) )
+ viewer_keychain = os.path.join(home_path, 'Library',
+ 'Keychains', 'viewer.keychain')
+ self.run_command(['security', 'unlock-keychain',
+ '-p', keychain_pwd, viewer_keychain])
signed=False
sign_attempts=3
sign_retry_wait=15
@@ -1172,11 +1402,9 @@ class DarwinManifest(ViewerManifest):
sign_attempts-=1;
self.run_command(
# Note: See blurb above about names of keychains
- 'codesign --verbose --deep --force --keychain "%(home_path)s/Library/Keychains/viewer.keychain" --sign %(identity)r %(bundle)r' % {
- 'home_path' : home_path,
- 'identity': identity,
- 'bundle': app_in_dmg
- })
+ ['codesign', '--verbose', '--deep', '--force',
+ '--keychain', viewer_keychain, '--sign', identity,
+ app_in_dmg])
signed=True # if no exception was raised, the codesign worked
except ManifestError as err:
if sign_attempts:
@@ -1186,18 +1414,19 @@ class DarwinManifest(ViewerManifest):
else:
print >> sys.stderr, "Maximum codesign attempts exceeded; giving up"
raise
- self.run_command('spctl -a -texec -vv %(bundle)r' % { 'bundle': app_in_dmg })
+ self.run_command(['spctl', '-a', '-texec', '-vv', app_in_dmg])
imagename="SecondLife_" + '_'.join(self.args['version'])
finally:
# Unmount the image even if exceptions from any of the above
- self.run_command('hdiutil detach -force %r' % devfile)
+ self.run_command(['hdiutil', 'detach', '-force', 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})
- self.run_command('hdiutil internet-enable -yes %(final)r' % {'final':finalname})
+ self.run_command(['hdiutil', 'convert', sparsename, '-format', 'UDZO',
+ '-imagekey', 'zlib-level=9', '-o', finalname])
+ self.run_command(['hdiutil', 'internet-enable', '-yes', finalname])
# get rid of the temp file
self.package_file = finalname
self.remove(sparsename)
@@ -1268,11 +1497,11 @@ class LinuxManifest(ViewerManifest):
"libmedia_plugin_gstreamer.so")
self.path2basename("libvlc", "libmedia_plugin_libvlc.so")
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"):
+ with self.prefix(src=os.path.join(pkgdir, 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"):
self.path( "plugins.dat" )
self.path( "*/*.so" )
- with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib' ), dst="lib"):
+ with self.prefix(src=os.path.join(pkgdir, 'lib' ), dst="lib"):
self.path( "libvlc*.so*" )
# llcommon
@@ -1288,43 +1517,42 @@ class LinuxManifest(ViewerManifest):
self.strip_binaries()
# 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.run_command(['find', self.get_dst_prefix(),
+ '-type', 'd', '-exec', 'chmod', '755', '{}', ';'])
+ for old, new in ('0700', '0755'), ('0500', '0555'), ('0600', '0644'), ('0400', '0444'):
+ self.run_command(['find', self.get_dst_prefix(),
+ '-type', 'f', '-perm', old,
+ '-exec', 'chmod', new, '{}', ';'])
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)})
+ realname = self.get_dst_prefix()
+ tempname = self.build_path_of(installer_name)
+ self.run_command(["mv", realname, tempname])
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)})
+ self.run_command(['tar', '-C', self.get_build_prefix(),
+ '--numeric-owner', '-cjf',
+ tempname + '.tar.bz2', installer_name])
else:
print "Skipping %s.tar.bz2 for non-Release build (%s)" % \
(installer_name, self.args['buildtype'])
finally:
- self.run_command("mv %(inst)s %(dst)s" % {
- 'dst': self.get_dst_prefix(),
- 'inst': self.build_path_of(installer_name)})
+ self.run_command(["mv", tempname, realname])
def strip_binaries(self):
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"
# makes some small assumptions about our packaged dir structure
- self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name \*.py \! -name SL_Launcher \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} )
+ self.run_command(
+ ["find"] +
+ [os.path.join(self.get_dst_prefix(), dir) for dir in ('bin', 'lib')] +
+ ['-type', 'f', '!', '-name', '*.py', '!', '-name', 'SL_Launcher',
+ '!', '-name', 'update_install', '-exec', 'strip', '-S', '{}', ';'])
class Linux_i686_Manifest(LinuxManifest):
address_size = 32
@@ -1421,46 +1649,5 @@ class Linux_x86_64_Manifest(LinuxManifest):
################################################################
-def symlinkf(src, dst):
- """
- Like ln -sf, but uses os.symlink() instead of running ln.
- """
- try:
- os.symlink(src, dst)
- except OSError as err:
- if err.errno != errno.EEXIST:
- raise
- # We could just blithely attempt to remove and recreate the target
- # file, but that strategy doesn't work so well if we don't have
- # permissions to remove it. Check to see if it's already the
- # symlink we want, which is the usual reason for EEXIST.
- elif os.path.islink(dst):
- if os.readlink(dst) == src:
- # the requested link already exists
- pass
- else:
- # dst is the wrong symlink; attempt to remove and recreate it
- os.remove(dst)
- os.symlink(src, dst)
- elif os.path.isdir(dst):
- print "Requested symlink (%s) exists but is a directory; replacing" % dst
- shutil.rmtree(dst)
- os.symlink(src, dst)
- elif os.path.exists(dst):
- print "Requested symlink (%s) exists but is a file; replacing" % dst
- os.remove(dst)
- os.symlink(src, dst)
- else:
- # see if the problem is that the parent directory does not exist
- # and try to explain what is missing
- (parent, tail) = os.path.split(dst)
- while not os.path.exists(parent):
- (parent, tail) = os.path.split(parent)
- if tail:
- raise Exception("Requested symlink (%s) cannot be created because %s does not exist"
- % os.path.join(parent, tail))
- else:
- raise
-
if __name__ == "__main__":
main()