diff options
| author | Nat Goodspeed <nat@lindenlab.com> | 2018-10-17 10:31:59 -0400 | 
|---|---|---|
| committer | Nat Goodspeed <nat@lindenlab.com> | 2018-10-17 10:31:59 -0400 | 
| commit | 036c52fcdf425fe79a9aa6682cd7c7fe709a2442 (patch) | |
| tree | 80b637dd5876fa63fa247567aa5c267cfaf10441 | |
| parent | 3abc0095c9e39faf8976ef815b0da656423c4905 (diff) | |
| parent | bffe112cf5a1b9dd8acf5146c733a0fe186f6e89 (diff) | |
Automated merge with ssh://bitbucket.org/nat_linden/viewer-poseidon
37 files changed, 804 insertions, 238 deletions
| @@ -541,3 +541,4 @@ ad0e15543836d64d6399d28b32852510435e344a 5.1.0-release  ac3b1332ad4f55b7182a8cbcc1254535a0069f75 5.1.7-release  23ea0fe36fadf009a60c080392ce80e4bf8af8d9 5.1.8-release  52422540bfe54b71155aa455360bee6e3ef1fd96 5.1.9-release +1cfa567caf5088ae299271be08cc2d9f0801ff6a pre-Poseidon 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/llerror.cpp b/indra/llcommon/llerror.cpp index 06c7aef8ab..6dfb4bf028 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -368,6 +368,7 @@ namespace  	public:  		std::ostringstream messageStream;  		bool messageStreamInUse; +		std::string mFatalMessage;  		void addCallSite(LLError::CallSite&);  		void invalidateCallSites(); @@ -666,11 +667,16 @@ namespace LLError  		s->mCrashFunction = f;  	} -    FatalFunction getFatalFunction() -    { +	FatalFunction getFatalFunction() +	{  		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); -        return s->mCrashFunction; -    } +		return s->mCrashFunction; +	} + +	std::string getFatalMessage() +	{ +		return Globals::getInstance()->mFatalMessage; +	}  	void setTimeFunction(TimeFunction f)  	{ @@ -1256,12 +1262,17 @@ namespace LLError  		}  		addEscapedMessage(message_stream, message); +		std::string message_line(message_stream.str()); -		writeToRecorders(site, message_stream.str()); -		 -		if (site.mLevel == LEVEL_ERROR  &&  s->mCrashFunction) +		writeToRecorders(site, message_line); + +		if (site.mLevel == LEVEL_ERROR)  		{ -			s->mCrashFunction(message_stream.str()); +			g->mFatalMessage = message_line; +			if (s->mCrashFunction) +			{ +				s->mCrashFunction(message_line); +			}  		}  	}  } 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/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 31b4201f47..9482f07524 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..1d55537427 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -25,7 +25,16 @@   */  #import "llappdelegate-objc.h" +#if defined(LL_BUGSPLAT) +#include <boost/filesystem.hpp> +#include <vector> +@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 +56,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 +207,138 @@      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"); +} + +struct AttachmentInfo +{ +    AttachmentInfo(const std::string& path, const std::string& type): +        pathname(path), +        basename(boost::filesystem::path(path).filename().string()), +        mimetype(type) +    {} + +    std::string pathname, basename, mimetype; +}; + +- (NSArray<BugsplatAttachment *> *)attachmentsForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +{ +    const CrashMetadata& metadata(CrashMetadata_instance()); + +    // Since we must do very similar processing for each of several file +    // pathnames, start by collecting them into a vector so we can iterate +    // instead of spelling out the logic for each. +    std::vector<AttachmentInfo> info{ +        AttachmentInfo(metadata.logFilePathname,      "text/plain"), +        AttachmentInfo(metadata.userSettingsPathname, "text/xml"), +        AttachmentInfo(metadata.staticDebugPathname,  "text/xml") +    }; + +    // We "happen to know" that info[0].basename is "SecondLife.old" -- due to +    // the fact that BugsplatMac only notices a crash during the viewer run +    // following the crash. Replace .old with .log to reduce confusion. +    info[0].basename =  +        boost::filesystem::path(info[0].pathname).stem().string() + ".log"; + +    NSMutableArray *attachments = [[NSMutableArray alloc] init]; + +    // Iterate over each AttachmentInfo in info vector +    for (const AttachmentInfo& attach : info) +    { +        NSString *nspathname = [NSString stringWithCString:attach.pathname.c_str() +                                                  encoding:NSUTF8StringEncoding]; +        NSString *nsbasename = [NSString stringWithCString:attach.basename.c_str() +                                                  encoding:NSUTF8StringEncoding]; +        NSString *nsmimetype = [NSString stringWithCString:attach.mimetype.c_str() +                                                  encoding:NSUTF8StringEncoding]; +        NSData *nsdata = [NSData dataWithContentsOfFile:nspathname]; + +        BugsplatAttachment *attachment = +            [[BugsplatAttachment alloc] initWithFilename:nsbasename +                                          attachmentData:nsdata +                                             contentType:nsmimetype]; + +        [attachments addObject:attachment]; +        infos("attachmentsForBugsplatStartupManager attaching " + attach.pathname); +    } + +    return attachments; +} + +- (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 bc4ce19f77..56e79bed74 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 @@ -2182,6 +2191,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);  } @@ -3049,14 +3064,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  { | 
