diff options
37 files changed, 1258 insertions, 542 deletions
| diff --git a/BuildParams b/BuildParams index cb908f1532..c5f96d5ee3 100755 --- a/BuildParams +++ b/BuildParams @@ -62,11 +62,6 @@ Linux.additional_packages = ""  EDU_sourceid = ""  EDU_viewer_channel_suffix = "edu" -# The EDU package allows us to create a separate release channel whose expirations -# are synchronized as much as possible with the academic year -EDU_sourceid = "" -EDU_viewer_channel_suffix = "edu" -  # Notifications - to configure email notices use the TeamCity parameter  # setting screen for your project or build configuration to set the  # environment variable 'email' to a space-separated list of email addresses @@ -95,23 +95,54 @@ pre_build()      && [ -r "$master_message_template_checkout/message_template.msg" ] \      && template_verifier_master_url="-DTEMPLATE_VERIFIER_MASTER_URL=file://$master_message_template_checkout/message_template.msg" -    # nat 2016-12-20: disable HAVOK on Mac until we get a 64-bit Mac build.      RELEASE_CRASH_REPORTING=ON      HAVOK=ON      SIGNING=() -    if [ "$arch" == "Darwin" ] +    if [ "$arch" == "Darwin" -a "$variant" == "Release" ] +    then SIGNING=("-DENABLE_SIGNING:BOOL=YES" \ +                  "-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.") +    fi + +    if [ "${RELEASE_CRASH_REPORTING:-}" != "OFF" ]      then -         if [ "$variant" == "Release" ] -         then SIGNING=("-DENABLE_SIGNING:BOOL=YES" \ -                       "-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.") +        case "$arch" in +            CYGWIN) +                symplat="windows" +                ;; +            Darwin) +                symplat="darwin" +                ;; +            Linux) +                symplat="linux" +                ;; +        esac +        # This name is consumed by indra/newview/CMakeLists.txt. Make it +        # absolute because we've had troubles with relative pathnames. +        abs_build_dir="$(cd "$build_dir"; pwd)" +        VIEWER_SYMBOL_FILE="$(native_path "$abs_build_dir/newview/$variant/secondlife-symbols-$symplat-${AUTOBUILD_ADDRSIZE}.tar.bz2")" +    fi + +    # don't spew credentials into build log +    bugsplat_sh="$build_secrets_checkout/bugsplat/bugsplat.sh" +    set +x +    if [ -r "$bugsplat_sh" ] +    then # show that we're doing this, just not the contents +         echo source "$bugsplat_sh" +         source "$bugsplat_sh" +         # important: we test this and use its value in [grand-]child processes +         if [ -n "${BUGSPLAT_DB:-}" ] +         then echo export BUGSPLAT_DB +              export BUGSPLAT_DB           fi      fi +    set -x      "$autobuild" configure --quiet -c $variant -- \       -DPACKAGE:BOOL=ON \ -     -DUNATTENDED:BOOL=ON \       -DHAVOK:BOOL="$HAVOK" \       -DRELEASE_CRASH_REPORTING:BOOL="$RELEASE_CRASH_REPORTING" \ +     -DVIEWER_SYMBOL_FILE:STRING="${VIEWER_SYMBOL_FILE:-}" \ +     -DBUGSPLAT_DB:STRING="${BUGSPLAT_DB:-}" \       -DVIEWER_CHANNEL:STRING="${viewer_channel}" \       -DGRID:STRING="\"$viewer_grid\"" \       -DTEMPLATE_VERIFIER_OPTIONS:STRING="$template_verifier_options" $template_verifier_master_url \ @@ -193,6 +224,8 @@ then      exit 1  fi +shopt -s nullglob # if nothing matches a glob, expand to nothing +  initialize_build # provided by master buildscripts build.sh  begin_section "autobuild initialize" @@ -232,7 +265,6 @@ initialize_version # provided by buildscripts build.sh; sets version id  # Now run the build  succeeded=true -build_processes=  last_built_variant=  for variant in $variants  do @@ -240,7 +272,6 @@ do    last_built_variant="$variant"    build_dir=`build_dir_$arch $variant` -  build_dir_stubs="$build_dir/win_setup/$variant"    begin_section "Initialize $variant Build Directory"    rm -rf "$build_dir" @@ -417,19 +448,7 @@ then            if [ "${RELEASE_CRASH_REPORTING:-}" != "OFF" ]            then                # Upload crash reporter file -              # These names must match the set of VIEWER_SYMBOL_FILE in indra/newview/CMakeLists.txt -              case "$arch" in -                  CYGWIN) -                      symbolfile="$build_dir/newview/Release/secondlife-symbols-windows-${AUTOBUILD_ADDRSIZE}.tar.bz2" -                      ;; -                  Darwin) -                      symbolfile="$build_dir/newview/Release/secondlife-symbols-darwin-${AUTOBUILD_ADDRSIZE}.tar.bz2" -                      ;; -                  Linux) -                      symbolfile="$build_dir/newview/Release/secondlife-symbols-linux-${AUTOBUILD_ADDRSIZE}.tar.bz2" -                      ;; -              esac -              python_cmd "$helpers/codeticket.py" addoutput "Symbolfile" "$symbolfile" \ +              python_cmd "$helpers/codeticket.py" addoutput "Symbolfile" "$VIEWER_SYMBOL_FILE" \                    || fatal "Upload of symbolfile failed"            fi diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index a40b2c0846..6c20a813ba 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -44,6 +44,7 @@ if (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts)  endif (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts)  add_custom_target(viewer) +  add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger)  add_subdirectory(${LIBS_OPEN_PREFIX}llplugin)  add_subdirectory(${LIBS_OPEN_PREFIX}llui) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4a3ebe4835..84e1c5d6fd 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -12,6 +12,7 @@ set(cmake_SOURCE_FILES      Audio.cmake      BerkeleyDB.cmake      Boost.cmake +    bugsplat.cmake      BuildVersion.cmake      CEFPlugin.cmake      CEFPlugin.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 09a97fc03e..dde53835fb 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -49,6 +49,20 @@ if(WINDOWS)          libhunspell.dll          ) +    # Filenames are different for 32/64 bit BugSplat file and we don't +    # have any control over them so need to branch. +    if (BUGSPLAT_DB) +      if(ADDRESS_SIZE EQUAL 32) +        set(release_files ${release_files} BugSplat.dll) +        set(release_files ${release_files} BugSplatRc.dll) +        set(release_files ${release_files} BsSndRpt.exe) +      else(ADDRESS_SIZE EQUAL 32) +        set(release_files ${release_files} BugSplat64.dll) +        set(release_files ${release_files} BugSplatRc64.dll) +        set(release_files ${release_files} BsSndRpt64.exe) +      endif(ADDRESS_SIZE EQUAL 32) +    endif (BUGSPLAT_DB) +      if (FMODEX)          if(ADDRESS_SIZE EQUAL 32) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 024bfe14a1..b3f42c1a5e 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,4 +1,5 @@  # -*- cmake -*- +include(00-Common)  include(LLTestCommand)  include(GoogleMock)  include(Tut) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index bd69c49856..2b54cd4155 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -33,6 +33,8 @@ set(INTEGRATION_TESTS_PREFIX)  set(LL_TESTS ON CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation")  set(INCREMENTAL_LINK OFF CACHE BOOL "Use incremental linking on win32 builds (enable for faster links on some machines)")  set(ENABLE_MEDIA_PLUGINS ON CACHE BOOL "Turn off building media plugins if they are imported by third-party library mechanism") +set(VIEWER_SYMBOL_FILE "" CACHE STRING "Name of tarball into which to place symbol files") +set(BUGSPLAT_DB "" CACHE STRING "BugSplat database name, if BugSplat crash reporting is desired")  if(LIBS_CLOSED_DIR)    file(TO_CMAKE_PATH "${LIBS_CLOSED_DIR}" LIBS_CLOSED_DIR) @@ -209,7 +211,6 @@ set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if  set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside")  set(USESYSTEMLIBS OFF CACHE BOOL "Use libraries from your system rather than Linden-supplied prebuilt libraries.") -set(UNATTENDED OFF CACHE BOOL "Should be set to ON for building with VC Express editions.")  set(USE_PRECOMPILED_HEADERS ON CACHE BOOL "Enable use of precompiled header directives where supported.") diff --git a/indra/cmake/bugsplat.cmake b/indra/cmake/bugsplat.cmake new file mode 100644 index 0000000000..59644b73ce --- /dev/null +++ b/indra/cmake/bugsplat.cmake @@ -0,0 +1,25 @@ +# BugSplat is engaged by setting BUGSPLAT_DB to the target BugSplat database +# name. +if (BUGSPLAT_DB) +  if (USESYSTEMLIBS) +    message(STATUS "Looking for system BugSplat") +    set(BUGSPLAT_FIND_QUIETLY ON) +    set(BUGSPLAT_FIND_REQUIRED ON) +    include(FindBUGSPLAT) +  else (USESYSTEMLIBS) +    message(STATUS "Engaging autobuild BugSplat") +    include(Prebuilt) +    use_prebuilt_binary(bugsplat) +    if (WINDOWS) +      set(BUGSPLAT_LIBRARIES  +        ${ARCH_PREBUILT_DIRS_RELEASE}/bugsplat.lib +        ) +    elseif (DARWIN) +      find_library(BUGSPLAT_LIBRARIES BugsplatMac +        PATHS "${ARCH_PREBUILT_DIRS_RELEASE}") +    else (WINDOWS) + +    endif (WINDOWS) +    set(BUGSPLAT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/bugsplat) +  endif (USESYSTEMLIBS) +endif (BUGSPLAT_DB) diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index 13cf1f7bde..d9353f904c 100644 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -40,7 +40,7 @@ add_executable(llimage_libtest      WIN32      MACOSX_BUNDLE      ${llimage_libtest_SOURCE_FILES} -) +    )  set_target_properties(llimage_libtest      PROPERTIES diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 598261e3d7..9569014a47 100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -33,13 +33,14 @@ import filecmp  import fnmatch  import getopt  import glob +import itertools +import operator  import os  import re  import shutil +import subprocess  import sys  import tarfile -import errno -import subprocess  class ManifestError(RuntimeError):      """Use an exception more specific than generic Python RuntimeError""" @@ -49,7 +50,9 @@ class ManifestError(RuntimeError):  class MissingError(ManifestError):      """You specified a file that doesn't exist""" -    pass +    def __init__(self, msg): +        self.msg = msg +        super(MissingError, self).__init__(self.msg)  def path_ancestors(path):      drive, path = os.path.splitdrive(os.path.normpath(path)) @@ -90,7 +93,7 @@ DEFAULT_SRCTREE = os.path.dirname(sys.argv[0])  CHANNEL_VENDOR_BASE = 'Second Life'  RELEASE_CHANNEL = CHANNEL_VENDOR_BASE + ' Release' -ARGUMENTS=[ +BASE_ARGUMENTS=[      dict(name='actions',           description="""This argument specifies the actions that are to be taken when the          script is run.  The meaningful actions are currently: @@ -108,8 +111,19 @@ ARGUMENTS=[          Example use: %(name)s --arch=i686          On Linux this would try to use Linux_i686Manifest.""",           default=""), +    dict(name='artwork', description='Artwork directory.', default=DEFAULT_SRCTREE),      dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE),      dict(name='buildtype', description='Build type (i.e. Debug, Release, RelWithDebInfo).', default=None), +    dict(name='bundleid', +         description="""The Mac OS X Bundle identifier.""", +         default="com.secondlife.indra.viewer"), +    dict(name='channel', +         description="""The channel to use for updates, packaging, settings name, etc.""", +         default='CHANNEL UNSET'), +    dict(name='channel_suffix', +         description="""Addition to the channel for packaging and channel value, +         but not application name (used internally)""", +         default=None),      dict(name='configuration',           description="""The build configuration used.""",           default="Release"), @@ -117,12 +131,6 @@ ARGUMENTS=[      dict(name='grid',           description="""Which grid the client will try to connect to.""",           default=None), -    dict(name='channel', -         description="""The channel to use for updates, packaging, settings name, etc.""", -         default='CHANNEL UNSET'), -    dict(name='channel_suffix', -         description="""Addition to the channel for packaging and channel value, but not application name (used internally)""", -         default=None),      dict(name='installer_name',           description=""" The name of the file that the installer should be          packaged up into. Only used on Linux at the moment.""", @@ -134,10 +142,14 @@ ARGUMENTS=[           description="""The current platform, to be used for looking up which          manifest class to run.""",           default=get_default_platform), +    dict(name='signature', +         description="""This specifies an identity to sign the viewer with, if any. +        If no value is supplied, the default signature will be used, if any. Currently +        only used on Mac OS X.""", +         default=None),      dict(name='source',           description='Source directory.',           default=DEFAULT_SRCTREE), -    dict(name='artwork', description='Artwork directory.', default=DEFAULT_SRCTREE),      dict(name='touch',           description="""File to touch when action is finished. Touch file will          contain the name of the final package in a form suitable @@ -145,23 +157,15 @@ ARGUMENTS=[           default=None),      dict(name='versionfile',           description="""The name of a file containing the full version number."""), -    dict(name='bundleid', -         description="""The Mac OS X Bundle identifier.""", -         default="com.secondlife.indra.viewer"), -    dict(name='signature', -         description="""This specifies an identity to sign the viewer with, if any. -        If no value is supplied, the default signature will be used, if any. Currently -        only used on Mac OS X.""", -         default=None)      ] -def usage(srctree=""): +def usage(arguments, srctree=""):      nd = {'name':sys.argv[0]}      print """Usage:      %(name)s [options] [destdir]      Options:      """ % nd -    for arg in ARGUMENTS: +    for arg in arguments:          default = arg['default']          if hasattr(default, '__call__'):              default = "(computed value) \"" + str(default(srctree)) + '"' @@ -172,11 +176,15 @@ def usage(srctree=""):              default,              arg['description'] % nd) -def main(): -##  import itertools +def main(extra=[]):  ##  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] +    # Supplement our default command-line switches with any desired by +    # application-specific caller. +    arguments = list(itertools.chain(BASE_ARGUMENTS, extra)) +    # Alphabetize them by option name in case we display usage. +    arguments.sort(key=operator.itemgetter('name')) +    option_names = [arg['name'] + '=' for arg in arguments]      option_names.append('help')      options, remainder = getopt.getopt(sys.argv[1:], "", option_names) @@ -199,11 +207,11 @@ def main():      # early out for help      if 'help' in args:          # *TODO: it is a huge hack to pass around the srctree like this -        usage(args['source']) +        usage(arguments, srctree=args['source'])          return      # defaults -    for arg in ARGUMENTS: +    for arg in arguments:          if arg['name'] not in args:              default = arg['default']              if hasattr(default, '__call__'): @@ -232,104 +240,68 @@ def main():          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'] = "" +    args['sourceid'] = os.environ.get("sourceid", "")      # Build base package.      touch = args.get('touch')      if touch: -        print 'Creating base package' -    args['package_id'] = "" # base package has no package ID +        print '================ Creating base package' +    else: +        print '================ Starting base copy'      wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)      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 +        print '================ Created base package ', wm.package_file          base_package_file = "" + wm.package_file +    else: +        print '================ Finished base copy'      # handle multiple packages if set -    try: -        additional_packages = os.environ["additional_packages"] -    except KeyError: -        additional_packages = "" +    # ''.split() produces empty list +    additional_packages = os.environ.get("additional_packages", "").split()      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 +        base_dest_parts = list(os.path.split(args['dest'])) +        base_dest_parts.insert(-1, "{}") +        base_dest_template = os.path.join(*base_dest_parts)          # Determine touched prefix / suffix for additional packages. -        base_touch_postfix = "" -        base_touch_prefix = ""          if touch: -            base_touch_postfix = touch -            base_touch_parts = touch.split('/') +            base_touch_parts = list(os.path.split(touch)) +            # Because of the special insert() logic below, we don't just want +            # [dirpath, basename]; we want [dirpath, directory, basename]. +            # Further split the dirpath and replace it in the list. +            base_touch_parts[0:1] = os.path.split(base_touch_parts[0])              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 +                base_touch_parts.insert(-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 +                base_touch_parts.insert(-2, "{}") +            base_touch_template = os.path.join(*base_touch_parts) +        for package_id in additional_packages: +            args['channel_suffix'] = os.environ.get(package_id + "_viewer_channel_suffix") +            args['sourceid']       = os.environ.get(package_id + "_sourceid") +            args['dest'] = base_dest_template.format(package_id)              if touch: -                print 'Creating additional package for "', package_id, '" in ', args['dest'] +                print '================ Creating additional package for "', package_id, '" in ', args['dest'] +            else: +                print '================ Starting additional copy for "', package_id, '" in ', args['dest']              try:                  wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)                  wm.do(*args['actions'])              except Exception as err:                  sys.exit(str(err))              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() -     +                print '================ Created additional package ', wm.package_file, ' for ', package_id +                with open(base_touch_template.format(package_id), 'w') as fp: +                    fp.write('set package_file=%s\n' % wm.package_file) +            else: +                print '================ Finished additional copy "', package_id, '" in ', args['dest']      # 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... -    touch = args.get('touch')      if touch: -        fp = open(touch, 'w') -        fp.write('set package_file=%s\n' % base_package_file) -        fp.close() +        with open(touch, 'w') as fp: +            fp.write('set package_file=%s\n' % base_package_file)          print 'touched', touch      return 0 @@ -375,7 +347,7 @@ class LLManifest(object):          in the file list by path()."""          self.excludes.append(glob) -    def prefix(self, src='', build=None, dst=None): +    def prefix(self, src='', build='', dst='', src_dst=None):          """          Usage: @@ -385,8 +357,21 @@ class LLManifest(object):          For the duration of the 'with' block, pushes a prefix onto the stack.          Within that block, all relevant method calls (esp. to path()) will          prefix paths with the entire prefix stack. Source and destination -        prefixes can be different, though if only one is provided they are -        both equal. To specify a no-op, use an empty string, not None. +        prefixes are independent; if omitted (or passed as the empty string), +        the prefix has no effect. Thus: + +        with self.prefix(src='foo'): +            # no effect on dst + +        with self.prefix(dst='bar'): +            # no effect on src + +        If you want to set both at once, use src_dst: + +        with self.prefix(src_dst='subdir'): +            # same as self.prefix(src='subdir', dst='subdir') +            # Passing src_dst makes any src or dst argument in the same +            # parameter list irrelevant.          Also supports the older (pre-Python-2.5) syntax: @@ -400,34 +385,42 @@ class LLManifest(object):          returned True specifically so that the caller could indent the          relevant block of code with 'if', just for aesthetic purposes.          """ -        if dst is None: -            dst = src -        if build is None: -            build = src +        if src_dst is not None: +            src = src_dst +            dst = src_dst          self.src_prefix.append(src)          self.artwork_prefix.append(src)          self.build_prefix.append(build)          self.dst_prefix.append(dst) +##      self.display_stacks() +          # The above code is unchanged from the original implementation. What's          # new is the return value. We're going to return an instance of          # PrefixManager that binds this LLManifest instance and Does The Right          # Thing on exit.          return self.PrefixManager(self) +    def display_stacks(self): +        width = 1 + max(len(stack) for stack in self.PrefixManager.stacks) +        for stack in self.PrefixManager.stacks: +            print "{} {}".format((stack + ':').ljust(width), +                                 os.path.join(*getattr(self, stack))) +      class PrefixManager(object): +        # stack attributes we manage in this LLManifest (sub)class +        # instance +        stacks = ("src_prefix", "artwork_prefix", "build_prefix", "dst_prefix") +          def __init__(self, manifest):              self.manifest = manifest -            # stack attributes we manage in this LLManifest (sub)class -            # instance -            stacks = ("src_prefix", "artwork_prefix", "build_prefix", "dst_prefix")              # If the caller wrote:              # with self.prefix(...):              # as intended, then bind the state of each prefix stack as it was              # just BEFORE the call to prefix(). Since prefix() appended an              # entry to each prefix stack, capture len()-1.              self.prevlen = { stack: len(getattr(self.manifest, stack)) - 1 -                             for stack in stacks } +                             for stack in self.stacks }          def __nonzero__(self):              # If the caller wrote: @@ -460,6 +453,8 @@ class LLManifest(object):                  # truncate that list back to 'prevlen'                  del getattr(self.manifest, stack)[prevlen:] +##          self.manifest.display_stacks() +      def end_prefix(self, descr=None):          """Pops a prefix off the stack.  If given an argument, checks          the argument against the top of the stack.  If the argument @@ -505,6 +500,19 @@ class LLManifest(object):          relative to the destination directory."""          return os.path.join(self.get_dst_prefix(), relpath) +    def _relative_dst_path(self, dstpath): +        """ +        Returns the path to a file or directory relative to the destination directory. +        This should only be used for generating diagnostic output in the path method. +        """ +        dest_root=self.dst_prefix[0] +        if dstpath.startswith(dest_root+os.path.sep): +            return dstpath[len(dest_root)+1:] +        elif dstpath.startswith(dest_root): +            return dstpath[len(dest_root):] +        else: +            return dstpath +      def ensure_src_dir(self, reldir):          """Construct the path for a directory relative to the          source path, and ensures that it exists.  Returns the @@ -609,7 +617,6 @@ class LLManifest(object):      def process_file(self, src, dst):          if self.includes(src, dst): -#            print src, "=>", dst              for action in self.actions:                  methodname = action + "_action"                  method = getattr(self, methodname, None) @@ -677,7 +684,11 @@ class LLManifest(object):          # 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. +##      reldst = (dst[len(self.dst_prefix[0]):] +##                if dst.startswith(self.dst_prefix[0]) +##                else dst).lstrip(r'\/')          if os.path.exists(dst) and filecmp.cmp(src, dst, True): +##          print "{} (skipping, {} exists)".format(src, reldst)              return          # only copy if it's not excluded          if self.includes(src, dst): @@ -687,6 +698,7 @@ class LLManifest(object):                  if err.errno != errno.ENOENT:                      raise +##          print "{} => {}".format(src, reldst)              shutil.copy2(src, dst)      def ccopytree(self, src, dst): @@ -785,13 +797,13 @@ class LLManifest(object):          return self.path(os.path.join(path, file), file)      def path(self, src, dst=None): -        sys.stdout.write("Processing %s => %s ... " % (src, dst))          sys.stdout.flush()          if src == None:              raise ManifestError("No source file, dst is " + dst)          if dst == None:              dst = src          dst = os.path.join(self.get_dst_prefix(), dst) +        sys.stdout.write("Processing %s => %s ... " % (src, self._relative_dst_path(dst)))          def try_path(src):              # expand globs @@ -811,22 +823,18 @@ class LLManifest(object):                      count += self.process_file(src, dst)              return count -        for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix(): +        try_prefixes = [self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix()] +        tried=[] +        count=0 +        while not count and try_prefixes:  +            pfx = try_prefixes.pop(0)              try:                  count = try_path(os.path.join(pfx, src))              except MissingError: -                # If src isn't a wildcard, and if that file doesn't exist in -                # this pfx, try next pfx. -                count = 0 -                continue - -            # Here try_path() didn't raise MissingError. Did it process any 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. - +                tried.append(pfx) +                if not try_prefixes: +                    # no more prefixes left to try +                    print "unable to find '%s'; looked in:\n  %s" % (src, '\n  '.join(tried))          print "%d files" % count          # Let caller check whether we processed as many files as expected. In diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index 029096df37..315aed8d11 100644 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -78,4 +78,4 @@ target_link_libraries(linux-crash-logger      )  add_custom_target(linux-crash-logger-target ALL -                  DEPENDS linux-crash-logger) +    DEPENDS linux-crash-logger) diff --git a/indra/llappearance/lllocaltextureobject.cpp b/indra/llappearance/lllocaltextureobject.cpp index f49cf21512..3f564ec3de 100644 --- a/indra/llappearance/lllocaltextureobject.cpp +++ b/indra/llappearance/lllocaltextureobject.cpp @@ -210,4 +210,3 @@ void LLLocalTextureObject::setBakedReady(BOOL ready)  {  	mIsBakedReady = ready;  } - diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index d9eb13d65a..42ad56f1b0 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -255,6 +255,11 @@ set(llcommon_HEADER_FILES  set_source_files_properties(${llcommon_HEADER_FILES}                              PROPERTIES HEADER_FILE_ONLY TRUE) +if (BUGSPLAT_DB) +  set_source_files_properties(llapp.cpp +    PROPERTIES COMPILE_DEFINITIONS "LL_BUGSPLAT") +endif (BUGSPLAT_DB) +  list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})  if(LLCOMMON_LINK_SHARED) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 6cc9e804d4..421af3006e 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -392,7 +392,7 @@ void LLApp::setupErrorHandling(bool second_instance)  #if LL_WINDOWS -#if LL_SEND_CRASH_REPORTS +#if LL_SEND_CRASH_REPORTS && ! defined(LL_BUGSPLAT)  	EnableCrashingOnCrashes();  	// This sets a callback to handle w32 signals to the console window. @@ -454,8 +454,15 @@ void LLApp::setupErrorHandling(bool second_instance)  			mExceptionHandler->set_handle_debug_exceptions(true);  		}  	} -#endif -#else +#endif // LL_SEND_CRASH_REPORTS && ! defined(LL_BUGSPLAT) +#else  // ! LL_WINDOWS + +#if defined(LL_BUGSPLAT) +	// Don't install our own signal handlers -- BugSplat needs to hook them, +	// or it's completely ineffectual. +	bool installHandler = false; + +#else // ! LL_BUGSPLAT  	//  	// Start up signal handling.  	// @@ -463,9 +470,11 @@ void LLApp::setupErrorHandling(bool second_instance)  	// thread, asynchronous signals can be delivered to any thread (in theory)  	//  	setup_signals(); -	 +  	// Add google breakpad exception handler configured for Darwin/Linux.  	bool installHandler = true; +#endif // ! LL_BUGSPLAT +  #if LL_DARWIN  	// For the special case of Darwin, we do not want to install the handler if  	// the process is being debugged as the app will exit with value ABRT (6) if @@ -498,7 +507,7 @@ void LLApp::setupErrorHandling(bool second_instance)  		// installing the handler.  		installHandler = true;  	} -	#endif +	#endif // ! LL_RELEASE_FOR_DOWNLOAD  	if(installHandler && (mExceptionHandler == 0))  	{ @@ -514,9 +523,9 @@ void LLApp::setupErrorHandling(bool second_instance)  		google_breakpad::MinidumpDescriptor desc(mDumpPath);  	    mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1);  	} -#endif +#endif // LL_LINUX -#endif +#endif // ! LL_WINDOWS  	startErrorThread();  } diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index caf2ba72c2..ddbcdc94a0 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -102,6 +102,9 @@ namespace LLError  	LL_COMMON_API FatalFunction getFatalFunction();  		// Retrieve the previously-set FatalFunction +	LL_COMMON_API std::string getFatalMessage(); +		// Retrieve the message last passed to FatalFunction, if any +  	/// temporarily override the FatalFunction for the duration of a  	/// particular scope, e.g. for unit tests  	class LL_COMMON_API OverrideFatalFunction diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 2879038c36..ef015fdce4 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -198,6 +198,8 @@  #define LL_TO_STRING_HELPER(x) #x  #define LL_TO_STRING(x) LL_TO_STRING_HELPER(x) +#define LL_TO_WSTRING_HELPER(x) L#x +#define LL_TO_WSTRING(x) LL_TO_WSTRING_HELPER(x)  #define LL_FILE_LINENO_MSG(msg) __FILE__ "(" LL_TO_STRING(__LINE__) ") : " msg  #define LL_GLUE_IMPL(x, y) x##y  #define LL_GLUE_TOKENS(x, y) LL_GLUE_IMPL(x, y) diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index a5a90d7297..38dd198ad3 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -30,7 +30,6 @@  #define LL_STRINGIZE_H  #include <sstream> -#include <boost/phoenix/phoenix.hpp>  #include <llstring.h>  /** @@ -53,12 +52,7 @@ std::basic_string<CHARTYPE> gstringize(const T& item)   */  inline std::string stringize(const std::wstring& item)  { -    LL_WARNS() << "WARNING:  Possible narrowing" << LL_ENDL; -     -    std::string s; -     -    s = wstring_to_utf8str(item); -    return gstringize<char>(s); +    return wstring_to_utf8str(item);  }  /** @@ -76,7 +70,10 @@ std::string stringize(const T& item)   */  inline std::wstring wstringize(const std::string& item)  { -    return gstringize<wchar_t>(item.c_str()); +    // utf8str_to_wstring() returns LLWString, which isn't necessarily the +    // same as std::wstring +    LLWString s(utf8str_to_wstring(item)); +    return std::wstring(s.begin(), s.end());  }  /** @@ -91,10 +88,10 @@ std::wstring wstringize(const T& item)  /**   * stringize_f(functor)   */ -template <typename Functor> -std::string stringize_f(Functor const & f) +template <typename CHARTYPE, typename Functor> +std::basic_string<CHARTYPE> stringize_f(Functor const & f)  { -    std::ostringstream out; +    std::basic_ostringstream<CHARTYPE> out;      f(out);      return out.str();  } @@ -108,31 +105,37 @@ std::string stringize_f(Functor const & f)   * return out.str();   * @endcode   */ -#define STRINGIZE(EXPRESSION) (stringize_f(boost::phoenix::placeholders::arg1 << EXPRESSION)) +#define STRINGIZE(EXPRESSION) (stringize_f<char>([&](std::ostream& out){ out << EXPRESSION; })) +/** + * WSTRINGIZE() is the wstring equivalent of STRINGIZE() + */ +#define WSTRINGIZE(EXPRESSION) (stringize_f<wchar_t>([&](std::wostream& out){ out << EXPRESSION; }))  /**   * destringize(str)   * defined for symmetry with stringize - * *NOTE - this has distinct behavior from boost::lexical_cast<T> regarding + * @NOTE - this has distinct behavior from boost::lexical_cast<T> regarding   * leading/trailing whitespace and handling of bad_lexical_cast exceptions + * @NOTE - no need for dewstringize(), since passing std::wstring will Do The + * Right Thing   */ -template <typename T> -T destringize(std::string const & str) +template <typename T, typename CHARTYPE> +T destringize(std::basic_string<CHARTYPE> const & str)  { -	T val; -    std::istringstream in(str); -	in >> val; +    T val; +    std::basic_istringstream<CHARTYPE> in(str); +    in >> val;      return val;  }  /**   * destringize_f(str, functor)   */ -template <typename Functor> -void destringize_f(std::string const & str, Functor const & f) +template <typename CHARTYPE, typename Functor> +void destringize_f(std::basic_string<CHARTYPE> const & str, Functor const & f)  { -    std::istringstream in(str); +    std::basic_istringstream<CHARTYPE> in(str);      f(in);  } @@ -143,8 +146,11 @@ void destringize_f(std::string const & str, Functor const & f)   * std::istringstream in(str);   * in >> item1 >> item2 >> item3 ... ;   * @endcode + * @NOTE - once we get generic lambdas, we shouldn't need DEWSTRINGIZE() any + * more since DESTRINGIZE() should do the right thing with a std::wstring. But + * until then, the lambda we pass must accept the right std::basic_istream.   */ -#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::phoenix::placeholders::arg1 >> EXPRESSION))) - +#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](std::istream& in){in >> EXPRESSION;})) +#define DEWSTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](std::wistream& in){in >> EXPRESSION;}))  #endif /* ! defined(LL_STRINGIZE_H) */ diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 9a4bbbd630..08fbf19b1c 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -109,6 +109,12 @@ public:          mMessages.push_back(message);      } +    friend inline +    std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log) +    { +        return log.streamto(out); +    } +      /// Don't assume the message we want is necessarily the LAST log message      /// emitted by the underlying code; search backwards through all messages      /// for the sought string. @@ -126,7 +132,7 @@ public:          throw tut::failure(STRINGIZE("failed to find '" << search                                       << "' in captured log messages:\n" -                                     << boost::ref(*this))); +                                     << *this));      }      std::ostream& streamto(std::ostream& out) const @@ -200,10 +206,4 @@ private:  	LLError::RecorderPtr mRecorder;  }; -inline -std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log) -{ -    return log.streamto(out); -} -  #endif /* ! defined(LL_WRAPLLERRS_H) */ diff --git a/indra/llimagej2coj/CMakeLists.txt b/indra/llimagej2coj/CMakeLists.txt index 97d22cf86a..c9423d50dd 100644 --- a/indra/llimagej2coj/CMakeLists.txt +++ b/indra/llimagej2coj/CMakeLists.txt @@ -29,7 +29,9 @@ set_source_files_properties(${llimagej2coj_HEADER_FILES}  list(APPEND llimagej2coj_SOURCE_FILES ${llimagej2coj_HEADER_FILES})  add_library (llimagej2coj ${llimagej2coj_SOURCE_FILES}) +  target_link_libraries(      llimagej2coj      ${OPENJPEG_LIBRARIES}      ) + diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index 0e5e835777..33520ad64c 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -48,7 +48,7 @@ add_executable(SLPlugin      WIN32      MACOSX_BUNDLE      ${SLPlugin_SOURCE_FILES} -) +    )  if (WINDOWS)  set_target_properties(SLPlugin diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 2069888774..18836e54b0 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -42,6 +42,7 @@  #include "lldiriterator.h"  #include "stringize.h" +#include "llstring.h"  #include <boost/filesystem.hpp>  #include <boost/foreach.hpp>  #include <boost/range/begin.hpp> @@ -317,9 +318,9 @@ const std::string& LLDir::getChatLogsDir() const  void LLDir::setDumpDir( const std::string& path )  {      LLDir::sDumpDir = path; -    if (! sDumpDir.empty() && sDumpDir.rbegin() == mDirDelimiter.rbegin() ) +    if (LLStringUtil::endsWith(sDumpDir, mDirDelimiter))      { -        sDumpDir.erase(sDumpDir.size() -1); +        sDumpDir.erase(sDumpDir.size() - mDirDelimiter.size());      }  } diff --git a/indra/media_plugins/base/CMakeLists.txt b/indra/media_plugins/base/CMakeLists.txt index 70c81d4023..7f2b82ffdd 100644 --- a/indra/media_plugins/base/CMakeLists.txt +++ b/indra/media_plugins/base/CMakeLists.txt @@ -48,5 +48,5 @@ set(media_plugin_base_HEADER_FILES  add_library(media_plugin_base      ${media_plugin_base_SOURCE_FILES} -) +    ) diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt index 5452fd9d1e..ce6278963d 100644 --- a/indra/media_plugins/cef/CMakeLists.txt +++ b/indra/media_plugins/cef/CMakeLists.txt @@ -81,7 +81,7 @@ list(APPEND media_plugin_cef_SOURCE_FILES ${media_plugin_cef_HEADER_FILES})  add_library(media_plugin_cef      SHARED      ${media_plugin_cef_SOURCE_FILES} -) +    )  #add_dependencies(media_plugin_cef  #  ${MEDIA_PLUGIN_BASE_LIBRARIES} diff --git a/indra/media_plugins/example/CMakeLists.txt b/indra/media_plugins/example/CMakeLists.txt index 6f5b28b8e9..eb067a7f6e 100644 --- a/indra/media_plugins/example/CMakeLists.txt +++ b/indra/media_plugins/example/CMakeLists.txt @@ -47,7 +47,7 @@ set(media_plugin_example_SOURCE_FILES  add_library(media_plugin_example      SHARED      ${media_plugin_example_SOURCE_FILES} -) +    )  target_link_libraries(media_plugin_example    ${LLPLUGIN_LIBRARIES} diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/media_plugins/gstreamer010/CMakeLists.txt index 6d18814b1e..571eb57b24 100644 --- a/indra/media_plugins/gstreamer010/CMakeLists.txt +++ b/indra/media_plugins/gstreamer010/CMakeLists.txt @@ -56,7 +56,7 @@ set(media_plugin_gstreamer010_HEADER_FILES  add_library(media_plugin_gstreamer010      SHARED      ${media_plugin_gstreamer010_SOURCE_FILES} -) +    )  target_link_libraries(media_plugin_gstreamer010    ${LLPLUGIN_LIBRARIES} diff --git a/indra/media_plugins/libvlc/CMakeLists.txt b/indra/media_plugins/libvlc/CMakeLists.txt index d3e9243069..97392bbe08 100644 --- a/indra/media_plugins/libvlc/CMakeLists.txt +++ b/indra/media_plugins/libvlc/CMakeLists.txt @@ -48,7 +48,7 @@ set(media_plugin_libvlc_SOURCE_FILES  add_library(media_plugin_libvlc      SHARED      ${media_plugin_libvlc_SOURCE_FILES} -) +    )  target_link_libraries(media_plugin_libvlc    ${LLPLUGIN_LIBRARIES} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2fc722d4c3..e573b927d7 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -3,7 +3,14 @@  project(viewer)  include(00-Common) +# DON'T move Linking.cmake to its place in the alphabetized list below: it +# sets variables on which the 3p .cmake files depend. +include(Linking) +  include(Boost) +if (BUGSPLAT_DB) +  include(bugsplat) +endif (BUGSPLAT_DB)  include(BuildPackagesInfo)  include(BuildVersion)  include(CMakeCopyIfDifferent) @@ -37,7 +44,6 @@ include(LLUI)  include(LLVFS)  include(LLWindow)  include(LLXML) -include(Linking)  include(NDOF)  include(NVAPI)  include(OPENAL) @@ -93,6 +99,12 @@ include_directories(      ${CMAKE_CURRENT_SOURCE_DIR}      ) +if (BUGSPLAT_DB) +  include_directories( +    ${BUGSPLAT_INCLUDE_DIR} +    ) +endif (BUGSPLAT_DB) +  include_directories(SYSTEM      ${LLCOMMON_SYSTEM_INCLUDE_DIRS}      ${LLXML_SYSTEM_INCLUDE_DIRS} @@ -1356,6 +1368,14 @@ if (DARWIN)    # This should be compiled with the viewer.    LIST(APPEND viewer_SOURCE_FILES llappdelegate-objc.mm) +  set_source_files_properties( +    llappdelegate-objc.mm +    PROPERTIES +    COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" +    # BugsplatMac is a module, imported with @import. That language feature +    # demands these switches. +    COMPILE_FLAGS "-fmodules -fcxx-modules" +    )    find_library(AGL_LIBRARY AGL)    find_library(APPKIT_LIBRARY AppKit) @@ -1370,6 +1390,12 @@ if (DARWIN)      ${COREAUDIO_LIBRARY}      ) +  if (BUGSPLAT_DB) +    list(APPEND viewer_LIBRARIES +      ${BUGSPLAT_LIBRARIES} +      ) +  endif (BUGSPLAT_DB) +    # Add resource files to the project.    set(viewer_RESOURCE_FILES      secondlife.icns @@ -1395,6 +1421,11 @@ endif (DARWIN)  if (LINUX)      LIST(APPEND viewer_SOURCE_FILES llappviewerlinux.cpp) +    set_source_files_properties( +      llappviewerlinux.cpp +      PROPERTIES +      COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" +      )      LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp)      SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") @@ -1411,6 +1442,11 @@ if (WINDOWS)           llappviewerwin32.cpp           llwindebug.cpp           ) +    set_source_files_properties( +      llappviewerwin32.cpp +      PROPERTIES +      COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" +      )      list(APPEND viewer_HEADER_FILES           llappviewerwin32.h @@ -1693,6 +1729,11 @@ if (SDL_FOUND)      )  endif (SDL_FOUND) +if (BUGSPLAT_DB) +  set_property(TARGET ${VIEWER_BINARY_NAME} +    PROPERTY COMPILE_DEFINITIONS "LL_BUGSPLAT") +endif (BUGSPLAT_DB) +  # add package files  file(GLOB EVENT_HOST_SCRIPT_GLOB_LIST       ${CMAKE_CURRENT_SOURCE_DIR}/../viewer_components/*.py) @@ -1791,7 +1832,7 @@ if (WINDOWS)             ${SHARED_LIB_STAGING_DIR}/Debug/fmodexL.dll            )      endif (FMODEX) -     +      add_custom_command(        OUTPUT  ${CMAKE_CFG_INTDIR}/copy_touched.bat        COMMAND ${PYTHON_EXECUTABLE} @@ -1800,15 +1841,16 @@ if (WINDOWS)          --actions=copy          --arch=${ARCH}          --artwork=${ARTWORK_DIR} +        "--bugsplat=${BUGSPLAT_DB}"          --build=${CMAKE_CURRENT_BINARY_DIR}          --buildtype=${CMAKE_BUILD_TYPE} +        "--channel=${VIEWER_CHANNEL}"          --configuration=${CMAKE_CFG_INTDIR}          --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}          --grid=${GRID} -        "--channel=${VIEWER_CHANNEL}" -        --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt          --source=${CMAKE_CURRENT_SOURCE_DIR}          --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat +        --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt        DEPENDS          ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py          stage_third_party_libs @@ -1826,24 +1868,9 @@ if (WINDOWS)      add_dependencies(${VIEWER_BINARY_NAME}        SLPlugin -   windows-crash-logger +      windows-crash-logger      ) -    # sets the 'working directory' for debugging from visual studio. -    if (NOT UNATTENDED) -        add_custom_command( -            TARGET ${VIEWER_BINARY_NAME} POST_BUILD -            COMMAND ${CMAKE_SOURCE_DIR}/tools/vstool/vstool.exe -            ARGS -              --solution -              ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.sln -              --workingdir -              ${VIEWER_BINARY_NAME} -              "${CMAKE_CURRENT_SOURCE_DIR}" -            COMMENT "Setting the ${VIEWER_BINARY_NAME} working directory for debugging." -            ) -    endif (NOT UNATTENDED) -      if (PACKAGE)        add_custom_command(          OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2 @@ -1866,15 +1893,16 @@ if (WINDOWS)            ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py            --arch=${ARCH}            --artwork=${ARTWORK_DIR} +          "--bugsplat=${BUGSPLAT_DB}"            --build=${CMAKE_CURRENT_BINARY_DIR}            --buildtype=${CMAKE_BUILD_TYPE}            "--channel=${VIEWER_CHANNEL}" -          --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt            --configuration=${CMAKE_CFG_INTDIR}            --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}            --grid=${GRID}            --source=${CMAKE_CURRENT_SOURCE_DIR}            --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/touched.bat +          --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt          DEPENDS              ${VIEWER_BINARY_NAME}              ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py @@ -1905,8 +1933,8 @@ else (WINDOWS)  endif (WINDOWS)  # *NOTE: - this list is very sensitive to ordering, test carefully on all -# platforms if you change the releative order of the entries here. -# In particular, cmake 2.6.4 (when buidling with linux/makefile generators) +# platforms if you change the relative order of the entries here. +# In particular, cmake 2.6.4 (when building with linux/makefile generators)  # appears to sometimes de-duplicate redundantly listed dependencies improperly.  # To work around this, higher level modules should be listed before the modules  # that they depend upon. -brad @@ -1981,6 +2009,12 @@ target_link_libraries(${VIEWER_BINARY_NAME}      ${LLAPPEARANCE_LIBRARIES}      ) +if (BUGSPLAT_DB) +  target_link_libraries(${VIEWER_BINARY_NAME} +    ${BUGSPLAT_LIBRARIES} +    ) +endif (BUGSPLAT_DB) +  set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH      "Path to artwork files.") @@ -2004,15 +2038,16 @@ if (LINUX)          ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py          --arch=${ARCH}          --artwork=${ARTWORK_DIR} +        "--bugsplat=${BUGSPLAT_DB}"          --build=${CMAKE_CURRENT_BINARY_DIR}          --buildtype=${CMAKE_BUILD_TYPE}          "--channel=${VIEWER_CHANNEL}" -        --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt          --configuration=${CMAKE_CFG_INTDIR}          --dest=${CMAKE_CURRENT_BINARY_DIR}/packaged          --grid=${GRID}          --source=${CMAKE_CURRENT_SOURCE_DIR}          --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched +        --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt        DEPENDS          ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py          ${COPY_INPUT_DEPENDENCIES} @@ -2026,17 +2061,18 @@ if (LINUX)      COMMAND ${PYTHON_EXECUTABLE}      ARGS        ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py -      --arch=${ARCH}        --actions=copy +      --arch=${ARCH}        --artwork=${ARTWORK_DIR} +      "--bugsplat=${BUGSPLAT_DB}"        --build=${CMAKE_CURRENT_BINARY_DIR}        --buildtype=${CMAKE_BUILD_TYPE} +      "--channel=${VIEWER_CHANNEL}"        --configuration=${CMAKE_CFG_INTDIR}        --dest=${CMAKE_CURRENT_BINARY_DIR}/packaged        --grid=${GRID} -      "--channel=${VIEWER_CHANNEL}" -      --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt        --source=${CMAKE_CURRENT_SOURCE_DIR} +      --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt      DEPENDS        ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py        ${COPY_INPUT_DEPENDENCIES} @@ -2054,8 +2090,10 @@ if (LINUX)  endif (LINUX)  if (DARWIN) -  # These all get set with PROPERTIES -  set(product "Second Life") +  # These all get set with PROPERTIES. It's not that the property names are +  # magically known to CMake -- it's that these names are referenced in the +  # Info-SecondLife.plist file in the configure_file() directive below. +  set(product "${VIEWER_CHANNEL}")    # this is the setting for the Python wrapper, see SL-322 and WRAPPER line in Info-SecondLife.plist    if (PACKAGE)        set(MACOSX_WRAPPER_EXECUTABLE_NAME "SL_Launcher") @@ -2063,28 +2101,41 @@ if (DARWIN)        # force the name of the actual executable to allow running it within Xcode for debugging        set(MACOSX_WRAPPER_EXECUTABLE_NAME "../Resources/Second Life Viewer.app/Contents/MacOS/Second Life")    endif (PACKAGE) -  set(MACOSX_BUNDLE_INFO_STRING "Second Life Viewer") +  set(MACOSX_BUNDLE_INFO_STRING "${VIEWER_CHANNEL}")    set(MACOSX_BUNDLE_ICON_FILE "secondlife.icns")    set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.secondlife.indra.viewer")    set(MACOSX_BUNDLE_LONG_VERSION_STRING "${VIEWER_CHANNEL} ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}")    set(MACOSX_BUNDLE_BUNDLE_NAME "SecondLife") -  set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}") +  set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}")    set(MACOSX_BUNDLE_BUNDLE_VERSION "${VIEWER_SHORT_VERSION}${VIEWER_MACOSX_PHASE}${VIEWER_REVISION}")    set(MACOSX_BUNDLE_COPYRIGHT "Copyright © Linden Research, Inc. 2007")    set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "SecondLife.nib")    set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "NSApplication") + +  # https://blog.kitware.com/upcoming-in-cmake-2-8-12-osx-rpath-support/ +  set(CMAKE_MACOSX_RPATH 1)    set_target_properties(      ${VIEWER_BINARY_NAME}      PROPERTIES      OUTPUT_NAME "${product}" +    # From Contents/MacOS/SecondLife, look in Contents/Frameworks +    INSTALL_RPATH "@loader_path/../Frameworks" +    # SIGH, as of 2018-05-24 (cmake 3.11.1) the INSTALL_RPATH property simply +    # does not work. Try this: +    LINK_FLAGS "-rpath @loader_path/../Frameworks"      MACOSX_BUNDLE_INFO_PLIST      "${CMAKE_CURRENT_SOURCE_DIR}/Info-SecondLife.plist"      ) +  set(VIEWER_APP_BUNDLE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app") +  set(VIEWER_APP_EXE "${VIEWER_APP_BUNDLE}/Contents/MacOS/${product}") +  set(VIEWER_APP_DSYM "${VIEWER_APP_EXE}.dSYM") +  set(VIEWER_APP_XCARCHIVE "${VIEWER_APP_BUNDLE}/../${product}.xcarchive.zip") +    configure_file(       "${CMAKE_CURRENT_SOURCE_DIR}/Info-SecondLife.plist" -     "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app/Contents/Info.plist" +     "${VIEWER_APP_BUNDLE}/Contents/Info.plist"      )    add_custom_command( @@ -2095,15 +2146,16 @@ if (DARWIN)        --actions=copy        --arch=${ARCH}        --artwork=${ARTWORK_DIR} +      "--bugsplat=${BUGSPLAT_DB}"        --build=${CMAKE_CURRENT_BINARY_DIR}        --buildtype=${CMAKE_BUILD_TYPE} +      --bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER} +      "--channel=${VIEWER_CHANNEL}"        --configuration=${CMAKE_CFG_INTDIR} -      --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app +      --dest=${VIEWER_APP_BUNDLE}        --grid=${GRID} -      "--channel=${VIEWER_CHANNEL}" -      --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt -      --bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER}        --source=${CMAKE_CURRENT_SOURCE_DIR} +      --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt      DEPENDS        ${VIEWER_BINARY_NAME}        ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py @@ -2128,15 +2180,16 @@ if (DARWIN)            ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py            --arch=${ARCH}            --artwork=${ARTWORK_DIR} +          "--bugsplat=${BUGSPLAT_DB}"            --build=${CMAKE_CURRENT_BINARY_DIR}            --buildtype=${CMAKE_BUILD_TYPE} +          "--channel=${VIEWER_CHANNEL}"            --configuration=${CMAKE_CFG_INTDIR} -          --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app +          --dest=${VIEWER_APP_BUNDLE}            --grid=${GRID} -          "--channel=${VIEWER_CHANNEL}" -          --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt            --source=${CMAKE_CURRENT_SOURCE_DIR}            --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched +          --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt            ${SIGNING_SETTING}          DEPENDS            ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py @@ -2148,67 +2201,152 @@ if (INSTALL)    include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake)  endif (INSTALL) -if (PACKAGE) -  set(SYMBOL_SEARCH_DIRS "") -  # Note that the path to VIEWER_SYMBOL_FILE must match that in ../../build.sh -  if (WINDOWS) -    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") -    set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-windows-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2") -    # slplugin.exe failing symbols dump - need to debug, might have to do with updated version of google breakpad -    # set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe") -    set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}") -    set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}") -    set(VIEWER_COPY_MANIFEST copy_w_viewer_manifest) -  endif (WINDOWS) -  if (DARWIN) -    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") -    # *TODO: Generate these search dirs in the cmake files related to each binary. -    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/llplugin/slplugin/${CMAKE_CFG_INTDIR}") -    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_crash_logger/${CMAKE_CFG_INTDIR}") -    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}") -    set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2") -    set(VIEWER_EXE_GLOBS "'Second Life' SLPlugin mac-crash-logger") -    set(VIEWER_EXE_GLOBS "'Second Life' mac-crash-logger") -    set(VIEWER_LIB_GLOB "*.dylib") -  endif (DARWIN) -  if (LINUX) -    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged") -    set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-linux-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2") -    set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin SLPlugin") -    set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin") -    set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*") -    set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) -  endif (LINUX) - -  if(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) -  if(CMAKE_CFG_INTDIR STREQUAL ".") -      set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE}) -  else(CMAKE_CFG_INTDIR STREQUAL ".") -      # set LLBUILD_CONFIG to be a shell variable evaluated at build time -      # reflecting the configuration we are currently building. -      set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR}) -  endif(CMAKE_CFG_INTDIR STREQUAL ".") -  add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}" -    COMMAND "${PYTHON_EXECUTABLE}" -    ARGS -      "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py" -      "${LLBUILD_CONFIG}" -      "${SYMBOL_SEARCH_DIRS}" -      "${VIEWER_EXE_GLOBS}" -      "${VIEWER_LIB_GLOB}" -      "${AUTOBUILD_INSTALL_DIR}/bin/dump_syms" -      "${VIEWER_SYMBOL_FILE}" -    DEPENDS generate_breakpad_symbols.py -        VERBATIM) - -  add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}") -  add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}") -  if (WINDOWS OR LINUX) -    add_dependencies(generate_breakpad_symbols "${VIEWER_COPY_MANIFEST}") -  endif (WINDOWS OR LINUX) -  add_dependencies(llpackage generate_breakpad_symbols) -  endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) -endif (PACKAGE) +# Note that the conventional VIEWER_SYMBOL_FILE is set by ../../build.sh +if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND VIEWER_SYMBOL_FILE) +  if (NOT BUGSPLAT_DB) +    # Breakpad symbol-file generation +    set(SYMBOL_SEARCH_DIRS "") +    if (WINDOWS) +      list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") +      # slplugin.exe failing symbols dump - need to debug, might have to do with updated version of google breakpad +      # set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe") +      set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}") +      set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}") +      set(VIEWER_COPY_MANIFEST copy_w_viewer_manifest) +    endif (WINDOWS) +    if (DARWIN) +      list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") +      # *TODO: Generate these search dirs in the cmake files related to each binary. +      list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/llplugin/slplugin/${CMAKE_CFG_INTDIR}") +      list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_crash_logger/${CMAKE_CFG_INTDIR}") +      list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}") +      set(VIEWER_EXE_GLOBS "'${product}' SLPlugin mac-crash-logger") +      set(VIEWER_EXE_GLOBS "'${product}' mac-crash-logger") +      set(VIEWER_LIB_GLOB "*.dylib") +    endif (DARWIN) +    if (LINUX) +      list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged") +      set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin SLPlugin") +      set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin") +      set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*") +      set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) +    endif (LINUX) + +    if(CMAKE_CFG_INTDIR STREQUAL ".") +        set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE}) +    else(CMAKE_CFG_INTDIR STREQUAL ".") +        # set LLBUILD_CONFIG to be a shell variable evaluated at build time +        # reflecting the configuration we are currently building. +        set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR}) +    endif(CMAKE_CFG_INTDIR STREQUAL ".") +    add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}" +      COMMAND "${PYTHON_EXECUTABLE}" +      ARGS +        "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py" +        "${LLBUILD_CONFIG}" +        "${SYMBOL_SEARCH_DIRS}" +        "${VIEWER_EXE_GLOBS}" +        "${VIEWER_LIB_GLOB}" +        "${AUTOBUILD_INSTALL_DIR}/bin/dump_syms" +        "${VIEWER_SYMBOL_FILE}" +      DEPENDS generate_breakpad_symbols.py +          VERBATIM) + +    add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" ${VIEWER_BINARY_NAME} "${VIEWER_COPY_MANIFEST}") +    add_dependencies(generate_symbols ${VIEWER_BINARY_NAME}) +    if (WINDOWS OR LINUX) +      add_dependencies(generate_symbols "${VIEWER_COPY_MANIFEST}") +    endif (WINDOWS OR LINUX) + +  else (NOT BUGSPLAT_DB) +    # BugSplat symbol-file generation +    if (WINDOWS) +      # Just pack up a tarball containing only the .pdb file for the +      # executable. Because we intend to use cygwin tar, we must render +      # VIEWER_SYMBOL_FILE in cygwin path syntax. +      execute_process(COMMAND "cygpath" "-u" "${VIEWER_SYMBOL_FILE}" +        OUTPUT_VARIABLE VIEWER_SYMBOL_FILE_CYGWIN +        OUTPUT_STRIP_TRAILING_WHITESPACE) +      execute_process(COMMAND "cygpath" "-u" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" +        OUTPUT_VARIABLE PARENT_DIRECTORY_CYGWIN +        OUTPUT_STRIP_TRAILING_WHITESPACE) +      add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}" +        # Use of 'tar ...j' here assumes VIEWER_SYMBOL_FILE endswith .tar.bz2; +        # testing a string suffix is painful enough in CMake language that +        # we'll continue assuming it until forced to generalize. +        COMMAND "tar" +        ARGS +          "cjf" +          "${VIEWER_SYMBOL_FILE_CYGWIN}" +          "-C" +          "${PARENT_DIRECTORY_CYGWIN}" +          "secondlife-bin.pdb" +        DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-bin.pdb" +        COMMENT "Packing viewer PDB into ${VIEWER_SYMBOL_FILE_CYGWIN}" +        ) +      add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" ${VIEWER_BINARY_NAME}) +      add_dependencies(generate_symbols ${VIEWER_BINARY_NAME}) +    endif (WINDOWS) +    if (DARWIN) +      # Have to run dsymutil first, then pack up the resulting .dSYM directory +      add_custom_command(OUTPUT "${VIEWER_APP_DSYM}" +        COMMAND "dsymutil" +        ARGS +          ${VIEWER_APP_EXE} +        COMMENT "Generating ${VIEWER_APP_DSYM}" +        ) +      add_custom_target(dsym_generate DEPENDS "${VIEWER_APP_DSYM}") +      add_dependencies(dsym_generate ${VIEWER_BINARY_NAME}) +      add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}" +        # See above comments about "tar ...j" +        COMMAND "tar" +        ARGS +          "cjf" +          "${VIEWER_SYMBOL_FILE}" +          "-C" +          "${VIEWER_APP_DSYM}/.." +          "${product}.dSYM" +        DEPENDS "${VIEWER_APP_DSYM}" +        COMMENT "Packing dSYM into ${VIEWER_SYMBOL_FILE}" +        ) +      add_custom_target(dsym_tarball DEPENDS "${VIEWER_SYMBOL_FILE}") +      add_dependencies(dsym_tarball dsym_generate) +      add_custom_command(OUTPUT "${VIEWER_APP_XCARCHIVE}" +        COMMAND "zip" +        ARGS +          "-r" +          "${VIEWER_APP_XCARCHIVE}" +          "." +        WORKING_DIRECTORY "${VIEWER_APP_DSYM}/.." +        DEPENDS "${VIEWER_APP_DSYM}" +        COMMENT "Generating xcarchive.zip for upload to BugSplat" +        ) +      add_custom_target(dsym_xcarchive DEPENDS "${VIEWER_APP_XCARCHIVE}") +      add_dependencies(dsym_xcarchive dsym_generate) +      # Have to create a stamp file, and depend on it, to force CMake to run +      # the cleanup step. +      add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/dsym.stamp" +        COMMAND rm -rf "${VIEWER_APP_DSYM}" +        COMMAND touch "${CMAKE_CURRENT_BINARY_DIR}/dsym.stamp" +        DEPENDS "${VIEWER_SYMBOL_FILE}" "${VIEWER_APP_XCARCHIVE}" +        COMMENT "Cleaning up dSYM" +        ) +      add_custom_target(generate_symbols DEPENDS +        "${VIEWER_APP_DSYM}" +        "${VIEWER_SYMBOL_FILE}" +        "${VIEWER_APP_XCARCHIVE}" +        "${CMAKE_CURRENT_BINARY_DIR}/dsym.stamp" +        ) +      add_dependencies(generate_symbols dsym_tarball dsym_xcarchive) +    endif (DARWIN) +    if (LINUX) +      # TBD +    endif (LINUX) +  endif (NOT BUGSPLAT_DB) + +  # for both BUGSPLAT_DB and Breakpad +  add_dependencies(llpackage generate_symbols) +endif ()  if (LL_TESTS)    # To add a viewer unit test, just add the test .cpp file below diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index af4cf26ac6..8aabd6818b 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -21,7 +21,7 @@  	<key>CFBundlePackageType</key>  	<string>APPL</string>  	<key>CFBundleShortVersionString</key> -	<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> +	<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>  	<key>CFBundleSignature</key>  	<string>????</string>  	<key>CFBundleVersion</key> diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index aebae4c434..f55304f30b 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -25,7 +25,14 @@   */  #import "llappdelegate-objc.h" +#if defined(LL_BUGSPLAT) +@import BugsplatMac; +// derived from BugsplatMac's BugsplatTester/AppDelegate.m +@interface LLAppDelegate () <BugsplatStartupManagerDelegate> +@end +#endif  #include "llwindowmacosx-objc.h" +#include "llappviewermacosx-for-objc.h"  #include <Carbon/Carbon.h> // Used for Text Input Services ("Safe" API - it's supported)  @implementation LLAppDelegate @@ -47,6 +54,25 @@  - (void) applicationDidFinishLaunching:(NSNotification *)notification  { +	// Call constructViewer() first so our logging subsystem is in place. This +	// risks missing crashes in the LLAppViewerMacOSX constructor, but for +	// present purposes it's more important to get the startup sequence +	// properly logged. +	// Someday I would like to modify the logging system so that calls before +	// it's initialized are cached in a std::ostringstream and then, once it's +	// initialized, "played back" into whatever handlers have been set up. +	constructViewer(); + +#if defined(LL_BUGSPLAT) +	// Engage BugsplatStartupManager *before* calling initViewer() to handle +	// any crashes during initialization. +	// https://www.bugsplat.com/docs/platforms/os-x#initialization +	[BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES; +	[BugsplatStartupManager sharedManager].askUserDetails = NO; +	[BugsplatStartupManager sharedManager].delegate = self; +	[[BugsplatStartupManager sharedManager] start]; +#endif +  	frameTimer = nil;  	[self languageUpdated]; @@ -179,4 +205,104 @@      return true;  } +#if defined(LL_BUGSPLAT) + +- (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +{ +    CrashMetadata& meta(CrashMetadata_instance()); +    // As of BugsplatMac 1.0.6, userName and userEmail properties are now +    // exposed by the BugsplatStartupManager. Set them here, since the +    // defaultUserNameForBugsplatStartupManager and +    // defaultUserEmailForBugsplatStartupManager methods are called later, for +    // the *current* run, rather than for the previous crashed run whose crash +    // report we are about to send. +    infos("applicationLogForBugsplatStartupManager setting userName = '" + +          meta.agentFullname + '"'); +    bugsplatStartupManager.userName = +        [NSString stringWithCString:meta.agentFullname.c_str() +                           encoding:NSUTF8StringEncoding]; +    // Use the email field for OS version, just as we do on Windows, until +    // BugSplat provides more metadata fields. +    infos("applicationLogForBugsplatStartupManager setting userEmail = '" + +          meta.OSInfo + '"'); +    bugsplatStartupManager.userEmail = +        [NSString stringWithCString:meta.OSInfo.c_str() +                           encoding:NSUTF8StringEncoding]; +    // This strangely-named override method's return value contributes the +    // User Description metadata field. +    infos("applicationLogForBugsplatStartupManager -> '" + meta.fatalMessage + "'"); +    return [NSString stringWithCString:meta.fatalMessage.c_str() +                              encoding:NSUTF8StringEncoding]; +} + +- (NSString *)applicationKeyForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager signal:(NSString *)signal exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason { +    // TODO: exceptionName, exceptionReason + +    // Windows sends location within region as well, but that's because +    // BugSplat for Windows intercepts crashes during the same run, and that +    // information can be queried once. On the Mac, any metadata we have is +    // written (and rewritten) to the static_debug_info.log file that we read +    // at the start of the next viewer run. It seems ridiculously expensive to +    // rewrite that file on every frame in which the avatar moves. +    std::string regionName(CrashMetadata_instance().regionName); +    infos("applicationKeyForBugsplatStartupManager -> '" + regionName + "'"); +    return [NSString stringWithCString:regionName.c_str() +                              encoding:NSUTF8StringEncoding]; +} + +- (NSString *)defaultUserNameForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { +    std::string agentFullname(CrashMetadata_instance().agentFullname); +    infos("defaultUserNameForBugsplatStartupManager -> '" + agentFullname + "'"); +    return [NSString stringWithCString:agentFullname.c_str() +                              encoding:NSUTF8StringEncoding]; +} + +- (NSString *)defaultUserEmailForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { +    // Use the email field for OS version, just as we do on Windows, until +    // BugSplat provides more metadata fields. +    std::string OSInfo(CrashMetadata_instance().OSInfo); +    infos("defaultUserEmailForBugsplatStartupManager -> '" + OSInfo + "'"); +    return [NSString stringWithCString:OSInfo.c_str() +                              encoding:NSUTF8StringEncoding]; +} + +- (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugsplatStartupManager +{ +    infos("bugsplatStartupManagerWillSendCrashReport"); +} + +- (BugsplatAttachment *)attachmentForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { +    std::string logfile = CrashMetadata_instance().logFilePathname; +    // Still to do: +    // userSettingsPathname +    // staticDebugPathname +    // but the BugsplatMac version 1.0.5 BugsplatStartupManagerDelegate API +    // doesn't yet provide a way to attach more than one file. +    NSString *ns_logfile = [NSString stringWithCString:logfile.c_str() +                                              encoding:NSUTF8StringEncoding]; +    NSData *data = [NSData dataWithContentsOfFile:ns_logfile]; + +    // Apologies for the hard-coded log-file basename, but I do not know the +    // incantation for "$(basename "$logfile")" in this language. +    BugsplatAttachment *attachment =  +        [[BugsplatAttachment alloc] initWithFilename:@"SecondLife.log" +                                      attachmentData:data +                                         contentType:@"text/plain"]; +    infos("attachmentForBugsplatStartupManager attaching " + logfile); +    return attachment; +} + +- (void)bugsplatStartupManagerDidFinishSendingCrashReport:(BugsplatStartupManager *)bugsplatStartupManager +{ +    infos("Sent crash report to BugSplat"); +} + +- (void)bugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager didFailWithError:(NSError *)error +{ +    // TODO: message string from NSError +    infos("Could not send crash report to BugSplat"); +} + +#endif // LL_BUGSPLAT +  @end diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 189f7c1426..dd82aa735f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -707,6 +707,22 @@ LLAppViewer::LLAppViewer()  	//  	LLLoginInstance::instance().setPlatformInfo(gPlatform, LLOSInfo::instance().getOSVersionString(), LLOSInfo::instance().getOSStringSimple()); + +	// Under some circumstances we want to read the static_debug_info.log file +	// from the previous viewer run between this constructor call and the +	// init() call, which will overwrite the static_debug_info.log file for +	// THIS run. So setDebugFileNames() early. +#if LL_BUGSPLAT +	// MAINT-8917: don't create a dump directory just for the +	// static_debug_info.log file +	std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); +#else // ! LL_BUGSPLAT +	// write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues. +	std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); +#endif // ! LL_BUGSPLAT +	mDumpPath = logdir; +	setMiniDumpDir(logdir); +	setDebugFileNames(logdir);  }  LLAppViewer::~LLAppViewer() @@ -781,13 +797,6 @@ bool LLAppViewer::init()  	initMaxHeapSize() ;  	LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize")); -	// write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues. -	std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); -	mDumpPath = logdir; -	setMiniDumpDir(logdir); -	logdir += gDirUtilp->getDirDelimiter(); -    setDebugFileNames(logdir); -  	// Although initLoggingAndGetLastDuration() is the right place to mess with  	// setFatalFunction(), we can't query gSavedSettings until after @@ -2172,6 +2181,12 @@ void errorCallback(const std::string &error_string)  	//Set the ErrorActivated global so we know to create a marker file  	gLLErrorActivated = true; +	gDebugInfo["FatalMessage"] = error_string; +	// We're not already crashing -- we simply *intend* to crash. Since we +	// haven't actually trashed anything yet, we can afford to write the whole +	// static info file. +	LLAppViewer::instance()->writeDebugInfo(); +  	LLError::crashAndLoop(error_string);  } @@ -3039,14 +3054,11 @@ void LLAppViewer::writeDebugInfo(bool isStatic)          ? getStaticDebugFile()          : getDynamicDebugFile() ); -	LL_INFOS() << "Opening debug file " << *debug_filename << LL_ENDL; -	llofstream out_file(debug_filename->c_str()); +    LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL; +    llofstream out_file(debug_filename->c_str());      isStatic ?  LLSDSerialize::toPrettyXML(gDebugInfo, out_file)               :  LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file); - - -	out_file.close();  }  LLSD LLAppViewer::getViewerInfo() const diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h new file mode 100644 index 0000000000..37e8a3917a --- /dev/null +++ b/indra/newview/llappviewermacosx-for-objc.h @@ -0,0 +1,53 @@ +/** + * @file   llappviewermacosx-for-objc.h + * @author Nat Goodspeed + * @date   2018-06-15 + * @brief  llappviewermacosx.h publishes the C++ API for + *         llappviewermacosx.cpp, just as + *         llappviewermacosx-objc.h publishes the Objective-C++ API for + *         llappviewermacosx-objc.mm. + * + *         This header is intended to publish for Objective-C++ consumers a + *         subset of the C++ API presented by llappviewermacosx.cpp. It's a + *         subset because, if an Objective-C++ consumer were to #include + *         the full llappviewermacosx.h, we would almost surely run into + *         trouble due to the discrepancy between Objective-C++'s BOOL versus + *         classic Microsoft/Linden BOOL. + *  + * $LicenseInfo:firstyear=2018&license=viewerlgpl$ + * Copyright (c) 2018, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) +#define LL_LLAPPVIEWERMACOSX_FOR_OBJC_H + +#include <string> + +void constructViewer(); +bool initViewer(); +void handleUrl(const char* url_utf8); +bool pumpMainLoop(); +void handleQuit(); +void cleanupViewer(); +void infos(const std::string& message); + +// This struct is malleable; it only serves as a way to convey a number of +// fields from llappviewermacosx.cpp's CrashMetadata_instance() function to the +// consuming functions in llappdelegate-objc.mm. As long as both those sources +// are compiled with this same header, the content and order of CrashMetadata +// can change as needed. +struct CrashMetadata +{ +    std::string logFilePathname; +    std::string userSettingsPathname; +    std::string staticDebugPathname; +    std::string OSInfo; +    std::string agentFullname; +    std::string regionName; +    std::string fatalMessage; +}; + +CrashMetadata& CrashMetadata_instance(); + +#endif /* ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) */ diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index d472f8926b..81f04744f8 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -36,20 +36,25 @@  #include "llappviewermacosx-objc.h"  #include "llappviewermacosx.h" +#include "llappviewermacosx-for-objc.h"  #include "llwindowmacosx-objc.h"  #include "llcommandlineparser.h" +#include "llsdserialize.h"  #include "llviewernetwork.h"  #include "llviewercontrol.h"  #include "llmd5.h"  #include "llfloaterworldmap.h"  #include "llurldispatcher.h" +#include "llerrorcontrol.h" +#include "llvoavatarself.h"         // for gAgentAvatarp->getFullname()  #include <ApplicationServices/ApplicationServices.h>  #ifdef LL_CARBON_CRASH_HANDLER  #include <Carbon/Carbon.h>  #endif  #include <vector>  #include <exception> +#include <fstream>  #include "lldir.h"  #include <signal.h> @@ -81,7 +86,7 @@ static void exceptionTerminateHandler()  	gOldTerminateHandler(); // call old terminate() handler  } -bool initViewer() +void constructViewer()  {  	// Set the working dir to <bundle>/Contents/Resources  	if (chdir(gDirUtilp->getAppRODataDir().c_str()) == -1) @@ -97,18 +102,20 @@ bool initViewer()  	gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);  	gViewerAppPtr->setErrorHandler(LLAppViewer::handleViewerCrash); +} -	 +bool initViewer() +{  	bool ok = gViewerAppPtr->init();  	if(!ok)  	{  		LL_WARNS() << "Application init failed." << LL_ENDL;  	} -    else if (!gHandleSLURL.empty()) -    { -        dispatchUrl(gHandleSLURL); -        gHandleSLURL = ""; -    } +	else if (!gHandleSLURL.empty()) +	{ +		dispatchUrl(gHandleSLURL); +		gHandleSLURL = ""; +	}  	return ok;  } @@ -147,6 +154,73 @@ void cleanupViewer()  	gViewerAppPtr = NULL;  } +// The BugsplatMac API is structured as a number of different method +// overrides, each returning a different piece of metadata. But since we +// obtain such metadata by opening and parsing a file, it seems ridiculous to +// reopen and reparse it for every individual string desired. What we want is +// to open and parse the file once, retaining the data for subsequent +// requests. That's why this is an LLSingleton. +// Another approach would be to provide a function that simply returns +// CrashMetadata, storing the struct in LLAppDelegate, but nat doesn't know +// enough Objective-C++ to code that. We'd still have to detect which of the +// method overrides is called first so that the results are order-insensitive. +class CrashMetadataSingleton: public CrashMetadata, public LLSingleton<CrashMetadataSingleton> +{ +    LLSINGLETON(CrashMetadataSingleton); + +    // convenience method to log each metadata field retrieved by constructor +    std::string get_metadata(const LLSD& info, const LLSD::String& key) const +    { +        std::string data(info[key].asString()); +        LL_INFOS() << "  " << key << "='" << data << "'" << LL_ENDL; +        return data; +    } +}; + +// Populate the fields of our public base-class struct. +CrashMetadataSingleton::CrashMetadataSingleton() +{ +    // Note: we depend on being able to read the static_debug_info.log file +    // from the *previous* run before we overwrite it with the new one for +    // *this* run. LLAppViewer initialization must happen in the Right Order. +    staticDebugPathname = *gViewerAppPtr->getStaticDebugFile(); +    std::ifstream static_file(staticDebugPathname); +    LLSD info; +    if (! static_file.is_open()) +    { +        LL_INFOS() << "Can't open '" << staticDebugPathname +                   << "'; no metadata about previous run" << LL_ENDL; +    } +    else if (! LLSDSerialize::deserialize(info, static_file, LLSDSerialize::SIZE_UNLIMITED)) +    { +        LL_INFOS() << "Can't parse '" << staticDebugPathname +                   << "'; no metadata about previous run" << LL_ENDL; +    } +    else +    { +        LL_INFOS() << "Metadata from '" << staticDebugPathname << "':" << LL_ENDL; +        logFilePathname      = get_metadata(info, "SLLog"); +        userSettingsPathname = get_metadata(info, "SettingsFilename"); +        OSInfo               = get_metadata(info, "OSInfo"); +        agentFullname        = get_metadata(info, "LoginName"); +        // Translate underscores back to spaces +        LLStringUtil::replaceChar(agentFullname, '_', ' '); +        regionName           = get_metadata(info, "CurrentRegion"); +        fatalMessage         = get_metadata(info, "FatalMessage"); +    } +} + +// Avoid having to compile all of our LLSingleton machinery in Objective-C++. +CrashMetadata& CrashMetadata_instance() +{ +    return CrashMetadataSingleton::instance(); +} + +void infos(const std::string& message) +{ +    LL_INFOS() << message << LL_ENDL; +} +  int main( int argc, char **argv )   {  	// Store off the command line args for use later. diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index f0a4a54fbf..0de942d507 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -66,8 +66,101 @@  #endif  #include "stringize.h" +#include "lldir.h" +#include "llerrorcontrol.h" +#include <fstream>  #include <exception> + +// Bugsplat (http://bugsplat.com) crash reporting tool +#ifdef LL_BUGSPLAT +#include "BugSplat.h" +#include "reader.h"                 // JsonCpp +#include "llagent.h"                // for agent location +#include "llviewerregion.h" +#include "llvoavatarself.h"         // for agent name + +namespace +{ +    // MiniDmpSender's constructor is defined to accept __wchar_t* instead of +    // plain wchar_t*. That said, wunder() returns std::basic_string<__wchar_t>, +    // NOT plain __wchar_t*, despite the apparent convenience. Calling +    // wunder(something).c_str() as an argument expression is fine: that +    // std::basic_string instance will survive until the function returns. +    // Calling c_str() on a std::basic_string local to wunder() would be +    // Undefined Behavior: we'd be left with a pointer into a destroyed +    // std::basic_string instance. But we can do that with a macro... +    #define WCSTR(string) wunder(string).c_str() + +    // It would be nice if, when wchar_t is the same as __wchar_t, this whole +    // function would optimize away. However, we use it only for the arguments +    // to the BugSplat API -- a handful of calls. +    inline std::basic_string<__wchar_t> wunder(const std::wstring& str) +    { +        return { str.begin(), str.end() }; +    } + +    // when what we have in hand is a std::string, convert from UTF-8 using +    // specific wstringize() overload +    inline std::basic_string<__wchar_t> wunder(const std::string& str) +    { +        return wunder(wstringize(str)); +    } + +    // Irritatingly, MiniDmpSender::setCallback() is defined to accept a +    // classic-C function pointer instead of an arbitrary C++ callable. If it +    // did accept a modern callable, we could pass a lambda that binds our +    // MiniDmpSender pointer. As things stand, though, we must define an +    // actual function and store the pointer statically. +    static MiniDmpSender *sBugSplatSender = nullptr; + +    bool bugsplatSendLog(UINT nCode, LPVOID lpVal1, LPVOID lpVal2) +    { +        if (nCode == MDSCB_EXCEPTIONCODE) +        { +            // send the main viewer log file +            // widen to wstring, convert to __wchar_t, then pass c_str() +            sBugSplatSender->sendAdditionalFile( +                WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.log"))); + +            sBugSplatSender->sendAdditionalFile( +                WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "settings.xml"))); + +            sBugSplatSender->sendAdditionalFile( +                WCSTR(*LLAppViewer::instance()->getStaticDebugFile())); + +            // We don't have an email address for any user. Hijack this +            // metadata field for the platform identifier. +            sBugSplatSender->setDefaultUserEmail( +                WCSTR(STRINGIZE(LLOSInfo::instance().getOSStringSimple() << " (" +                                << ADDRESS_SIZE << "-bit)"))); + +            if (gAgentAvatarp) +            { +                // user name, when we have it +                sBugSplatSender->setDefaultUserName(WCSTR(gAgentAvatarp->getFullname())); +            } + +            // LL_ERRS message, when there is one +            sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage())); + +            if (gAgent.getRegion()) +            { +                // region location, when we have it +                LLVector3 loc = gAgent.getPositionAgent(); +                sBugSplatSender->resetAppIdentifier( +                    WCSTR(STRINGIZE(gAgent.getRegion()->getName() +                                    << '/' << loc.mV[0] +                                    << '/' << loc.mV[1] +                                    << '/' << loc.mV[2]))); +            } +        } // MDSCB_EXCEPTIONCODE + +        return false; +    } +} +#endif // LL_BUGSPLAT +  namespace  {      void (*gOldTerminateHandler)() = NULL; @@ -495,15 +588,69 @@ bool LLAppViewerWin32::init()  	LLWinDebug::instance();  #endif -#if LL_WINDOWS  #if LL_SEND_CRASH_REPORTS - +#if ! defined(LL_BUGSPLAT) +#pragma message("Building without BugSplat")  	LLAppViewer* pApp = LLAppViewer::instance();  	pApp->initCrashReporting(); -#endif -#endif +#else // LL_BUGSPLAT +#pragma message("Building with BugSplat") + +	std::string build_data_fname( +		gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "build_data.json")); +	std::ifstream inf(build_data_fname.c_str()); +	if (! inf.is_open()) +	{ +		LL_WARNS() << "Can't initialize BugSplat, can't read '" << build_data_fname +				   << "'" << LL_ENDL; +	} +	else +	{ +		Json::Reader reader; +		Json::Value build_data; +		if (! reader.parse(inf, build_data, false)) // don't collect comments +		{ +			// gah, the typo is baked into Json::Reader API +			LL_WARNS() << "Can't initialize BugSplat, can't parse '" << build_data_fname +					   << "': " << reader.getFormatedErrorMessages() << LL_ENDL; +		} +		else +		{ +			Json::Value BugSplat_DB = build_data["BugSplat DB"]; +			if (! BugSplat_DB) +			{ +				LL_WARNS() << "Can't initialize BugSplat, no 'BugSplat DB' entry in '" +						   << build_data_fname << "'" << LL_ENDL; +			} +			else +			{ +				// Got BugSplat_DB, onward! +				std::wstring version_string(WSTRINGIZE(LL_VIEWER_VERSION_MAJOR << '.' << +													   LL_VIEWER_VERSION_MINOR << '.' << +													   LL_VIEWER_VERSION_PATCH << '.' << +													   LL_VIEWER_VERSION_BUILD)); + +				// have to convert normal wide strings to strings of __wchar_t +				sBugSplatSender = new MiniDmpSender( +					WCSTR(BugSplat_DB.asString()), +					WCSTR(LL_TO_WSTRING(LL_VIEWER_CHANNEL)), +					WCSTR(version_string), +					nullptr,              // szAppIdentifier -- set later +					MDSF_NONINTERACTIVE | // automatically submit report without prompting +					MDSF_PREVENTHIJACKING); // disallow swiping Exception filter +				sBugSplatSender->setCallback(bugsplatSendLog); + +				// engage stringize() overload that converts from wstring +				LL_INFOS() << "Engaged BugSplat(" << LL_TO_STRING(LL_VIEWER_CHANNEL) +						   << ' ' << stringize(version_string) << ')' << LL_ENDL; +			} // got BugSplat_DB +		} // parsed build_data.json +	} // opened build_data.json + +#endif // LL_BUGSPLAT +#endif // LL_SEND_CRASH_REPORTS  	bool success = LLAppViewer::init(); diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index 375dce485d..4e07223784 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -101,14 +101,11 @@ namespace  {  	// LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The  	// macro expands to the string name of the channel, but without quotes. We -	// need to turn it into a quoted string. This macro trick does that. -#define stringize_inner(x) #x -#define stringize_outer(x) stringize_inner(x) - +	// need to turn it into a quoted string. LL_TO_STRING() does that.  	/// Storage of the channel name the viewer is using.  	//  The channel name is set by hardcoded constant,   	//  or by calling LLVersionInfo::resetChannel() -	std::string sWorkingChannelName(stringize_outer(LL_VIEWER_CHANNEL)); +	std::string sWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL));  	// Storage for the "version and channel" string.  	// This will get reset too. diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 4f0460da29..8b8ce3ca9e 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -44,6 +44,7 @@  #include "llagent.h"  #include "llagentcamera.h" +#include "llappviewer.h"  #include "llavatarrenderinfoaccountant.h"  #include "llcallingcard.h"  #include "llcommandhandler.h" @@ -104,6 +105,18 @@ typedef std::map<std::string, std::string> CapabilityMap;  static void log_capabilities(const CapabilityMap &capmap); +namespace +{ + +void newRegionEntry(LLViewerRegion& region) +{ +    LL_INFOS("LLViewerRegion") << "Entering region [" << region.getName() << "]" << LL_ENDL; +    gDebugInfo["CurrentRegion"] = region.getName(); +    LLAppViewer::instance()->writeDebugInfo(); +} + +} // anonymous namespace +  // support for secondlife:///app/region/{REGION} SLapps  // N.B. this is defined to work exactly like the classic secondlife://{REGION}  // However, the later syntax cannot support spaces in the region name because @@ -249,6 +262,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)              return; // this error condition is not recoverable.          } +        // record that we just entered a new region +        newRegionEntry(*regionp); +          // After a few attempts, continue login.  But keep trying to get the caps:          if (mSeedCapAttempts >= mSeedCapMaxAttemptsBeforeLogin &&              STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) @@ -369,6 +385,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle)              break; // this error condition is not recoverable.          } +        // record that we just entered a new region +        newRegionEntry(*regionp); +          LLSD capabilityNames = LLSD::emptyArray();          buildCapabilityNames(capabilityNames); diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index 2f7a4e9601..58f0469552 100644 --- a/indra/newview/tests/llversioninfo_test.cpp +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -33,10 +33,8 @@  // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The  // macro expands to the string name of the channel, but without quotes. We -// need to turn it into a quoted string. This macro trick does that. -#define stringize_inner(x) #x -#define stringize_outer(x) stringize_inner(x) -#define ll_viewer_channel stringize_outer(LL_VIEWER_CHANNEL) +// need to turn it into a quoted string. LL_TO_STRING() does that. +#define ll_viewer_channel LL_TO_STRING(LL_VIEWER_CHANNEL)  namespace tut  { diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 541112a765..464f7aa3e9 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -26,19 +26,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA  $/LicenseInfo$  """ -import sys -import os -import os.path -import shutil  import errno  import json +import os +import os.path  import plistlib  import random  import re +import shutil  import stat  import subprocess +import sys  import tarfile  import time +import zipfile  viewer_dir = os.path.dirname(__file__)  # Add indra/lib/python to our path so we don't have to muck with PYTHONPATH. @@ -63,7 +64,7 @@ class ViewerManifest(LLManifest):          self.path(src="../../etc/message.xml", dst="app_settings/message.xml")          if self.is_packaging_viewer(): -            with self.prefix(src="app_settings"): +            with self.prefix(src_dst="app_settings"):                  self.exclude("logcontrol.xml")                  self.exclude("logcontrol-dev.xml")                  self.path("*.ini") @@ -85,7 +86,7 @@ class ViewerManifest(LLManifest):                  # ... and the included spell checking dictionaries                  pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') -                with self.prefix(src=pkgdir,dst=""): +                with self.prefix(src=pkgdir):                      self.path("dictionaries")                  # include the extracted packages information (see BuildPackagesInfo.cmake) @@ -107,17 +108,18 @@ class ViewerManifest(LLManifest):                                          Type='String',                                          Value=''))                  settings_install = {} -                if 'sourceid' in self.args and self.args['sourceid']: +                sourceid = self.args.get('sourceid') +                if sourceid:                      settings_install['sourceid'] = settings_template['sourceid'].copy() -                    settings_install['sourceid']['Value'] = self.args['sourceid'] -                    print "Set sourceid in settings_install.xml to '%s'" % self.args['sourceid'] +                    settings_install['sourceid']['Value'] = sourceid +                    print "Set sourceid in settings_install.xml to '%s'" % sourceid -                if 'channel_suffix' in self.args and self.args['channel_suffix']: +                if self.args.get('channel_suffix'):                      settings_install['CmdLineChannel'] = settings_template['CmdLineChannel'].copy()                      settings_install['CmdLineChannel']['Value'] = self.channel_with_pkg_suffix()                      print "Set CmdLineChannel in settings_install.xml to '%s'" % self.channel_with_pkg_suffix() -                if 'grid' in self.args and self.args['grid']: +                if self.args.get('grid'):                      settings_install['CmdLineGridChoice'] = settings_template['CmdLineGridChoice'].copy()                      settings_install['CmdLineGridChoice']['Value'] = self.grid()                      print "Set CmdLineGridChoice in settings_install.xml to '%s'" % self.grid() @@ -129,20 +131,20 @@ class ViewerManifest(LLManifest):                                   src="environment") -            with self.prefix(src="character"): +            with self.prefix(src_dst="character"):                  self.path("*.llm")                  self.path("*.xml")                  self.path("*.tga")              # Include our fonts -            with self.prefix(src="fonts"): +            with self.prefix(src_dst="fonts"):                  self.path("*.ttf")                  self.path("*.txt")              # skins -            with self.prefix(src="skins"): +            with self.prefix(src_dst="skins"):                      # include the entire textures directory recursively -                    with self.prefix(src="*/textures"): +                    with self.prefix(src_dst="*/textures"):                              self.path("*/*.tga")                              self.path("*/*.j2c")                              self.path("*/*.jpg") @@ -170,7 +172,7 @@ class ViewerManifest(LLManifest):              # local_assets dir (for pre-cached textures) -            with self.prefix(src="local_assets"): +            with self.prefix(src_dst="local_assets"):                  self.path("*.j2c")                  self.path("*.tga") @@ -186,6 +188,10 @@ class ViewerManifest(LLManifest):                              "Address Size":self.address_size,                              "Update Service":"https://update.secondlife.com/update",                              } +            # Only store this if it's both present and non-empty +            bugsplat_db = self.args.get('bugsplat') +            if bugsplat_db: +                build_data_dict["BugSplat DB"] = bugsplat_db              build_data_dict = self.finish_build_data_dict(build_data_dict)              with open(os.path.join(os.pardir,'build_data.json'), 'w') as build_data_handle:                  json.dump(build_data_dict,build_data_handle) @@ -206,8 +212,9 @@ class ViewerManifest(LLManifest):      def channel_with_pkg_suffix(self):          fullchannel=self.channel() -        if 'channel_suffix' in self.args and self.args['channel_suffix']: -            fullchannel+=' '+self.args['channel_suffix'] +        channel_suffix = self.args.get('channel_suffix') +        if channel_suffix: +            fullchannel+=' '+channel_suffix          return fullchannel      def channel_variant(self): @@ -215,8 +222,7 @@ class ViewerManifest(LLManifest):          return self.channel().replace(CHANNEL_VENDOR_BASE, "").strip()      def channel_type(self): # returns 'release', 'beta', 'project', or 'test' -        global CHANNEL_VENDOR_BASE -        channel_qualifier=self.channel().replace(CHANNEL_VENDOR_BASE, "").lower().strip() +        channel_qualifier=self.channel_variant().lower()          if channel_qualifier.startswith('release'):              channel_type='release'          elif channel_qualifier.startswith('beta'): @@ -234,11 +240,12 @@ class ViewerManifest(LLManifest):          if self.channel_type() == 'release':              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())) +        if suffix: +            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())) +        # ''.split() produces empty list, so suffix only changes if +        # channel_suffix is non-empty +        suffix = "_".join([suffix] + self.args.get('channel_suffix', '').split())          return suffix      def installer_base_name(self): @@ -488,19 +495,19 @@ class WindowsManifest(ViewerManifest):              # 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()) -            with self.prefix(src=os.path.join(pkgdir, "VMP"), dst=""): +            with self.prefix(src=os.path.join(pkgdir, "VMP")):                  # include the compiled launcher scripts so that it gets included in the file_list                  self.path('SL_Launcher.exe')                  #IUM is not normally executed directly, just imported.  No exe needed.                  self.path("InstallerUserMessage.py") -            with self.prefix(src=self.icon_path(), dst="vmp_icons"): -                self.path("secondlife.ico") - -            #VMP  Tkinter icons -            with self.prefix("vmp_icons"): -                self.path("*.png") -                self.path("*.gif") +            with self.prefix(dst="vmp_icons"): +                with self.prefix(src=self.icon_path()): +                    self.path("secondlife.ico") +                #VMP  Tkinter icons +                with self.prefix(src="vmp_icons"): +                    self.path("*.png") +                    self.path("*.gif")              #before, we only needed llbase at build time.  With VMP, we need it at run time.              with self.prefix(src=os.path.join(pkgdir, "lib", "python", "llbase"), dst="llbase"): @@ -513,8 +520,8 @@ class WindowsManifest(ViewerManifest):                             "slplugin.exe")          # Get shared libs from the shared libs staging directory -        with self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), -                       dst=""): +        with self.prefix(src=os.path.join(self.args['build'], os.pardir, +                                          'sharedlibs', self.args['configuration'])):              # Get llcommon and deps. If missing assume static linkage and continue.              try: @@ -580,6 +587,17 @@ class WindowsManifest(ViewerManifest):              # Hunspell              self.path("libhunspell.dll") +            # BugSplat +            if self.args.get('bugsplat'): +                if(self.address_size == 64): +                    self.path("BsSndRpt64.exe") +                    self.path("BugSplat64.dll") +                    self.path("BugSplatRc64.dll") +                else: +                    self.path("BsSndRpt.exe") +                    self.path("BugSplat.dll") +                    self.path("BugSplatRc.dll") +              # For google-perftools tcmalloc allocator.              try:                  if self.args['configuration'].lower() == 'debug': @@ -589,114 +607,116 @@ class WindowsManifest(ViewerManifest):              except:                  print "Skipping libtcmalloc_minimal.dll" -          self.path(src="licenses-win32.txt", dst="licenses.txt")          self.path("featuretable.txt") -        with self.prefix(src=pkgdir,dst=""): +        with self.prefix(src=pkgdir):              self.path("ca-bundle.crt")          # Media plugins - CEF -        with self.prefix(src='../media_plugins/cef/%s' % self.args['configuration'], dst="llplugin"): -            self.path("media_plugin_cef.dll") - -        # Media plugins - LibVLC -        with self.prefix(src='../media_plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): -            self.path("media_plugin_libvlc.dll") - -        # Media plugins - Example (useful for debugging - not shipped with release viewer) -        if self.channel_type() != 'release': -            with self.prefix(src='../media_plugins/example/%s' % self.args['configuration'], dst="llplugin"): -                self.path("media_plugin_example.dll") - -        # CEF runtime files - debug -        # CEF runtime files - not debug (release, relwithdebinfo etc.) -        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"): -            self.path("msvcp120.dll") -            self.path("msvcr120.dll") - -        # CEF files common to all configurations -        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") -            self.path("cef_extensions.pak") -            self.path("devtools_resources.pak") -            self.path("icudtl.dat") - -        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") -            self.path("bn.pak") -            self.path("ca.pak") -            self.path("cs.pak") -            self.path("da.pak") -            self.path("de.pak") -            self.path("el.pak") -            self.path("en-GB.pak") -            self.path("en-US.pak") -            self.path("es-419.pak") -            self.path("es.pak") -            self.path("et.pak") -            self.path("fa.pak") -            self.path("fi.pak") -            self.path("fil.pak") -            self.path("fr.pak") -            self.path("gu.pak") -            self.path("he.pak") -            self.path("hi.pak") -            self.path("hr.pak") -            self.path("hu.pak") -            self.path("id.pak") -            self.path("it.pak") -            self.path("ja.pak") -            self.path("kn.pak") -            self.path("ko.pak") -            self.path("lt.pak") -            self.path("lv.pak") -            self.path("ml.pak") -            self.path("mr.pak") -            self.path("ms.pak") -            self.path("nb.pak") -            self.path("nl.pak") -            self.path("pl.pak") -            self.path("pt-BR.pak") -            self.path("pt-PT.pak") -            self.path("ro.pak") -            self.path("ru.pak") -            self.path("sk.pak") -            self.path("sl.pak") -            self.path("sr.pak") -            self.path("sv.pak") -            self.path("sw.pak") -            self.path("ta.pak") -            self.path("te.pak") -            self.path("th.pak") -            self.path("tr.pak") -            self.path("uk.pak") -            self.path("vi.pak") -            self.path("zh-CN.pak") -            self.path("zh-TW.pak") - -        with self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="llplugin"): -            self.path("libvlc.dll") -            self.path("libvlccore.dll") -            self.path("plugins/") +        with self.prefix(dst="llplugin"): +            with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins')): +                with self.prefix(src=os.path.join('cef', self.args['configuration'])): +                    self.path("media_plugin_cef.dll") + +                # Media plugins - LibVLC +                with self.prefix(src=os.path.join('libvlc', self.args['configuration'])): +                    self.path("media_plugin_libvlc.dll") + +                # Media plugins - Example (useful for debugging - not shipped with release viewer) +                if self.channel_type() != 'release': +                    with self.prefix(src=os.path.join('example', self.args['configuration'])): +                        self.path("media_plugin_example.dll") + +            # CEF runtime files - debug +            # CEF runtime files - not debug (release, relwithdebinfo etc.) +            config = 'debug' if self.args['configuration'].lower() == 'debug' else 'release' +            with self.prefix(src=os.path.join(pkgdir, 'bin', config)): +                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(self.args['build'], os.pardir, +                                              'sharedlibs', 'Release')): +                self.path("msvcp120.dll") +                self.path("msvcr120.dll") + +            # CEF files common to all configurations +            with self.prefix(src=os.path.join(pkgdir, 'resources')): +                self.path("cef.pak") +                self.path("cef_100_percent.pak") +                self.path("cef_200_percent.pak") +                self.path("cef_extensions.pak") +                self.path("devtools_resources.pak") +                self.path("icudtl.dat") + +            with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst='locales'): +                self.path("am.pak") +                self.path("ar.pak") +                self.path("bg.pak") +                self.path("bn.pak") +                self.path("ca.pak") +                self.path("cs.pak") +                self.path("da.pak") +                self.path("de.pak") +                self.path("el.pak") +                self.path("en-GB.pak") +                self.path("en-US.pak") +                self.path("es-419.pak") +                self.path("es.pak") +                self.path("et.pak") +                self.path("fa.pak") +                self.path("fi.pak") +                self.path("fil.pak") +                self.path("fr.pak") +                self.path("gu.pak") +                self.path("he.pak") +                self.path("hi.pak") +                self.path("hr.pak") +                self.path("hu.pak") +                self.path("id.pak") +                self.path("it.pak") +                self.path("ja.pak") +                self.path("kn.pak") +                self.path("ko.pak") +                self.path("lt.pak") +                self.path("lv.pak") +                self.path("ml.pak") +                self.path("mr.pak") +                self.path("ms.pak") +                self.path("nb.pak") +                self.path("nl.pak") +                self.path("pl.pak") +                self.path("pt-BR.pak") +                self.path("pt-PT.pak") +                self.path("ro.pak") +                self.path("ru.pak") +                self.path("sk.pak") +                self.path("sl.pak") +                self.path("sr.pak") +                self.path("sv.pak") +                self.path("sw.pak") +                self.path("ta.pak") +                self.path("te.pak") +                self.path("th.pak") +                self.path("tr.pak") +                self.path("uk.pak") +                self.path("vi.pak") +                self.path("zh-CN.pak") +                self.path("zh-TW.pak") + +            with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): +                self.path("libvlc.dll") +                self.path("libvlccore.dll") +                self.path("plugins/")          # pull in the crash logger and updater from other projects          # tag:"crash-logger" here as a cue to the exporter @@ -879,15 +899,25 @@ class DarwinManifest(ViewerManifest):          # darwin requires full app bundle packaging even for debugging.          return True +    def is_rearranging(self): +        # That said, some stuff should still only be performed once. +        # Are either of these actions in 'actions'? Is the set intersection +        # non-empty? +        return bool(set(["package", "unpacked"]).intersection(self.args['actions'])) +      def construct(self):          # 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" +        toplevel_app = self.channel()+".app"  +        toplevel_icon = "secondlife.icns"          launcher_app, launcher_icon = "Second Life Launcher.app", "secondlife.icns"          viewer_app,   viewer_icon   = "Second Life Viewer.app",   "secondlife.icns" +        # capture the path to the directory containing toplevel_app +        parentdir = os.path.join(self.get_dst_prefix(), os.pardir) +          # copy over the build result (this is a no-op if run within the xcode script)          self.path(os.path.join(self.args['configuration'], toplevel_app), dst="") @@ -897,27 +927,27 @@ class DarwinManifest(ViewerManifest):          # -------------------- top-level Second Life.app ---------------------          # top-level Second Life application is only a container -        with self.prefix(src="", dst="Contents"):  # everything goes in Contents +        with self.prefix(dst="Contents"):  # everything goes in Contents              # 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) +            Info_plist = self.dst_path_of("Info.plist") +            toplevel_MacOS = self.dst_path_of("MacOS")              # 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("""\ +            if not self.is_rearranging(): +                trampoline = "" +            else: +                with self.prefix(dst="MacOS"): +                    trampoline = self.put_in_file("""\  #!/bin/bash  open "%s" --args "$@"  """ % -                    # 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 -                self.run_command(["chmod", "+x", trampoline]) +                        # 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 +                    self.run_command(["chmod", "+x", trampoline])              # Make a symlink to a nested app Frameworks directory that doesn't              # yet exist. We shouldn't need this; the only things that need @@ -929,10 +959,10 @@ open "%s" --args "$@"              # rather than relsymlinkf().              self.symlinkf(os.path.join("Resources", viewer_app, "Contents", "Frameworks")) -            with self.prefix(src="", dst="Resources"): +            with self.prefix(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="") : +                with self.prefix(src=self.icon_path()) :                      self.path(toplevel_icon)                  # ------------------- nested launcher_app -------------------- @@ -952,15 +982,15 @@ open "%s" --args "$@"                          #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=""): +                        with self.prefix(src=os.path.join(pkgdir, "VMP")):                              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=""): +                        with self.prefix(src=os.path.join(pkgdir, "lib", "python")):                              # llbase provides our llrest service layer and llsd decoding -                            with self.prefix("llbase"): +                            with self.prefix(src="llbase", dst="llbase"):                                  # (Why is llbase treated specially here? What                                  # DON'T we want to copy out of lib/python/llbase?)                                  self.path("*.py") @@ -973,86 +1003,111 @@ open "%s" --args "$@"                      # launcher_app/Contents/Resources                      with self.prefix(dst="Resources"): -                        with self.prefix(src=self.icon_path(), dst="") : +                        with self.prefix(src=self.icon_path()) :                              self.path(launcher_icon)                              with self.prefix(dst="vmp_icons"):                                  self.path("secondlife.ico")                          #VMP Tkinter icons -                        with self.prefix("vmp_icons"): +                        with self.prefix(src_dst="vmp_icons"):                              self.path("*.png")                              self.path("*.gif")                  # -------------------- nested viewer_app ---------------------                  with self.prefix(dst=os.path.join(viewer_app, "Contents")): +                    # defer Info.plist until after MacOS +                    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   = self.dst_path_of(f) +                            fromrel   = self.relpath(fromwhere, relbase) +                            torel     = self.relpath(towhere, relbase) +                            if not self.is_rearranging(): +                                print "Not yet moving {} => {}".format(fromrel, torel) +                            else: +                                print "Moving {} => {}".format(fromrel, torel) +                                # now do it, only without relativizing paths +                                os.rename(fromwhere, towhere) + +                        # If we haven't yet moved executables, find our viewer +                        # executable where it was linked, in toplevel_MacOS. +                        # If we have, find it here. +                        whichdir = here if self.is_rearranging() else toplevel_MacOS +                        # Pick the biggest of the executables as the real viewer. +                        # Make (basename, fullpath) pairs; for each pair, +                        # expand to (size, basename, fullpath) triples; sort +                        # by size; pick the last triple; take the basename and +                        # fullpath from that. +                        _, exename, exepath = \ +                            sorted((os.path.getsize(path), name, path) +                                   for name, path in +                                   ((name, os.path.join(whichdir, name)) +                                    for name in os.listdir(whichdir)))[-1] + +                        if self.is_rearranging(): +                            # 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. +                            self.run_command(['strip', '-S', exepath]) +                      # 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["CFBundleExecutable"] = exename                      Info["CFBundleIconFile"] = viewer_icon +                    bugsplat_db = self.args.get('bugsplat') +                    if bugsplat_db: +                        # https://www.bugsplat.com/docs/platforms/os-x#configuration +                        Info["BugsplatServerURL"] = \ +                            "https://{}.bugsplat.com/".format(bugsplat_db)                      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"): +                    with self.prefix(dst="Frameworks"): +                        # CEF framework goes inside viewer_app/Contents/Frameworks.                          CEF_framework = "Chromium Embedded Framework.framework"                          self.path2basename(relpkgdir, CEF_framework) +                        # Remember where we parked this car.                          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')]) +                        if self.args.get('bugsplat'): +                            self.path2basename(relpkgdir, "BugsplatMac.framework")                      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="") : +                        with self.prefix(src=self.icon_path()) :                              self.path(viewer_icon) -                        with self.prefix(src=relpkgdir, dst=""): +                        with self.prefix(src=relpkgdir):                              self.path("libndofdev.dylib")                              self.path("libhunspell-1.3.0.dylib")    -                        with self.prefix("cursors_mac"): +                        with self.prefix(src_dst="cursors_mac"):                              self.path("*.tif")                          self.path("licenses-mac.txt", dst="licenses.txt")                          self.path("featuretable_mac.txt")                          self.path("SecondLife.nib") -                        with self.prefix(src=pkgdir,dst=""): +                        with self.prefix(src=pkgdir):                              self.path("ca-bundle.crt")                          self.path("SecondLife.nib") @@ -1153,13 +1208,14 @@ open "%s" --args "$@"                          # 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) +                        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")) @@ -1265,7 +1321,7 @@ open "%s" --args "$@"                              with self.prefix(src=relpkgdir, dst="lib"):                                  self.path( "libvlc*.dylib*" )                                  # copy LibVLC plugins folder -                                with self.prefix(src='plugins', dst=""): +                                with self.prefix(src='plugins'):                                      self.path( "*.dylib" )                                      self.path( "plugins.dat" ) @@ -1458,24 +1514,24 @@ class LinuxManifest(ViewerManifest):          debpkgdir = os.path.join(pkgdir, "lib", "debug")          self.path("licenses-linux.txt","licenses.txt") -        with self.prefix("linux_tools", dst=""): +        with self.prefix("linux_tools"):              self.path("client-readme.txt","README-linux.txt")              self.path("client-readme-voice.txt","README-linux-voice.txt")              self.path("client-readme-joystick.txt","README-linux-joystick.txt")              self.path("wrapper.sh","secondlife") -            with self.prefix(src="", dst="etc"): +            with self.prefix(dst="etc"):                  self.path("handle_secondlifeprotocol.sh")                  self.path("register_secondlifeprotocol.sh")                  self.path("refresh_desktop_app_entry.sh")                  self.path("launch_url.sh")              self.path("install.sh") -        with self.prefix(src="", dst="bin"): +        with self.prefix(dst="bin"):              self.path("secondlife-bin","do-not-directly-run-secondlife-bin")              self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin")              self.path2basename("../llplugin/slplugin", "SLPlugin")               #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 -            with self.prefix(src="../viewer_components/manager", dst=""): +            with self.prefix(src="../viewer_components/manager"):                  self.path("SL_Launcher")                  self.path("*.py")              with self.prefix(src=os.path.join("lib", "python", "llbase"), dst="llbase"): @@ -1488,9 +1544,9 @@ class LinuxManifest(ViewerManifest):          # Get the icons based on the channel type          icon_path = self.icon_path()          print "DEBUG: icon_path '%s'" % icon_path -        with self.prefix(src=icon_path, dst="") : +        with self.prefix(src=icon_path) :              self.path("secondlife_256.png","secondlife_icon.png") -            with self.prefix(src="",dst="res-sdl") : +            with self.prefix(dst="res-sdl") :                  self.path("secondlife_256.BMP","ll_icon.BMP")          # plugins @@ -1512,7 +1568,7 @@ class LinuxManifest(ViewerManifest):          self.path("featuretable_linux.txt") -        with self.prefix(src=pkgdir,dst=""): +        with self.prefix(src=pkgdir):              self.path("ca-bundle.crt")      def package_finish(self): @@ -1568,7 +1624,7 @@ class Linux_i686_Manifest(LinuxManifest):          relpkgdir = os.path.join(pkgdir, "lib", "release")          debpkgdir = os.path.join(pkgdir, "lib", "debug") -        with self.prefix(relpkgdir, dst="lib"): +        with self.prefix(src=relpkgdir, dst="lib"):              self.path("libapr-1.so")              self.path("libapr-1.so.0")              self.path("libapr-1.so.0.4.5") @@ -1654,4 +1710,8 @@ class Linux_x86_64_Manifest(LinuxManifest):  ################################################################  if __name__ == "__main__": -    main() +    extra_arguments = [ +        dict(name='bugsplat', description="""BugSplat database to which to post crashes, +             if BugSplat crash reporting is desired""", default=''), +        ] +    main(extra=extra_arguments) | 
