Skip to content
Snippets Groups Projects
Commit a507df6e authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

SWAT-711: Create Mac app bundle symlinks only for .dylib files that exist.

Previous viewer_manifest.py unconditionally created Mac symlinks for all expected
.dylib files. Recent change to revert to statically linking llcommon means we
no longer build libllcommon.dylib, therefore we no longer copy it, therefore
any symlink to that library will be broken by definition.
Change to create symlinks for .dylib files that were successfully copied.
parent dab915c1
No related branches found
No related tags found
No related merge requests found
...@@ -621,6 +621,23 @@ def expand_globs(self, src, dst): ...@@ -621,6 +621,23 @@ def expand_globs(self, src, dst):
d = src_re.sub(d_template, s.replace('\\', '/')) d = src_re.sub(d_template, s.replace('\\', '/'))
yield os.path.normpath(s), os.path.normpath(d) yield os.path.normpath(s), os.path.normpath(d)
def path2basename(self, path, file):
"""
It is a common idiom to write:
self.path(os.path.join(somedir, somefile), somefile)
So instead you can write:
self.path2basename(somedir, somefile)
Note that this is NOT the same as:
self.path(os.path.join(somedir, somefile))
which is the same as:
temppath = os.path.join(somedir, somefile)
self.path(temppath, temppath)
"""
return self.path(os.path.join(path, file), file)
def path(self, src, dst=None): def path(self, src, dst=None):
sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.write("Processing %s => %s ... " % (src, dst))
sys.stdout.flush() sys.stdout.flush()
...@@ -666,6 +683,10 @@ def try_path(src): ...@@ -666,6 +683,10 @@ def try_path(src):
print "%d files" % count print "%d files" % count
# Let caller check whether we processed as many files as expected. In
# particular, let caller notice 0.
return count
def do(self, *actions): def do(self, *actions):
self.actions = actions self.actions = actions
self.construct() self.construct()
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
""" """
import sys import sys
import os.path import os.path
import errno
import re import re
import tarfile import tarfile
import time import time
...@@ -74,20 +75,20 @@ def construct(self): ...@@ -74,20 +75,20 @@ def construct(self):
# include the list of Lindens (if any) # include the list of Lindens (if any)
# see https://wiki.lindenlab.com/wiki/Generated_Linden_Credits # see https://wiki.lindenlab.com/wiki/Generated_Linden_Credits
linden_names_path = os.getenv("LINDEN_CREDITS") linden_names_path = os.getenv("LINDEN_CREDITS")
if linden_names_path : if not linden_names_path :
print "No 'LINDEN_CREDITS' specified in environment, using built-in list"
else:
try: try:
linden_file = open(linden_names_path,'r') linden_file = open(linden_names_path,'r')
except IOError:
print "No Linden names found at '%s', using built-in list" % linden_names_path
else:
# all names should be one line, but the join below also converts to a string # all names should be one line, but the join below also converts to a string
linden_names = ', '.join(linden_file.readlines()) linden_names = ', '.join(linden_file.readlines())
self.put_in_file(linden_names, "lindens.txt") self.put_in_file(linden_names, "lindens.txt")
linden_file.close() linden_file.close()
print "Linden names extracted from '%s'" % linden_names_path print "Linden names extracted from '%s'" % linden_names_path
self.file_list.append([linden_names_path,self.dst_path_of("lindens.txt")]) self.file_list.append([linden_names_path,self.dst_path_of("lindens.txt")])
except IOError:
print "No Linden names found at '%s', using built-in list" % linden_names_path
pass
else :
print "No 'LINDEN_CREDITS' specified in environment, using built-in list"
# ... and the entire windlight directory # ... and the entire windlight directory
self.path("windlight") self.path("windlight")
...@@ -149,14 +150,9 @@ def construct(self): ...@@ -149,14 +150,9 @@ def construct(self):
self.path("gpu_table.txt") self.path("gpu_table.txt")
# The summary.json file gets left in the base checkout dir by # The summary.json file gets left in the base checkout dir by
# build.sh. It's only created for a build.sh build, therefore we # build.sh. It's only created for a build.sh build.
# have to check whether it exists. :-P if not self.path2basename(os.path.join(os.pardir, os.pardir), "summary.json"):
summary_json = "summary.json" print "No summary.json file"
summary_json_path = os.path.join(os.pardir, os.pardir, summary_json)
if os.path.exists(os.path.join(self.get_src_prefix(), summary_json_path)):
self.path(summary_json_path, summary_json)
else:
print "No %s" % os.path.join(self.get_src_prefix(), summary_json_path)
def login_channel(self): def login_channel(self):
"""Channel reported for login and upgrade purposes ONLY; """Channel reported for login and upgrade purposes ONLY;
...@@ -327,13 +323,13 @@ def construct(self): ...@@ -327,13 +323,13 @@ def construct(self):
self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe())
# Plugin host application # Plugin host application
self.path(os.path.join(os.pardir, self.path2basename(os.path.join(os.pardir,
'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"), 'llplugin', 'slplugin', self.args['configuration']),
"slplugin.exe") "slplugin.exe")
#self.disable_manifest_check() #self.disable_manifest_check()
self.path(src="../viewer_components/updater/scripts/windows/update_install.bat", dst="update_install.bat") self.path2basename("../viewer_components/updater/scripts/windows", "update_install.bat")
# Get shared libs from the shared libs staging directory # Get shared libs from the shared libs staging directory
if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
dst=""): dst=""):
...@@ -367,9 +363,7 @@ def construct(self): ...@@ -367,9 +363,7 @@ def construct(self):
# Get fmod dll, continue if missing # Get fmod dll, continue if missing
try: if not self.path("fmod.dll"):
self.path("fmod.dll")
except:
print "Skipping fmod.dll" print "Skipping fmod.dll"
# For textures # For textures
...@@ -710,85 +704,82 @@ def construct(self): ...@@ -710,85 +704,82 @@ def construct(self):
self.path("uk.lproj") self.path("uk.lproj")
self.path("zh-Hans.lproj") self.path("zh-Hans.lproj")
libdir = "../packages/lib/release" def path_optional(src, dst):
dylibs = {} """
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.
"""
if self.path(src, dst):
return [dst]
print "Skipping %s" % dst
return []
# Need to get the llcommon dll from any of the build directories as well libdir = "../packages/lib/release"
lib = "llcommon" # dylibs is a list of all the .dylib files we expect to need
libfile = "lib%s.dylib" % lib # in our bundled sub-apps. For each of these we'll create a
try: # symlink from sub-app/Contents/Resources to the real .dylib.
self.path(self.find_existing_file(os.path.join(os.pardir, # Need to get the llcommon dll from any of the build directories as well.
lib, libfile = "libllcommon.dylib"
self.args['configuration'], dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir,
libfile), "llcommon",
os.path.join(libdir, libfile)), self.args['configuration'],
dst=libfile) libfile),
except RuntimeError: os.path.join(libdir, libfile)),
print "Skipping %s" % libfile dst=libfile)
dylibs[lib] = False
else: for libfile in (
dylibs[lib] = True "libapr-1.0.dylib",
"libaprutil-1.0.dylib",
if dylibs["llcommon"]: "libcollada14dom.dylib",
for libfile in ("libapr-1.0.dylib", "libexpat.1.5.2.dylib",
"libaprutil-1.0.dylib", "libexception_handler.dylib",
"libexpat.1.5.2.dylib", "libGLOD.dylib",
"libexception_handler.dylib", ):
"libGLOD.dylib", dylibs += path_optional(os.path.join(libdir, libfile), libfile)
"libcollada14dom.dylib"
): # SLVoice and vivox lols, no symlinks needed
self.path(os.path.join(libdir, libfile), libfile) for libfile in (
'libortp.dylib',
# SLVoice and vivox lols 'libsndfile.dylib',
for libfile in ('libsndfile.dylib', 'libvivoxoal.dylib', 'libortp.dylib', \ 'libvivoxoal.dylib',
'libvivoxsdk.dylib', 'libvivoxplatform.dylib', 'SLVoice') : 'libvivoxsdk.dylib',
self.path(os.path.join(libdir, libfile), libfile) 'libvivoxplatform.dylib',
'SLVoice',
):
self.path2basename(libdir, libfile)
try: # FMOD for sound
# FMOD for sound libfile = "libfmodwrapper.dylib"
self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib") path_optional(os.path.join(self.args['configuration'], libfile), libfile)
except:
print "Skipping FMOD - not found"
# our apps # our apps
self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app") for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"),
self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app") ("mac_updater", "mac-updater.app"),
# plugin launcher
# plugin launcher (os.path.join("llplugin", "slplugin"), "SLPlugin.app"),
self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin.app", "SLPlugin.app") ):
self.path2basename(os.path.join(os.pardir,
# our apps dependencies on shared libs app_bld_dir, self.args['configuration']),
if dylibs["llcommon"]: app)
mac_crash_logger_res_path = self.dst_path_of("mac-crash-logger.app/Contents/Resources")
mac_updater_res_path = self.dst_path_of("mac-updater.app/Contents/Resources") # our apps dependencies on shared libs
slplugin_res_path = self.dst_path_of("SLPlugin.app/Contents/Resources") # for each app, for each dylib we collected in dylibs,
for libfile in ("libllcommon.dylib", # create a symlink to the real copy of the dylib.
"libapr-1.0.dylib", resource_path = self.dst_path_of(os.path.join(app, "Contents", "Resources"))
"libaprutil-1.0.dylib", for libfile in dylibs:
"libexpat.1.5.2.dylib", symlinkf(os.path.join(os.pardir, os.pardir, os.pardir, libfile),
"libexception_handler.dylib", os.path.join(resource_path, libfile))
"libGLOD.dylib",
"libcollada14dom.dylib"
):
target_lib = os.path.join('../../..', libfile)
self.run_command("ln -sf %(target)r %(link)r" %
{'target': target_lib,
'link' : os.path.join(mac_crash_logger_res_path, libfile)}
)
self.run_command("ln -sf %(target)r %(link)r" %
{'target': target_lib,
'link' : os.path.join(mac_updater_res_path, libfile)}
)
self.run_command("ln -sf %(target)r %(link)r" %
{'target': target_lib,
'link' : os.path.join(slplugin_res_path, libfile)}
)
# plugins # plugins
if self.prefix(src="", dst="llplugin"): if self.prefix(src="", dst="llplugin"):
self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") self.path2basename("../media_plugins/quicktime/" + self.args['configuration'],
self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") "media_plugin_quicktime.dylib")
self.path("../packages/lib/release/libllqtwebkit.dylib", "libllqtwebkit.dylib") self.path2basename("../media_plugins/webkit/" + self.args['configuration'],
"media_plugin_webkit.dylib")
self.path2basename("../packages/lib/release", "libllqtwebkit.dylib")
self.end_prefix("llplugin") self.end_prefix("llplugin")
...@@ -931,20 +922,25 @@ def construct(self): ...@@ -931,20 +922,25 @@ def construct(self):
self.path("client-readme-voice.txt","README-linux-voice.txt") self.path("client-readme-voice.txt","README-linux-voice.txt")
self.path("client-readme-joystick.txt","README-linux-joystick.txt") self.path("client-readme-joystick.txt","README-linux-joystick.txt")
self.path("wrapper.sh","secondlife") self.path("wrapper.sh","secondlife")
self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh") if self.prefix(src="", dst="etc"):
self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh") self.path("handle_secondlifeprotocol.sh")
self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh") self.path("register_secondlifeprotocol.sh")
self.path("launch_url.sh","etc/launch_url.sh") self.path("refresh_desktop_app_entry.sh")
self.path("launch_url.sh")
self.end_prefix("etc")
self.path("install.sh") self.path("install.sh")
self.end_prefix("linux_tools") self.end_prefix("linux_tools")
# Create an appropriate gridargs.dat for this package, denoting required grid. # Create an appropriate gridargs.dat for this package, denoting required grid.
self.put_in_file(self.flags_list(), 'etc/gridargs.dat') self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
self.path("secondlife-bin","bin/do-not-directly-run-secondlife-bin") if self.prefix(src="", dst="bin"):
self.path("../linux_crash_logger/linux-crash-logger","bin/linux-crash-logger.bin") self.path("secondlife-bin","do-not-directly-run-secondlife-bin")
self.path("../linux_updater/linux-updater", "bin/linux-updater.bin") self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin")
self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin") self.path("../linux_updater/linux-updater", "linux-updater.bin")
self.path2basename("../llplugin/slplugin", "SLPlugin")
self.path2basename("../viewer_components/updater/scripts/linux", "update_install")
self.end_prefix("bin")
if self.prefix("res-sdl"): if self.prefix("res-sdl"):
self.path("*") self.path("*")
...@@ -960,17 +956,13 @@ def construct(self): ...@@ -960,17 +956,13 @@ def construct(self):
self.end_prefix("res-sdl") self.end_prefix("res-sdl")
self.end_prefix(icon_path) self.end_prefix(icon_path)
self.path("../viewer_components/updater/scripts/linux/update_install", "bin/update_install")
# plugins # plugins
if self.prefix(src="", dst="bin/llplugin"): if self.prefix(src="", dst="bin/llplugin"):
self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") self.path2basename("../media_plugins/webkit", "libmedia_plugin_webkit.so")
self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so")
self.end_prefix("bin/llplugin") self.end_prefix("bin/llplugin")
try: if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"):
self.path("../llcommon/libllcommon.so", "lib/libllcommon.so")
except:
print "Skipping llcommon.so (assuming llcommon was linked statically)" print "Skipping llcommon.so (assuming llcommon was linked statically)"
self.path("featuretable_linux.txt") self.path("featuretable_linux.txt")
...@@ -1054,11 +1046,8 @@ def construct(self): ...@@ -1054,11 +1046,8 @@ def construct(self):
self.path("libopenjpeg.so*") self.path("libopenjpeg.so*")
self.path("libdirectfb-1.4.so.5") self.path("libdirectfb-1.4.so.5")
self.path("libfusion-1.4.so.5") self.path("libfusion-1.4.so.5")
self.path("libdirect-1.4.so.5.0.4") self.path("libdirect-1.4.so.5*")
self.path("libdirect-1.4.so.5") self.path("libhunspell-1.3.so*")
self.path("libhunspell-1.3.so")
self.path("libhunspell-1.3.so.0")
self.path("libhunspell-1.3.so.0.0.0")
self.path("libalut.so") self.path("libalut.so")
self.path("libopenal.so", "libopenal.so.1") self.path("libopenal.so", "libopenal.so.1")
self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
...@@ -1082,12 +1071,8 @@ def construct(self): ...@@ -1082,12 +1071,8 @@ def construct(self):
# version number. # version number.
self.path("libfontconfig.so.*.*") self.path("libfontconfig.so.*.*")
self.path("libtcmalloc.so*") #formerly called google perf tools self.path("libtcmalloc.so*") #formerly called google perf tools
try: if not self.path("libfmod-3.75.so"):
self.path("libfmod-3.75.so") print "Skipping libfmod-3.75.so - not found"
pass
except:
print "Skipping libfmod-3.75.so - not found"
pass
self.end_prefix("lib") self.end_prefix("lib")
# Vivox runtimes # Vivox runtimes
...@@ -1116,5 +1101,25 @@ def construct(self): ...@@ -1116,5 +1101,25 @@ def construct(self):
################################################################ ################################################################
def symlinkf(src, dst):
"""
Like ln -sf, but uses os.symlink() instead of running ln.
"""
try:
os.symlink(src, dst)
except OSError, 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.
if not (os.path.islink(dst) and os.readlink(dst) == src):
# Here either dst isn't a symlink or it's the wrong symlink.
# Remove and recreate. Caller will just have to deal with any
# exceptions at this stage.
os.remove(dst)
os.symlink(src, dst)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment