Fix up icon copying and make manifest actually start to kinda work again

This commit is contained in:
Drake Arconis
2016-01-16 11:18:40 -05:00
parent 13666ac62a
commit e9f4b917d1
14 changed files with 586 additions and 473 deletions

2
.gitignore vendored
View File

@@ -10,6 +10,8 @@
/indra/viewer-* /indra/viewer-*
/indra/newview/vivox-runtime/ /indra/newview/vivox-runtime/
/indra/newview/dbghelp.dll /indra/newview/dbghelp.dll
indra/newview/res/viewer_icon.*
indra/newview/res-sdl/viewer_icon.*
/libraries/ /libraries/
/lib/ /lib/
*.pyc *.pyc

View File

@@ -24,9 +24,10 @@ set(LIBS_OPEN_PREFIX)
set(SCRIPTS_PREFIX ../scripts) set(SCRIPTS_PREFIX ../scripts)
set(VIEWER_PREFIX) set(VIEWER_PREFIX)
set(INTEGRATION_TESTS_PREFIX) set(INTEGRATION_TESTS_PREFIX)
set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds automatically disable TCMalloc)")
set(LL_TESTS OFF CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation)") set(LL_TESTS OFF CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation)")
# Compiler and toolchain options
set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds automatically disable TCMalloc)")
set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.") set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.")
if(LIBS_CLOSED_DIR) if(LIBS_CLOSED_DIR)
@@ -168,15 +169,10 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# Default deploy grid # Default deploy grid
set(GRID agni CACHE STRING "Target Grid") set(GRID agni CACHE STRING "Target Grid")
set(VIEWER_CHANNEL "Singularity" CACHE STRING "Viewer Channel Name") set(VIEWER_CHANNEL "Singularity Test" CACHE STRING "Viewer Channel Name")
set(VIEWER_LOGIN_CHANNEL "${VIEWER_CHANNEL}" CACHE STRING "Fake login channel for A/B Testing") set(ENABLE_SIGNING OFF CACHE BOOL "Enable signing the viewer")
set(VIEWER_BRANDING_ID "singularity" CACHE STRING "Viewer branding id (currently secondlife|snowglobe)") set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if necessary.")
# *TODO: break out proper Branding-secondlife.cmake, Branding-snowglobe.cmake, etc
string(REGEX REPLACE " +" "" VIEWER_CHANNEL_ONE_WORD "${VIEWER_CHANNEL}")
set(VIEWER_BRANDING_NAME "${VIEWER_CHANNEL_ONE_WORD}")
set(VIEWER_BRANDING_NAME_CAMELCASE "${VIEWER_CHANNEL_ONE_WORD}")
set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside") set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside")
set(STANDALONE OFF CACHE BOOL "Do not use Linden-supplied prebuilt libraries.") set(STANDALONE OFF CACHE BOOL "Do not use Linden-supplied prebuilt libraries.")

View File

@@ -41,6 +41,14 @@ import tarfile
import errno import errno
import subprocess import subprocess
class ManifestError(RuntimeError):
"""Use an exception more specific than generic Python RuntimeError"""
pass
class MissingError(ManifestError):
"""You specified a file that doesn't exist"""
pass
def path_ancestors(path): def path_ancestors(path):
drive, path = os.path.splitdrive(os.path.normpath(path)) drive, path = os.path.splitdrive(os.path.normpath(path))
result = [] result = []
@@ -76,31 +84,9 @@ def get_default_platform(dummy):
'darwin':'darwin' 'darwin':'darwin'
}[sys.platform] }[sys.platform]
def get_default_version(srctree):
# look up llversion.h and parse out the version info
paths = [os.path.join(srctree, x, 'llversionviewer.h') for x in ['llcommon', '../llcommon', '../../indra/llcommon.h']]
for p in paths:
if os.path.exists(p):
contents = open(p, 'r').read()
major = re.search("LL_VERSION_MAJOR\s=\s([0-9]+)", contents).group(1)
minor = re.search("LL_VERSION_MINOR\s=\s([0-9]+)", contents).group(1)
patch = re.search("LL_VERSION_PATCH\s=\s([0-9]+)", contents).group(1)
build = re.search("LL_VERSION_BUILD\s=\s([0-9]+)", contents).group(1)
return major, minor, patch, build
def get_channel(srctree):
# look up llversionserver.h and parse out the version info
paths = [os.path.join(srctree, x, 'llversionviewer.h') for x in ['llcommon', '../llcommon', '../../indra/llcommon.h']]
for p in paths:
if os.path.exists(p):
contents = open(p, 'r').read()
channel = re.search("LL_CHANNEL\s=\s\"(.+)\";\s*$", contents, flags = re.M).group(1)
return channel
DEFAULT_SRCTREE = os.path.dirname(sys.argv[0]) DEFAULT_SRCTREE = os.path.dirname(sys.argv[0])
DEFAULT_CHANNEL = 'Second Life Release' CHANNEL_VENDOR_BASE = 'Singularity'
DEFAULT_CHANNEL_SNOWGLOBE = 'Snowglobe Release' RELEASE_CHANNEL = CHANNEL_VENDOR_BASE + ' Release'
ARGUMENTS=[ ARGUMENTS=[
dict(name='actions', dict(name='actions',
@@ -121,27 +107,19 @@ ARGUMENTS=[
On Linux this would try to use Linux_i686Manifest.""", On Linux this would try to use Linux_i686Manifest.""",
default=""), default=""),
dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE), dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE),
dict(name='buildtype', description="""The build type used. ('Debug', 'Release', or 'RelWithDebInfo') dict(name='buildtype', description='Build type (i.e. Debug, Release, RelWithDebInfo).', default=None),
Default is Release """,
default="Release"),
dict(name='branding_id', description="""Identifier for the branding set to
use. Currently, 'secondlife' or 'snowglobe')""",
default='secondlife'),
dict(name='configuration', dict(name='configuration',
description="""The build configuration used. Only used on OS X for description="""The build configuration used.""",
now, but it could be used for other platforms as well.""", default="Release"),
default="Universal"),
dict(name='dest', description='Destination directory.', default=DEFAULT_SRCTREE), dict(name='dest', description='Destination directory.', default=DEFAULT_SRCTREE),
dict(name='grid', dict(name='grid',
description="""Which grid the client will try to connect to. Even description="""Which grid the client will try to connect to.""",
though it's not strictly a grid, 'firstlook' is also an acceptable default=None),
value for this parameter.""",
default=""),
dict(name='channel', dict(name='channel',
description="""The channel to use for updates, packaging, settings name, etc.""", description="""The channel to use for updates, packaging, settings name, etc.""",
default=get_channel), default='CHANNEL UNSET'),
dict(name='login_channel', dict(name='channel_suffix',
description="""The channel to use for login handshake/updates only.""", description="""Addition to the channel for packaging and channel value, but not application name (used internally)""",
default=None), default=None),
dict(name='installer_name', dict(name='installer_name',
description=""" The name of the file that the installer should be description=""" The name of the file that the installer should be
@@ -166,12 +144,12 @@ ARGUMENTS=[
contain the name of the final package in a form suitable contain the name of the final package in a form suitable
for use by a .bat file.""", for use by a .bat file.""",
default=None), default=None),
dict(name='version', dict(name='versionfile',
description="""This specifies the version of Second Life that is description="""The name of a file containing the full version number."""),
being packaged up.""", dict(name='signature',
default=get_default_version), description="""This specifies an identity to sign the viewer with, if any.
dict(name='extra_libraries', If no value is supplied, the default signature will be used, if any. Currently
description="""List of extra libraries to include in package""", only used on Mac OS X.""",
default=None) default=None)
] ]
@@ -193,8 +171,9 @@ def usage(srctree=""):
arg['description'] % nd) arg['description'] % nd)
def main(): def main():
print "cwd:", os.getcwd() ## import itertools
print " ".join(sys.argv) ## print ' '.join((("'%s'" % item) if ' ' in item else item)
## for item in itertools.chain([sys.executable], sys.argv))
option_names = [arg['name'] + '=' for arg in ARGUMENTS] option_names = [arg['name'] + '=' for arg in ARGUMENTS]
option_names.append('help') option_names.append('help')
options, remainder = getopt.getopt(sys.argv[1:], "", option_names) options, remainder = getopt.getopt(sys.argv[1:], "", option_names)
@@ -231,12 +210,17 @@ def main():
args[arg['name']] = default args[arg['name']] = default
# fix up version # fix up version
if isinstance(args.get('version'), str): if isinstance(args.get('versionfile'), str):
args['version'] = args['version'].split('.') try: # read in the version string
vf = open(args['versionfile'], 'r')
# default and agni are default args['version'] = vf.read().strip().split('.')
if args['grid'] in ['default', 'agni']: except:
args['grid'] = '' print "Unable to read versionfile '%s'" % args['versionfile']
raise
# unspecified, default, and agni are default
if args['grid'] in ['', 'default', 'agni']:
args['grid'] = None
if 'actions' in args: if 'actions' in args:
args['actions'] = args['actions'].split() args['actions'] = args['actions'].split()
@@ -245,15 +229,101 @@ def main():
for opt in args: for opt in args:
print "Option:", opt, "=", args[opt] print "Option:", opt, "=", args[opt]
# pass in sourceid as an argument now instead of an environment variable
try:
args['sourceid'] = os.environ["sourceid"]
except KeyError:
args['sourceid'] = ""
# Build base package.
touch = args.get('touch')
if touch:
print 'Creating base package'
args['package_id'] = "" # base package has no package ID
wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)
wm.do(*args['actions']) wm.do(*args['actions'])
# Store package file for later if making touched file.
base_package_file = ""
if touch:
print 'Created base package ', wm.package_file
base_package_file = "" + wm.package_file
# handle multiple packages if set
try:
additional_packages = os.environ["additional_packages"]
except KeyError:
additional_packages = ""
if additional_packages:
# Determine destination prefix / suffix for additional packages.
base_dest_postfix = args['dest']
base_dest_prefix = ""
base_dest_parts = args['dest'].split(os.sep)
if len(base_dest_parts) > 1:
base_dest_postfix = base_dest_parts[len(base_dest_parts) - 1]
base_dest_prefix = base_dest_parts[0]
i = 1
while i < len(base_dest_parts) - 1:
base_dest_prefix = base_dest_prefix + os.sep + base_dest_parts[i]
i = i + 1
# Determine touched prefix / suffix for additional packages.
base_touch_postfix = ""
base_touch_prefix = ""
if touch:
base_touch_postfix = touch
base_touch_parts = touch.split('/')
if "arwin" in args['platform']:
if len(base_touch_parts) > 1:
base_touch_postfix = base_touch_parts[len(base_touch_parts) - 1]
base_touch_prefix = base_touch_parts[0]
i = 1
while i < len(base_touch_parts) - 1:
base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i]
i = i + 1
else:
if len(base_touch_parts) > 2:
base_touch_postfix = base_touch_parts[len(base_touch_parts) - 2] + '/' + base_touch_parts[len(base_touch_parts) - 1]
base_touch_prefix = base_touch_parts[0]
i = 1
while i < len(base_touch_parts) - 2:
base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i]
i = i + 1
# Store base channel name.
base_channel_name = args['channel']
# Build each additional package.
package_id_list = additional_packages.split(" ")
args['channel'] = base_channel_name
for package_id in package_id_list:
try:
if package_id + "_viewer_channel_suffix" in os.environ:
args['channel_suffix'] = os.environ[package_id + "_viewer_channel_suffix"]
else:
args['channel_suffix'] = None
if package_id + "_sourceid" in os.environ:
args['sourceid'] = os.environ[package_id + "_sourceid"]
else:
args['sourceid'] = None
args['dest'] = base_dest_prefix + os.sep + package_id + os.sep + base_dest_postfix
except KeyError:
sys.stderr.write("Failed to create package for package_id: %s" % package_id)
sys.stderr.flush()
continue
if touch:
print 'Creating additional package for "', package_id, '" in ', args['dest']
wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)
wm.do(*args['actions'])
if touch:
print 'Created additional package ', wm.package_file, ' for ', package_id
faketouch = base_touch_prefix + '/' + package_id + '/' + base_touch_postfix
fp = open(faketouch, 'w')
fp.write('set package_file=%s\n' % wm.package_file)
fp.close()
# Write out the package file in this format, so that it can easily be called # Write out the package file in this format, so that it can easily be called
# and used in a .bat file - yeah, it sucks, but this is the simplest... # and used in a .bat file - yeah, it sucks, but this is the simplest...
touch = args.get('touch') touch = args.get('touch')
if touch: if touch:
fp = open(touch, 'w') fp = open(touch, 'w')
fp.write('set package_file=%s\n' % wm.package_file) fp.write('set package_file=%s\n' % base_package_file)
fp.close() fp.close()
print 'touched', touch print 'touched', touch
return 0 return 0
@@ -269,8 +339,8 @@ class LLManifest(object):
__metaclass__ = LLManifestRegistry __metaclass__ = LLManifestRegistry
manifests = {} manifests = {}
def for_platform(self, platform, arch = None): def for_platform(self, platform, arch = None):
if arch and platform != "windows": if arch:
platform = platform + '_' + arch platform = platform + '_' + arch + '_'
return self.manifests[platform.lower()] return self.manifests[platform.lower()]
for_platform = classmethod(for_platform) for_platform = classmethod(for_platform)
@@ -280,26 +350,15 @@ class LLManifest(object):
self.file_list = [] self.file_list = []
self.excludes = [] self.excludes = []
self.actions = [] self.actions = []
self.src_prefix = list([args['source']]) self.src_prefix = [args['source']]
self.artwork_prefix = list([args['artwork']]) self.artwork_prefix = [args['artwork']]
self.build_prefix = list([args['build']]) self.build_prefix = [args['build']]
self.alt_build_prefix = list([args['build']]) self.dst_prefix = [args['dest']]
self.dst_prefix = list([args['dest']])
self.created_paths = [] self.created_paths = []
self.package_name = "Unknown" self.package_name = "Unknown"
def default_grid(self):
return self.args.get('grid', None) == ''
def default_channel(self): def default_channel(self):
return self.args.get('channel', None) == DEFAULT_CHANNEL return self.args.get('channel', None) == RELEASE_CHANNEL
def default_channel_for_brand(self):
if self.viewer_branding_id()=='secondlife':
return self.args.get('channel', None) == DEFAULT_CHANNEL
elif self.viewer_branding_id()=="snowglobe":
return self.args.get('channel', None) == DEFAULT_CHANNEL_SNOWGLOBE
else:
return True
def construct(self): def construct(self):
""" Meant to be overriden by LLManifest implementors with code that """ Meant to be overriden by LLManifest implementors with code that
@@ -311,7 +370,7 @@ class LLManifest(object):
in the file list by path().""" in the file list by path()."""
self.excludes.append(glob) self.excludes.append(glob)
def prefix(self, src='', build=None, dst=None, alt_build=None): def prefix(self, src='', build=None, dst=None):
""" Pushes a prefix onto the stack. Until end_prefix is """ Pushes a prefix onto the stack. Until end_prefix is
called, all relevant method calls (esp. to path()) will prefix called, all relevant method calls (esp. to path()) will prefix
paths with the entire prefix stack. Source and destination paths with the entire prefix stack. Source and destination
@@ -322,15 +381,10 @@ class LLManifest(object):
dst = src dst = src
if build is None: if build is None:
build = src build = src
if alt_build is None:
alt_build = build
self.src_prefix.append(src) self.src_prefix.append(src)
self.artwork_prefix.append(src) self.artwork_prefix.append(src)
self.build_prefix.append(build) self.build_prefix.append(build)
self.dst_prefix.append(dst) self.dst_prefix.append(dst)
self.alt_build_prefix.append(alt_build)
return True # so that you can wrap it in an if to get indentation return True # so that you can wrap it in an if to get indentation
def end_prefix(self, descr=None): def end_prefix(self, descr=None):
@@ -343,30 +397,25 @@ class LLManifest(object):
src = self.src_prefix.pop() src = self.src_prefix.pop()
artwork = self.artwork_prefix.pop() artwork = self.artwork_prefix.pop()
build = self.build_prefix.pop() build = self.build_prefix.pop()
alt_build_prefix = self.alt_build_prefix.pop()
dst = self.dst_prefix.pop() dst = self.dst_prefix.pop()
if descr and not(src == descr or build == descr or dst == descr): if descr and not(src == descr or build == descr or dst == descr):
raise ValueError, "End prefix '" + descr + "' didn't match '" +src+ "' or '" +dst + "'" raise ValueError, "End prefix '" + descr + "' didn't match '" +src+ "' or '" +dst + "'"
def get_src_prefix(self): def get_src_prefix(self):
""" Returns the current source prefix.""" """ Returns the current source prefix."""
return os.path.relpath(os.path.normpath(os.path.join(*self.src_prefix))) return os.path.join(*self.src_prefix)
def get_artwork_prefix(self): def get_artwork_prefix(self):
""" Returns the current artwork prefix.""" """ Returns the current artwork prefix."""
return os.path.relpath(os.path.normpath(os.path.join(*self.artwork_prefix))) return os.path.join(*self.artwork_prefix)
def get_build_prefix(self): def get_build_prefix(self):
""" Returns the current build prefix.""" """ Returns the current build prefix."""
return os.path.relpath(os.path.normpath(os.path.join(*self.build_prefix))) return os.path.join(*self.build_prefix)
def get_alt_build_prefix(self):
""" Returns the current alternate source prefix."""
return os.path.relpath(os.path.normpath(os.path.join(*self.alt_build_prefix)))
def get_dst_prefix(self): def get_dst_prefix(self):
""" Returns the current destination prefix.""" """ Returns the current destination prefix."""
return os.path.relpath(os.path.normpath(os.path.join(*self.dst_prefix))) return os.path.join(*self.dst_prefix)
def src_path_of(self, relpath): def src_path_of(self, relpath):
"""Returns the full path to a file or directory specified """Returns the full path to a file or directory specified
@@ -383,25 +432,45 @@ class LLManifest(object):
relative to the destination directory.""" relative to the destination directory."""
return os.path.join(self.get_dst_prefix(), relpath) return os.path.join(self.get_dst_prefix(), relpath)
def ensure_src_dir(self, reldir):
"""Construct the path for a directory relative to the
source path, and ensures that it exists. Returns the
full path."""
path = os.path.join(self.get_src_prefix(), reldir)
self.cmakedirs(path)
return path
def ensure_dst_dir(self, reldir):
"""Construct the path for a directory relative to the
destination path, and ensures that it exists. Returns the
full path."""
path = os.path.join(self.get_dst_prefix(), reldir)
self.cmakedirs(path)
return path
def run_command(self, command): def run_command(self, command):
""" Runs an external command, and returns the output. Raises """ Runs an external command, and returns the output. Raises
an exception if the command reurns a nonzero status code. For an exception if the command returns a nonzero status code. For
debugging/informational purpoases, prints out the command's debugging/informational purposes, prints out the command's
output as it is received.""" output as it is received."""
print "Running command:", command print "Running command:", command
fd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) sys.stdout.flush()
child = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=True)
lines = [] lines = []
while True: while True:
lines.append(fd.stdout.readline()) lines.append(child.stdout.readline())
if lines[-1] == '': if lines[-1] == '':
break break
else: else:
print lines[-1].rstrip('\n'), print lines[-1],
output = ''.join(lines) output = ''.join(lines)
if fd.returncode: child.stdout.close()
raise RuntimeError( status = child.wait()
if status:
raise ManifestError(
"Command %s returned non-zero status (%s) \noutput:\n%s" "Command %s returned non-zero status (%s) \noutput:\n%s"
% (command, fd.returncode, output) ) % (command, status, output) )
return output return output
def created_path(self, path): def created_path(self, path):
@@ -409,14 +478,24 @@ class LLManifest(object):
a) verify that you really have created it a) verify that you really have created it
b) schedule it for cleanup""" b) schedule it for cleanup"""
if not os.path.exists(path): if not os.path.exists(path):
raise RuntimeError, "Should be something at path " + path raise ManifestError, "Should be something at path " + path
self.created_paths.append(path) self.created_paths.append(path)
def put_in_file(self, contents, dst): def put_in_file(self, contents, dst, src=None):
# write contents as dst # write contents as dst
f = open(self.dst_path_of(dst), "wb") dst_path = self.dst_path_of(dst)
f.write(contents) f = open(dst_path, "wb")
f.close() try:
f.write(contents)
finally:
f.close()
# Why would we create a file in the destination tree if not to include
# it in the installer? The default src=None (plus the fact that the
# src param is last) is to preserve backwards compatibility.
if src:
self.file_list.append([src, dst_path])
return dst_path
def replace_in(self, src, dst=None, searchdict={}): def replace_in(self, src, dst=None, searchdict={}):
if dst == None: if dst == None:
@@ -436,11 +515,7 @@ class LLManifest(object):
# ensure that destination path exists # ensure that destination path exists
self.cmakedirs(os.path.dirname(dst)) self.cmakedirs(os.path.dirname(dst))
self.created_paths.append(dst) self.created_paths.append(dst)
if not os.path.isdir(src): self.ccopymumble(src, dst)
self.ccopy(src,dst)
else:
# src is a dir
self.ccopytree(src,dst)
else: else:
print "Doesn't exist:", src print "Doesn't exist:", src
@@ -479,30 +554,29 @@ class LLManifest(object):
if method is not None: if method is not None:
method(src, dst) method(src, dst)
self.file_list.append([src, dst]) self.file_list.append([src, dst])
return [dst] return 1
else: else:
sys.stdout.write(" (excluding %r, %r)" % (src, dst)) sys.stdout.write(" (excluding %r, %r)" % (src, dst))
sys.stdout.flush() sys.stdout.flush()
return [] return 0
def process_directory(self, src, dst): def process_directory(self, src, dst):
if not self.includes(src, dst): if not self.includes(src, dst):
sys.stdout.write(" (excluding %r, %r)" % (src, dst)) sys.stdout.write(" (excluding %r, %r)" % (src, dst))
sys.stdout.flush() sys.stdout.flush()
return [] return 0
names = os.listdir(src) names = os.listdir(src)
self.cmakedirs(dst) self.cmakedirs(dst)
errors = [] errors = []
found_files = []
count = 0 count = 0
for name in names: for name in names:
srcname = os.path.join(src, name) srcname = os.path.join(src, name)
dstname = os.path.join(dst, name) dstname = os.path.join(dst, name)
if os.path.isdir(srcname): if os.path.isdir(srcname):
found_files.extend(self.process_directory(srcname, dstname)) count += self.process_directory(srcname, dstname)
else: else:
found_files.extend(self.process_file(srcname, dstname)) count += self.process_file(srcname, dstname)
return found_files return count
def includes(self, src, dst): def includes(self, src, dst):
if src: if src:
@@ -520,28 +594,38 @@ class LLManifest(object):
else: else:
os.remove(path) os.remove(path)
def ccopy(self, src, dst): def ccopymumble(self, src, dst):
""" Copy a single file or symlink. Uses filecmp to skip copying for existing files.""" """Copy a single symlink, file or directory."""
if os.path.islink(src): if os.path.islink(src):
linkto = os.readlink(src) linkto = os.readlink(src)
if os.path.islink(dst) or os.path.exists(dst): if os.path.islink(dst) or os.path.isfile(dst):
os.remove(dst) # because symlinking over an existing link fails os.remove(dst) # because symlinking over an existing link fails
elif os.path.isdir(dst):
shutil.rmtree(dst)
os.symlink(linkto, dst) os.symlink(linkto, dst)
elif os.path.isdir(src):
self.ccopytree(src, dst)
else: else:
# Don't recopy file if it's up-to-date. self.ccopyfile(src, dst)
# If we seem to be not not overwriting files that have been # XXX What about devices, sockets etc.?
# updated, set the last arg to False, but it will take longer. # YYY would we put such things into a viewer package?!
if os.path.exists(dst) and filecmp.cmp(src, dst, True):
return
# only copy if it's not excluded
if self.includes(src, dst):
try:
os.unlink(dst)
except OSError, err:
if err.errno != errno.ENOENT:
raise
shutil.copy2(src, dst) def ccopyfile(self, src, dst):
""" Copy a single file. Uses filecmp to skip copying for existing files."""
# Don't recopy file if it's up-to-date.
# If we seem to be not not overwriting files that have been
# updated, set the last arg to False, but it will take longer.
if os.path.exists(dst) and filecmp.cmp(src, dst, True):
return
# only copy if it's not excluded
if self.includes(src, dst):
try:
os.unlink(dst)
except OSError, err:
if err.errno != errno.ENOENT:
raise
shutil.copy2(src, dst)
def ccopytree(self, src, dst): def ccopytree(self, src, dst):
"""Direct copy of shutil.copytree with the additional """Direct copy of shutil.copytree with the additional
@@ -557,15 +641,11 @@ class LLManifest(object):
srcname = os.path.join(src, name) srcname = os.path.join(src, name)
dstname = os.path.join(dst, name) dstname = os.path.join(dst, name)
try: try:
if os.path.isdir(srcname): self.ccopymumble(srcname, dstname)
self.ccopytree(srcname, dstname)
else:
self.ccopy(srcname, dstname)
# XXX What about devices, sockets etc.?
except (IOError, os.error), why: except (IOError, os.error), why:
errors.append((srcname, dstname, why)) errors.append((srcname, dstname, why))
if errors: if errors:
raise RuntimeError, errors raise ManifestError, errors
def cmakedirs(self, path): def cmakedirs(self, path):
@@ -582,11 +662,25 @@ class LLManifest(object):
if os.path.exists(f): if os.path.exists(f):
return f return f
# didn't find it, return last item in list # didn't find it, return last item in list
if list: if len(list) > 0:
return list[-1] return list[-1]
else: else:
return None return None
def contents_of_tar(self, src_tar, dst_dir):
""" Extracts the contents of the tarfile (specified
relative to the source prefix) into the directory
specified relative to the destination directory."""
self.check_file_exists(src_tar)
tf = tarfile.open(self.src_path_of(src_tar), 'r')
for member in tf.getmembers():
tf.extract(member, self.ensure_dst_dir(dst_dir))
# TODO get actions working on these dudes, perhaps we should extract to a temporary directory and then process_directory on it?
self.file_list.append([src_tar,
self.dst_path_of(os.path.join(dst_dir,member.name))])
tf.close()
def wildcard_regex(self, src_glob, dst_glob): def wildcard_regex(self, src_glob, dst_glob):
src_re = re.escape(src_glob) src_re = re.escape(src_glob)
src_re = src_re.replace('\*', '([-a-zA-Z0-9._ ]*)') src_re = src_re.replace('\*', '([-a-zA-Z0-9._ ]*)')
@@ -597,7 +691,12 @@ class LLManifest(object):
i = i+1 i = i+1
return re.compile(src_re), dst_temp return re.compile(src_re), dst_temp
wildcard_pattern = re.compile('\*') def check_file_exists(self, path):
if not os.path.exists(path) and not os.path.islink(path):
raise MissingError("Path %s doesn't exist" % (os.path.abspath(path),))
wildcard_pattern = re.compile(r'\*')
def expand_globs(self, src, dst): def expand_globs(self, src, dst):
src_list = glob.glob(src) src_list = glob.glob(src)
src_re, d_template = self.wildcard_regex(src.replace('\\', '/'), src_re, d_template = self.wildcard_regex(src.replace('\\', '/'),
@@ -624,43 +723,53 @@ class LLManifest(object):
return self.path(os.path.join(path, file), file) 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.flush() sys.stdout.flush()
if src == None: if src == None:
raise RuntimeError("No source file, dst is " + dst) raise ManifestError("No source file, dst is " + dst)
if dst == None: if dst == None:
dst = src dst = src
dst = os.path.join(self.get_dst_prefix(), dst) dst = os.path.join(self.get_dst_prefix(), dst)
sys.stdout.write("Processing %s => %s ... " % (src, dst))
count = 0
is_glob = False
found_files = []
# look under each prefix for matching paths. Paths are normalized so './../blah' will match '../blah/../blah/' def try_path(src):
paths = set([os.path.normpath(os.path.join(self.get_src_prefix(), src)), # expand globs
os.path.normpath(os.path.join(self.get_artwork_prefix(), src)), count = 0
os.path.normpath(os.path.join(self.get_build_prefix(), src)), if self.wildcard_pattern.search(src):
os.path.normpath(os.path.join(self.get_alt_build_prefix(), src))] for s,d in self.expand_globs(src, dst):
)
for path in paths:
if self.wildcard_pattern.search(path):
is_glob = True
for s,d in self.expand_globs(path, dst):
assert(s != d) assert(s != d)
found_files.extend(self.process_file(s, d)) count += self.process_file(s, d)
else: else:
# if we're specifying a single path (not a glob),
# we should error out if it doesn't exist
self.check_file_exists(src)
# if it's a directory, recurse through it # if it's a directory, recurse through it
if os.path.isdir(path): if os.path.isdir(src):
found_files.extend(self.process_directory(path, dst)) count += self.process_directory(src, dst)
elif os.path.exists(path): else:
found_files.extend(self.process_file(path, dst)) count += self.process_file(src, dst)
return count
# if we're specifying a single path (not a glob), for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix():
# we should error out if it doesn't exist try:
if not found_files and not is_glob: count = try_path(os.path.join(pfx, src))
raise RuntimeError("No files match %s\n" % str(paths)) except MissingError:
# If src isn't a wildcard, and if that file doesn't exist in
# this pfx, try next pfx.
count = 0
continue
print "%d files" % len(found_files) # Here try_path() didn't raise MissingError. Did it process any files?
return found_files if count:
break
# Even though try_path() didn't raise MissingError, it returned 0
# files. src is probably a wildcard meant for some other pfx. Loop
# back to try the next.
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

Binary file not shown.

View File

@@ -1178,7 +1178,6 @@ if (DARWIN)
# Add resource files to the project. # Add resource files to the project.
set(viewer_RESOURCE_FILES set(viewer_RESOURCE_FILES
${VIEWER_BRANDING_ID}_icon.icns
macview.r macview.r
gpu_table.txt gpu_table.txt
SecondLife.nib/ SecondLife.nib/
@@ -1233,6 +1232,27 @@ if (WINDOWS)
list(APPEND viewer_SOURCE_FILES llviewerprecompiledheaders.cpp) list(APPEND viewer_SOURCE_FILES llviewerprecompiledheaders.cpp)
endif(USE_PRECOMPILED_HEADERS) endif(USE_PRECOMPILED_HEADERS)
# Replace the icons with the appropriate ones for the channel
# ('test' is the default)
set(ICON_PATH "default")
set(VIEWER_MACOSX_PHASE "d")
message("Copying icons for ${ICON_PATH}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/icons/${ICON_PATH}/viewer.ico"
"${CMAKE_CURRENT_SOURCE_DIR}/res/viewer_icon.ico"
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/icons/${ICON_PATH}/viewer_256.BMP"
"${CMAKE_CURRENT_SOURCE_DIR}/res/viewer_icon.BMP"
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/icons/${ICON_PATH}/viewer_256.BMP"
"${CMAKE_CURRENT_SOURCE_DIR}/res-sdl/viewer_icon.BMP"
)
# Add resource files to the project. # Add resource files to the project.
# viewerRes.rc is the only buildable file, but # viewerRes.rc is the only buildable file, but
# the rest are all dependencies of it. # the rest are all dependencies of it.
@@ -1261,7 +1281,9 @@ if (WINDOWS)
res/lltooltranslate.cur res/lltooltranslate.cur
res/lltoolzoomin.cur res/lltoolzoomin.cur
res/lltoolzoomout.cur res/lltoolzoomout.cur
res/${VIEWER_BRANDING_ID}_icon.ico res-sdl/viewer_icon.BMP
res/viewer_icon.BMP
res/viewer_icon.ico
res/resource.h res/resource.h
res/toolpickobject.cur res/toolpickobject.cur
res/toolpickobject2.cur res/toolpickobject2.cur
@@ -1513,16 +1535,15 @@ if (WINDOWS)
--actions=copy --actions=copy
--arch=${ARCH} --arch=${ARCH}
--artwork=${ARTWORK_DIR} --artwork=${ARTWORK_DIR}
--branding_id=${VIEWER_BRANDING_ID}
--build=${CMAKE_CURRENT_BINARY_DIR} --build=${CMAKE_CURRENT_BINARY_DIR}
--channel=${VIEWER_CHANNEL} --buildtype=${CMAKE_BUILD_TYPE}
--configuration=${CMAKE_CFG_INTDIR} --configuration=${CMAKE_CFG_INTDIR}
--dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
--grid=${GRID} --grid=${GRID}
--login_channel=${VIEWER_LOGIN_CHANNEL} --channel=${VIEWER_CHANNEL}
--versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--source=${CMAKE_CURRENT_SOURCE_DIR} --source=${CMAKE_CURRENT_SOURCE_DIR}
--touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat
--extra_libraries="${MANIFEST_LIBRARIES}"
DEPENDS DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
stage_third_party_libs stage_third_party_libs
@@ -1537,6 +1558,10 @@ if (WINDOWS)
if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts)
add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts) add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts)
endif (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) endif (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts)
add_dependencies(${VIEWER_BINARY_NAME}
SLPlugin
)
# sets the 'working directory' for debugging from visual studio. # sets the 'working directory' for debugging from visual studio.
if (NOT UNATTENDED) if (NOT UNATTENDED)
add_custom_command( add_custom_command(
@@ -1551,6 +1576,7 @@ if (WINDOWS)
COMMENT "Setting the ${VIEWER_BINARY_NAME} working directory for debugging." COMMENT "Setting the ${VIEWER_BINARY_NAME} working directory for debugging."
) )
endif (NOT UNATTENDED) endif (NOT UNATTENDED)
if (PACKAGE)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CFG_INTDIR}/touched.bat OUTPUT ${CMAKE_CFG_INTDIR}/touched.bat
@@ -1559,16 +1585,15 @@ if (WINDOWS)
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
--arch=${ARCH} --arch=${ARCH}
--artwork=${ARTWORK_DIR} --artwork=${ARTWORK_DIR}
--branding_id=${VIEWER_BRANDING_ID}
--build=${CMAKE_CURRENT_BINARY_DIR} --build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
--channel=${VIEWER_CHANNEL} --channel=${VIEWER_CHANNEL}
--versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--configuration=${CMAKE_CFG_INTDIR} --configuration=${CMAKE_CFG_INTDIR}
--dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
--grid=${GRID} --grid=${GRID}
--login_channel=${VIEWER_LOGIN_CHANNEL}
--source=${CMAKE_CURRENT_SOURCE_DIR} --source=${CMAKE_CURRENT_SOURCE_DIR}
--touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/touched.bat --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/touched.bat
--extra_libraries="${MANIFEST_LIBRARIES}"
DEPENDS DEPENDS
${VIEWER_BINARY_NAME} ${VIEWER_BINARY_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
@@ -1576,7 +1601,6 @@ if (WINDOWS)
${COPY_INPUT_DEPENDENCIES} ${COPY_INPUT_DEPENDENCIES}
) )
if (PACKAGE)
add_custom_target(llpackage ALL DEPENDS add_custom_target(llpackage ALL DEPENDS
${CMAKE_CFG_INTDIR}/touched.bat ${CMAKE_CFG_INTDIR}/touched.bat
windows-setup-build-all windows-setup-build-all
@@ -1709,7 +1733,7 @@ endif (LINUX)
if (DARWIN) if (DARWIN)
set(product ${VIEWER_BRANDING_NAME}) set(product ${VIEWER_BRANDING_NAME})
set(MACOSX_BUNDLE_INFO_STRING "A stable third-party Second Life viewer.") set(MACOSX_BUNDLE_INFO_STRING "A stable third-party Second Life viewer.")
set(MACOSX_BUNDLE_ICON_FILE "${VIEWER_BRANDING_ID}_icon.icns") set(MACOSX_BUNDLE_ICON_FILE "viewer.icns")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "${VIEWER_BRANDING_NAME}") set(MACOSX_BUNDLE_GUI_IDENTIFIER "${VIEWER_BRANDING_NAME}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${VIEWER_CHANNEL} ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${VIEWER_CHANNEL} ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}")
set(MACOSX_BUNDLE_BUNDLE_NAME "${VIEWER_BRANDING_NAME}") set(MACOSX_BUNDLE_BUNDLE_NAME "${VIEWER_BRANDING_NAME}")

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -222,10 +222,6 @@ void LLUserAuth::authenticate(
// * We append "64" to channel name on 64-bit for systems for the LL stats system to be able to produce independent // * We append "64" to channel name on 64-bit for systems for the LL stats system to be able to produce independent
// crash statistics depending on the architecture // crash statistics depending on the architecture
std::string chan(LLVersionInfo::getChannel()); std::string chan(LLVersionInfo::getChannel());
if (chan == "Singularity")
{
chan += " Release";
}
#if defined(_WIN64) || defined(__x86_64__) #if defined(_WIN64) || defined(__x86_64__)
chan += " 64"; chan += " 64";
#endif #endif

View File

@@ -60,8 +60,8 @@ END
// Icon with lowest ID value placed first to ensure application icon // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems. // remains consistent on all systems.
IDI_LL_ICON ICON "singularity_icon.ico" IDI_LL_ICON ICON "viewer_icon.ico"
IDI_LCD_LL_ICON ICON "singularity_icon.ico" IDI_LCD_LL_ICON ICON "viewer_icon.ico"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //

Binary file not shown.

Binary file not shown.

View File

@@ -33,254 +33,248 @@ $/LicenseInfo$
""" """
import sys import sys
import os.path import os.path
import errno
import re import re
import tarfile import tarfile
import time import time
import subprocess import random
import errno
viewer_dir = os.path.dirname(__file__) viewer_dir = os.path.dirname(__file__)
# add llmanifest library to our path so we don't have to muck with PYTHONPATH # Add indra/lib/python to our path so we don't have to muck with PYTHONPATH.
sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util')) # Put it FIRST because some of our build hosts have an ancient install of
from llmanifest import LLManifest, main, proper_windows_path, path_ancestors # indra.util.llmanifest under their system Python!
sys.path.insert(0, os.path.join(viewer_dir, os.pardir, "lib", "python"))
from indra.util.llmanifest import LLManifest, main, path_ancestors, CHANNEL_VENDOR_BASE, RELEASE_CHANNEL, ManifestError
try:
from llbase import llsd
except ImportError:
from indra.base import llsd
class ViewerManifest(LLManifest): class ViewerManifest(LLManifest):
def is_packaging_viewer(self):
# Some commands, files will only be included
# if we are packaging the viewer on windows.
# This manifest is also used to copy
# files during the build (see copy_w_viewer_manifest
# and copy_l_viewer_manifest targets)
return 'package' in self.args['actions']
def construct(self): def construct(self):
super(ViewerManifest, self).construct() super(ViewerManifest, self).construct()
self.exclude("*.svn*")
self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg")
self.path(src="../../etc/message.xml", dst="app_settings/message.xml") self.path(src="../../etc/message.xml", dst="app_settings/message.xml")
if self.prefix(src="app_settings"): if self.is_packaging_viewer():
self.exclude("logcontrol.xml") if self.prefix(src="app_settings"):
self.exclude("logcontrol-dev.xml") self.exclude("logcontrol.xml")
self.path("*.pem") self.exclude("logcontrol-dev.xml")
self.path("*.ini") self.path("*.pem")
self.path("*.xml") self.path("*.ini")
self.path("*.db2") self.path("*.xml")
self.path("*.db2")
# include the entire shaders directory recursively # include the entire shaders directory recursively
self.path("shaders") self.path("shaders")
# ... and the entire windlight directory # ... and the entire windlight directory
self.path("windlight") self.path("windlight")
# ... and the hunspell dictionaries # ... and the hunspell dictionaries
self.path("dictionaries") self.path("dictionaries")
self.end_prefix("app_settings") self.end_prefix("app_settings")
if self.prefix(src="character"): if self.prefix(src="character"):
self.path("*.llm") self.path("*.llm")
self.path("*.xml") self.path("*.xml")
self.path("*.tga")
self.end_prefix("character")
# Include our fonts
if self.prefix(src="fonts"):
self.path("*.ttf")
self.path("*.txt")
self.end_prefix("fonts")
# skins
if self.prefix(src="skins"):
self.path("paths.xml")
# include the entire textures directory recursively
if self.prefix(src="default/textures"):
self.path("*.tga") self.path("*.tga")
self.path("*.j2c") self.end_prefix("character")
self.path("*.jpg")
self.path("*.png")
self.path("textures.xml")
self.end_prefix("default/textures")
self.path("default/xui/*/*.xml")
self.path("Default.xml")
self.path("default/*.xml")
if self.prefix(src="dark/textures"):
self.path("*.tga")
self.path("*.j2c")
self.path("*.jpg")
self.path("*.png")
self.path("textures.xml")
self.end_prefix("dark/textures")
self.path("dark.xml")
self.path("dark/*.xml")
# Local HTML files (e.g. loading screen) # Include our fonts
if self.prefix(src="*/html"): if self.prefix(src="fonts"):
self.path("*.png") self.path("*.ttf")
self.path("*/*/*.html") self.path("*.txt")
self.path("*/*/*.gif") self.end_prefix("fonts")
self.end_prefix("*/html")
self.end_prefix("skins") # skins
if self.prefix(src="skins"):
self.path("paths.xml")
self.path("default/xui/*/*.xml")
self.path("Default.xml")
self.path("default/*.xml")
self.path("dark.xml")
self.path("dark/*.xml")
# include the entire textures directory recursively
if self.prefix(src="default/textures"):
self.path("*.tga")
self.path("*.j2c")
self.path("*.jpg")
self.path("*.png")
self.path("textures.xml")
self.end_prefix("default/textures")
if self.prefix(src="dark/textures"):
self.path("*.tga")
self.path("*.j2c")
self.path("*.jpg")
self.path("*.png")
self.path("textures.xml")
self.end_prefix("dark/textures")
# Files in the newview/ directory
self.path("gpu_table.txt")
def login_channel(self): # Local HTML files (e.g. loading screen)
"""Channel reported for login and upgrade purposes ONLY; if self.prefix(src="*/html"):
used for A/B testing""" self.path("*.png")
# NOTE: Do not return the normal channel if login_channel self.path("*/*/*.html")
# is not specified, as some code may branch depending on self.path("*/*/*.gif")
# whether or not this is present self.end_prefix("*/html")
return self.args.get('login_channel')
self.end_prefix("skins")
# Files in the newview/ directory
self.path("gpu_table.txt")
# The summary.json file gets left in the build directory by newview/CMakeLists.txt.
if not self.path2basename(os.pardir, "summary.json"):
print "No summary.json file"
def buildtype(self):
return self.args['buildtype']
def standalone(self): def standalone(self):
return self.args['standalone'] == "ON" return self.args['standalone'] == "ON"
def grid(self): def grid(self):
return self.args['grid'] return self.args['grid']
def channel(self): def channel(self):
return self.args['channel'] return self.args['channel']
def channel_unique(self):
return self.channel().replace("Second Life", "").strip()
def channel_oneword(self):
return "".join(self.channel_unique().split())
def channel_lowerword(self):
return self.channel_oneword().lower()
def viewer_branding_id(self):
return self.args['branding_id']
def installer_prefix(self):
return self.channel_oneword() + "_"
def flags_list(self): def channel_with_pkg_suffix(self):
""" Convenience function that returns the command-line flags fullchannel=self.channel()
for the grid""" if 'channel_suffix' in self.args and self.args['channel_suffix']:
fullchannel+=' '+self.args['channel_suffix']
return fullchannel
# Set command line flags relating to the target grid def channel_variant(self):
grid_flags = '' global CHANNEL_VENDOR_BASE
if not self.default_grid(): return self.channel().replace(CHANNEL_VENDOR_BASE, "").strip()
grid_flags = "--grid %(grid)s "\
"--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\
{'grid':self.grid()}
# set command line flags for channel def channel_type(self): # returns 'release', 'beta', 'project', or 'test'
channel_flags = '' global CHANNEL_VENDOR_BASE
if self.login_channel() and self.login_channel() != self.channel(): channel_qualifier=self.channel().replace(CHANNEL_VENDOR_BASE, "").lower().strip()
# Report a special channel during login, but use default if channel_qualifier.startswith('release'):
channel_flags = '--channel "%s"' % (self.login_channel()) channel_type='release'
elif channel_qualifier.startswith('beta'):
channel_type='beta'
elif channel_qualifier.startswith('project'):
channel_type='project'
else: else:
channel_flags = '--channel "%s"' % self.channel() channel_type='test'
return channel_type
# Deal with settings def channel_variant_app_suffix(self):
if self.default_channel() and self.default_grid(): # get any part of the compiled channel name after the CHANNEL_VENDOR_BASE
setting_flags = '' suffix=self.channel_variant()
elif self.default_grid(): # by ancient convention, we don't use Release in the app name
setting_flags = '--settings settings_%s.xml'\ if self.channel_type() == 'release':
% self.channel_lowerword() suffix=suffix.replace('Release', '').strip()
# for the base release viewer, suffix will now be null - for any other, append what remains
if len(suffix) > 0:
suffix = "_"+ ("_".join(suffix.split()))
# the additional_packages mechanism adds more to the installer name (but not to the app name itself)
if 'channel_suffix' in self.args and self.args['channel_suffix']:
suffix+='_'+("_".join(self.args['channel_suffix'].split()))
return suffix
def installer_base_name(self):
global CHANNEL_VENDOR_BASE
# a standard map of strings for replacing in the templates
substitution_strings = {
'channel_vendor_base' : '_'.join(CHANNEL_VENDOR_BASE.split()),
'channel_variant_underscores':self.channel_variant_app_suffix(),
'version_underscores' : '_'.join(self.args['version']),
'arch':self.args['arch']
}
return "%(channel_vendor_base)s%(channel_variant_underscores)s_%(version_underscores)s_%(arch)s" % substitution_strings
def app_name(self):
global CHANNEL_VENDOR_BASE
channel_type=self.channel_type()
if channel_type == 'release':
app_suffix='Viewer'
else: else:
setting_flags = '--settings settings_%s_%s.xml'\ app_suffix=self.channel_variant()
% (self.grid(), self.channel_lowerword()) return CHANNEL_VENDOR_BASE + ' ' + app_suffix
return " ".join((channel_flags, grid_flags, setting_flags)).strip() def app_name_oneword(self):
return ''.join(self.app_name().split())
def icon_path(self): def icon_path(self):
return "../../indra/newview/res/" return "icons/default"
def path_optional(self, src, dst=None): def extract_names(self,src):
"""
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.
"""
found_files = []
try: try:
found_files = self.path(src, dst) contrib_file = open(src,'r')
except RuntimeError, err: except IOError:
pass print "Failed to open '%s'" % src
if not found_files: raise
print "Skipping %s" % dst lines = contrib_file.readlines()
return found_files contrib_file.close()
def add_extra_libraries(self): # All lines up to and including the first blank line are the file header; skip them
found_libs = [] lines.reverse() # so that pop will pull from first to last line
config_arg = self.args['configuration'].lower() while not re.match("\s*$", lines.pop()) :
if(config_arg == '.'): pass # do nothing
config_arg = self.buildtype().lower()
if 'extra_libraries' in self.args and self.args['extra_libraries'] != '': # A line that starts with a non-whitespace character is a name; all others describe contributions, so collect the names
try: names = []
path_list = self.args['extra_libraries'].strip('"').split('|') for line in lines :
except: if re.match("\S", line) :
return None names.append(line.rstrip())
for cur_path in path_list: # It's not fair to always put the same people at the head of the list
if cur_path is None or cur_path == '': random.shuffle(names)
continue return ', '.join(names)
try:
config, file = cur_path.split(' ', 1)
except:
config, file = (None, None)
if(config == 'optimized'):
if(config_arg != 'release' and config_arg != 'relwithdebinfo' and config_arg != 'universal'):
continue
cur_path = file
if(config == 'debug'):
if(config_arg != 'debug'):
continue
cur_path = file
if(cur_path != ''):
if sys.platform == "linux" or sys.platform == "linux2":
found_libs += self.path_optional(cur_path+"*")
else:
found_libs += self.path_optional(cur_path)
return found_libs
class WindowsManifest(ViewerManifest): class WindowsManifest(ViewerManifest):
def is_win64(self): def is_win64(self):
return self.args.get('arch') == "x86_64" return self.args.get('arch') == "x86_64"
def final_exe(self): def final_exe(self):
return self.channel_oneword() + 'Viewer.exe' return self.app_name_oneword()+".exe"
def construct(self): def construct(self):
super(WindowsManifest, self).construct() super(WindowsManifest, self).construct()
# the final exe is complicated because we're not sure where it's coming from,
# nor do we have a fixed name for the executable pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) relpkgdir = os.path.join(pkgdir, "lib", "release")
debpkgdir = os.path.join(pkgdir, "lib", "debug")
if self.is_packaging_viewer():
# Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe.
self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe())
# Plugin host application # Plugin host application
self.path2basename(os.path.join(os.pardir, self.path2basename(os.path.join(os.pardir,
'llplugin', 'slplugin', self.args['configuration']), 'llplugin', 'slplugin', self.args['configuration']),
"SLplugin.exe") "SLplugin.exe")
# Get shared libs from the shared libs staging directory
if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
dst=""):
# Get llcommon and deps. If missing assume static linkage and continue. # Get llcommon and deps. If missing assume static linkage and continue.
if self.prefix(src=self.args['configuration'], dst=""):
try: try:
self.path('llcommon.dll') self.path('llcommon.dll')
self.path('libapr-1.dll')
self.path('libaprutil-1.dll')
self.path('libapriconv-1.dll')
except RuntimeError, err: except RuntimeError, err:
print err.message print err.message
print "Skipping llcommon.dll (assuming llcommon was linked statically)" print "Skipping llcommon.dll (assuming llcommon was linked statically)"
# Mesh 3rd party libs needed for auto LOD and collada reading
try: try:
self.path('libapr-1.dll') self.path("glod.dll")
self.path('libaprutil-1.dll')
self.path('libapriconv-1.dll')
except RuntimeError, err: except RuntimeError, err:
pass print err.message
print "Skipping GLOD library (assumming linked statically)"
# For mesh upload
if not self.is_win64():
self.path("libcollada14dom22.dll")
self.path("glod.dll")
self.add_extra_libraries()
if(self.prefix(src="..", dst="")):
found_files = self.path("msvc*.dll")
self.end_prefix()
if(not found_files):
try:
if self.prefix(src="msvcrt", dst=""):
self.path("*.dll")
self.path("*.manifest")
self.end_prefix()
except:
pass
# Vivox runtimes # Vivox runtimes
self.path("SLVoice.exe") self.path("SLVoice.exe")
@@ -296,15 +290,18 @@ class WindowsManifest(ViewerManifest):
self.path("ssleay32.dll") self.path("ssleay32.dll")
self.path("libeay32.dll") self.path("libeay32.dll")
# For spellchecking # Hunspell
self.path("libhunspell.dll") self.path("libhunspell.dll")
# For google-perftools tcmalloc allocator. # For google-perftools tcmalloc allocator.
if not self.is_win64(): try:
try: if self.args['configuration'].lower() == 'debug':
self.path('libtcmalloc_minimal-debug.dll')
else:
self.path('libtcmalloc_minimal.dll') self.path('libtcmalloc_minimal.dll')
except: except:
print "Skipping libtcmalloc_minimal.dll" print "Skipping libtcmalloc_minimal.dll"
self.end_prefix() self.end_prefix()
self.path(src="licenses-win32.txt", dst="licenses.txt") self.path(src="licenses-win32.txt", dst="licenses.txt")
@@ -361,7 +358,8 @@ class WindowsManifest(ViewerManifest):
self.end_prefix() self.end_prefix()
self.package_file = 'none' if not self.is_packaging_viewer():
self.package_file = "copied_deps"
def nsi_file_commands(self, install=True): def nsi_file_commands(self, install=True):
def wpath(path): def wpath(path):
@@ -372,7 +370,6 @@ class WindowsManifest(ViewerManifest):
result = "" result = ""
dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])] dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])]
dest_files = list(set(dest_files)) # remove duplicates
# sort deepest hierarchy first # sort deepest hierarchy first
dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
dest_files.reverse() dest_files.reverse()
@@ -380,15 +377,15 @@ class WindowsManifest(ViewerManifest):
for pkg_file in dest_files: for pkg_file in dest_files:
rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,'')) rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,''))
installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file))) installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file)))
pkg_file = wpath(os.path.join(os.pardir,os.path.normpath(pkg_file))) pkg_file = wpath(os.path.normpath(pkg_file))
if installed_dir != out_path: if installed_dir != out_path:
if install: if install:
out_path = installed_dir out_path = installed_dir
result += 'SetOutPath "' + out_path + '"\n' result += 'SetOutPath ' + out_path + '\n'
if install: if install:
result += 'File "' + pkg_file + '"\n' result += 'File ' + pkg_file + '\n'
else: else:
result += 'Delete "' + wpath(os.path.join('$INSTDIR', rel_file)) + '"\n' result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n'
# at the end of a delete, just rmdir all the directories # at the end of a delete, just rmdir all the directories
if not install: if not install:
@@ -407,28 +404,6 @@ class WindowsManifest(ViewerManifest):
prev = d prev = d
return result return result
def installer_file(self):
if self.is_win64():
mask = "%s_%s_x86-64_Setup.exe"
else:
mask = "%s_%s_Setup.exe"
return mask % (self.channel_oneword(), '-'.join(self.args['version']))
def sign_command(self, *argv):
return [
"signtool.exe",
"sign", "/v",
"/f",os.environ['VIEWER_SIGNING_KEY'],
"/p",os.environ['VIEWER_SIGNING_PASSWORD'],
"/d","%s" % self.channel(),
"/du",os.environ['VIEWER_SIGNING_URL'],
"/t","http://timestamp.comodoca.com/authenticode"
] + list(argv)
def sign(self, *argv):
subprocess.check_call(self.sign_command(*argv))
def package_finish(self): def package_finish(self):
# a standard map of strings for replacing in the templates # a standard map of strings for replacing in the templates
@@ -437,66 +412,43 @@ class WindowsManifest(ViewerManifest):
'version_short' : '.'.join(self.args['version'][:-1]), 'version_short' : '.'.join(self.args['version'][:-1]),
'version_dashes' : '-'.join(self.args['version']), 'version_dashes' : '-'.join(self.args['version']),
'final_exe' : self.final_exe(), 'final_exe' : self.final_exe(),
'grid':self.args['grid'], 'flags':'',
'grid_caps':self.args['grid'].upper(), 'app_name':self.app_name(),
# escape quotes becase NSIS doesn't handle them well 'app_name_oneword':self.app_name_oneword()
'flags':self.flags_list().replace('"', '$\\"'),
'channel':self.channel(),
'channel_oneword':self.channel_oneword(),
'channel_unique':self.channel_unique(),
'inst_name':self.channel_oneword() + ' (64 bit)' if self.is_win64() else self.channel_oneword(),
'installer_file':self.installer_file(),
'viewer_name': "%s%s" % (self.channel(), " (64 bit)" if self.is_win64() else "" ),
'install_icon': "install_icon_%s.ico" % self.viewer_branding_id(),
'uninstall_icon': "uninstall_icon_%s.ico" % self.viewer_branding_id(),
} }
installer_file = self.installer_base_name() + '_Setup.exe'
substitution_strings['installer_file'] = installer_file
version_vars = """ version_vars = """
!define INSTEXE "%(final_exe)s" !define INSTEXE "%(final_exe)s"
!define VERSION "%(version_short)s" !define VERSION "%(version_short)s"
!define VERSION_LONG "%(version)s" !define VERSION_LONG "%(version)s"
!define VERSION_DASHES "%(version_dashes)s" !define VERSION_DASHES "%(version_dashes)s"
""" % substitution_strings """ % substitution_strings
installer_file = "%(installer_file)s"
grid_vars_template = """
OutFile "%(installer_file)s"
!define VIEWERNAME "%(viewer_name)s"
!define INSTFLAGS "%(flags)s"
!define INSTNAME "%(inst_name)s"
!define SHORTCUT "%(viewer_name)s Viewer"
!define URLNAME "secondlife"
!define INSTALL_ICON "%(install_icon)s"
!define UNINSTALL_ICON "%(uninstall_icon)s"
!define AUTHOR "Linden Research, Inc." #TODO: Hook this up to cmake et al for easier branding.
Caption "${VIEWERNAME} ${VERSION_LONG}"
"""
if 'installer_name' in self.args:
installer_file = self.args['installer_name']
else:
installer_file = installer_file % substitution_strings
substitution_strings['installer_file'] = installer_file
# Sign the binaries if self.channel_type() == 'release':
if 'VIEWER_SIGNING_PASSWORD' in os.environ: substitution_strings['caption'] = CHANNEL_VENDOR_BASE
try: else:
self.sign(self.args['configuration']+"\\"+self.final_exe()) substitution_strings['caption'] = self.app_name() + ' ${VERSION}'
self.sign(self.args['configuration']+"\\SLPlugin.exe")
self.sign(self.args['configuration']+"\\SLVoice.exe") inst_vars_template = """
except Exception, e: OutFile "%(installer_file)s"
print "Couldn't sign binaries. Tried to sign %s" % self.args['configuration'] + "\\" + self.final_exe() + "\nException: %s" % e !define INSTNAME "%(app_name_oneword)s"
!define SHORTCUT "%(app_name)s"
!define URLNAME "secondlife"
Caption "%(caption)s"
"""
tempfile = "secondlife_setup_tmp.nsi" tempfile = "secondlife_setup_tmp.nsi"
# the following replaces strings in the nsi template # the following replaces strings in the nsi template
# it also does python-style % substitution # it also does python-style % substitution
self.replace_in("installers/windows/installer_template.nsi", tempfile, { self.replace_in("installers/windows/installer_template.nsi", tempfile, {
"%%VERSION%%":version_vars, "%%VERSION%%":version_vars,
"%%SOURCE%%":os.path.abspath(self.get_src_prefix()), "%%SOURCE%%":self.get_src_prefix(),
"%%GRID_VARS%%":grid_vars_template % substitution_strings, "%%INST_VARS%%":inst_vars_template % substitution_strings,
"%%INSTALL_FILES%%":self.nsi_file_commands(True), "%%INSTALL_FILES%%":self.nsi_file_commands(True),
"%%DELETE_FILES%%":self.nsi_file_commands(False), "%%DELETE_FILES%%":self.nsi_file_commands(False)})
"%%INSTALLDIR%%":"%s\\%s" % ('$PROGRAMFILES64' if self.is_win64() else '$PROGRAMFILES', self.channel_oneword()),
"%%WIN64_BIN_BUILD%%":"!define WIN64_BIN_BUILD 1" if self.is_win64() else "",
})
# We use the Unicode version of NSIS, available from # We use the Unicode version of NSIS, available from
# http://www.scratchpaper.com/ # http://www.scratchpaper.com/
@@ -511,29 +463,63 @@ class WindowsManifest(ViewerManifest):
except: except:
NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\Unicode\\makensis.exe' NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\Unicode\\makensis.exe'
self.run_command([proper_windows_path(NSIS_path),self.dst_path_of(tempfile)]) self.run_command([proper_windows_path(NSIS_path),self.dst_path_of(tempfile)])
# self.remove(self.dst_path_of(tempfile)) # self.remove(self.dst_path_of(tempfile))
# If we're on a build machine, sign the code using our Authenticode certificate. JC
# Sign the installer sign_py = os.path.expandvars("${SIGN}")
# We're probably not on a build machine, but maybe we want to sign if not sign_py or sign_py == "${SIGN}":
if 'VIEWER_SIGNING_PASSWORD' in os.environ: sign_py = 'C:\\buildscripts\\code-signing\\sign.py'
try:
self.sign(self.args['configuration'] + "\\" + substitution_strings['installer_file'])
except Exception, e:
print "Couldn't sign windows installer. Tried to sign %s" % self.args['configuration'] + "\\" + substitution_strings['installer_file'] + "\nException: %s" % e
else: else:
# If we're on a build machine, sign the code using our Authenticode certificate. JC sign_py = sign_py.replace('\\', '\\\\\\\\')
sign_py = os.path.expandvars("{SIGN_PY}") python = os.path.expandvars("${PYTHON}")
if sign_py == "" or sign_py == "{SIGN_PY}": if not python or python == "${PYTHON}":
sign_py = 'C:\\buildscripts\\code-signing\\sign.py' python = 'python'
if os.path.exists(sign_py): if os.path.exists(sign_py):
self.run_command('python ' + sign_py + ' ' + self.dst_path_of(installer_file)) self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\\', '\\\\\\\\')))
else: else:
print "Skipping code signing,", sign_py, "does not exist" print "Skipping code signing,", sign_py, "does not exist"
self.created_path(self.dst_path_of(installer_file)) self.created_path(self.dst_path_of(installer_file))
self.package_file = installer_file self.package_file = installer_file
class Windows_i686_Manifest(WindowsManifest):
def construct(self):
super(Windows_i686_Manifest, self).construct()
# Get shared libs from the shared libs staging directory
if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
dst=""):
# Get fmod studio dll, continue if missing
try:
if self.args['configuration'].lower() == 'debug':
self.path("fmodL.dll")
else:
self.path("fmod.dll")
except:
print "Skipping fmodstudio audio library(assuming other audio engine)"
self.end_prefix()
class Windows_x86_64_Manifest(WindowsManifest):
def construct(self):
super(Windows_x86_64_Manifest, self).construct()
# Get shared libs from the shared libs staging directory
if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
dst=""):
# Get fmodstudio dll, continue if missing
try:
if self.args['configuration'].lower() == 'debug':
self.path("fmodL64.dll")
else:
self.path("fmod64.dll")
except:
print "Skipping fmodstudio audio library(assuming other audio engine)"
self.end_prefix()
class DarwinManifest(ViewerManifest): class DarwinManifest(ViewerManifest):
def construct(self): def construct(self):
# copy over the build result (this is a no-op if run within the xcode script) # copy over the build result (this is a no-op if run within the xcode script)
@@ -906,9 +892,9 @@ class LinuxManifest(ViewerManifest):
self.run_command("find %(d)r/bin %(d)r/lib* -type f | xargs -d '\n' --no-run-if-empty strip --strip-unneeded" % {'d': self.get_dst_prefix()} ) self.run_command("find %(d)r/bin %(d)r/lib* -type f | xargs -d '\n' --no-run-if-empty strip --strip-unneeded" % {'d': self.get_dst_prefix()} )
self.run_command("find %(d)r/bin %(d)r/lib* -type f -not -name \\*.so | xargs -d '\n' --no-run-if-empty strip -s" % {'d': self.get_dst_prefix()} ) self.run_command("find %(d)r/bin %(d)r/lib* -type f -not -name \\*.so | xargs -d '\n' --no-run-if-empty strip -s" % {'d': self.get_dst_prefix()} )
class Linux_i686Manifest(LinuxManifest): class Linux_i686_Manifest(LinuxManifest):
def construct(self): def construct(self):
super(Linux_i686Manifest, self).construct() super(Linux_i686_Manifest, self).construct()
# llcommon # llcommon
if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"):
@@ -964,9 +950,9 @@ class Linux_i686Manifest(LinuxManifest):
self.path("libvivoxsdk.so") self.path("libvivoxsdk.so")
self.end_prefix("lib") self.end_prefix("lib")
class Linux_x86_64Manifest(LinuxManifest): class Linux_x86_64_Manifest(LinuxManifest):
def construct(self): def construct(self):
super(Linux_x86_64Manifest, self).construct() super(Linux_x86_64_Manifest, self).construct()
# llcommon # llcommon
if not self.path("../llcommon/libllcommon.so", "lib64/libllcommon.so"): if not self.path("../llcommon/libllcommon.so", "lib64/libllcommon.so"):