diff options
| author | Nat Goodspeed <nat@lindenlab.com> | 2018-08-02 16:15:34 -0400 | 
|---|---|---|
| committer | Nat Goodspeed <nat@lindenlab.com> | 2018-08-02 16:15:34 -0400 | 
| commit | 67955043306bddcb22cee706415fe702807e1a30 (patch) | |
| tree | d13e4ea862af00dbb78dbed4468eb78a03eff28b | |
| parent | 09f97172bb478a2c977d8b7b0958196e7e98c433 (diff) | |
| parent | f1eb3c1b63b97de2a91c967e5c66f7b25c4963a2 (diff) | |
Automated merge with ssh://bitbucket.org/lindenlab/viewer-release
| -rwxr-xr-x | build.sh | 32 | ||||
| -rw-r--r-- | indra/cmake/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | indra/cmake/Copy3rdPartyLibs.cmake | 14 | ||||
| -rw-r--r-- | indra/cmake/Variables.cmake | 1 | ||||
| -rw-r--r-- | indra/cmake/bugsplat.cmake | 26 | ||||
| -rwxr-xr-x | indra/lib/python/indra/util/llmanifest.py | 51 | ||||
| -rw-r--r-- | indra/llappearance/lllocaltextureobject.cpp | 1 | ||||
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | indra/llcommon/llapp.cpp | 23 | ||||
| -rw-r--r-- | indra/llcommon/llerror.cpp | 33 | ||||
| -rw-r--r-- | indra/llcommon/llerrorcontrol.h | 3 | ||||
| -rw-r--r-- | indra/llcommon/llpreprocessor.h | 2 | ||||
| -rw-r--r-- | indra/llcommon/stringize.h | 52 | ||||
| -rw-r--r-- | indra/llcommon/tests/wrapllerrs.h | 14 | ||||
| -rw-r--r-- | indra/newview/Info-SecondLife.plist | 2 | ||||
| -rw-r--r-- | indra/newview/llappdelegate-objc.mm | 65 | ||||
| -rw-r--r-- | indra/newview/llappviewermacosx-for-objc.h | 37 | ||||
| -rw-r--r-- | indra/newview/llappviewermacosx.cpp | 23 | ||||
| -rw-r--r-- | indra/newview/llappviewerwin32.cpp | 150 | ||||
| -rw-r--r-- | indra/newview/llversioninfo.cpp | 7 | ||||
| -rw-r--r-- | indra/newview/tests/llversioninfo_test.cpp | 6 | ||||
| -rwxr-xr-x | indra/newview/viewer_manifest.py | 426 | 
22 files changed, 711 insertions, 263 deletions
| @@ -95,26 +95,36 @@ 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" ] -    then -         if [ "$variant" == "Release" ] -         then SIGNING=("-DENABLE_SIGNING:BOOL=YES" \ -                       "-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.") +    if [ "$arch" == "Darwin" -a "$variant" == "Release" ] +    then SIGNING=("-DENABLE_SIGNING:BOOL=YES" \ +                  "-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.") +    fi + +    # don't spew credentials into build log +    bugsplat_sh="$build_secrets_checkout/bugsplat/bugsplat.sh" +    set +x +    # HACK: Suppress for Mac until BugSplat fixes the Mac client API +    if [ -r "$bugsplat_sh" -a "$arch" != "Darwin" ] +    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_CHANNEL:STRING="${viewer_channel}" \       -DGRID:STRING="\"$viewer_grid\"" \ -     -DLL_TESTS:BOOL="$run_tests" \       -DTEMPLATE_VERIFIER_OPTIONS:STRING="$template_verifier_options" $template_verifier_master_url \       "${SIGNING[@]}" \      || fatal "$variant configuration failed" @@ -194,6 +204,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" @@ -415,7 +427,9 @@ then        if [ "$last_built_variant" = "Release" ]        then            # nat 2016-12-22: without RELEASE_CRASH_REPORTING, we have no symbol file. -          if [ "${RELEASE_CRASH_REPORTING:-}" != "OFF" ] +          # Likewise, BUGSPLAT_DB suppresses generating the symbol file. +          if [ "${RELEASE_CRASH_REPORTING:-}" != "OFF" \ +               -a -z "${BUGSPLAT_DB:-}" ]            then                # Upload crash reporter file                # These names must match the set of VIEWER_SYMBOL_FILE in indra/newview/CMakeLists.txt 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..c9519b0e1d 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 (DEFINED ENV{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 (DEFINED ENV{BUGSPLAT_DB}) +      if (FMODEX)          if(ADDRESS_SIZE EQUAL 32) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index bd69c49856..b913d6398e 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -209,7 +209,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..eb5808b1fb --- /dev/null +++ b/indra/cmake/bugsplat.cmake @@ -0,0 +1,26 @@ +# BugSplat is engaged by setting environment variable BUGSPLAT_DB to the +# target BugSplat database name prior to running CMake (and during autobuild +# build). +if (DEFINED ENV{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 (DEFINED ENV{BUGSPLAT_DB}) diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 598261e3d7..974ac18e34 100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -375,7 +375,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 +385,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 +413,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 +481,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 @@ -609,7 +632,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 +699,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 +713,7 @@ class LLManifest(object):                  if err.errno != errno.ENOENT:                      raise +##          print "{} => {}".format(src, reldst)              shutil.copy2(src, dst)      def ccopytree(self, src, dst): 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..8977acb873 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 (DEFINED ENV{BUGSPLAT_DB}) +  set_source_files_properties(llapp.cpp +    PROPERTIES COMPILE_DEFINITIONS "LL_BUGSPLAT") +endif (DEFINED ENV{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 29de79dc64..64b957b188 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -367,6 +367,7 @@ namespace  	public:  		std::ostringstream messageStream;  		bool messageStreamInUse; +		std::string mFatalMessage;  		void addCallSite(LLError::CallSite&);  		void invalidateCallSites(); @@ -660,11 +661,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)  	{ @@ -1184,7 +1190,7 @@ namespace LLError  		{  			writeToRecorders(site, "error", true, true, true, false, false);  		} -		 +  		std::ostringstream message_stream;  		if (site.mPrintOnce) @@ -1209,14 +1215,19 @@ namespace LLError  				s->mUniqueLogMessages[message] = 1;  			}  		} -		 +  		message_stream << message; -		 -		writeToRecorders(site, message_stream.str()); -		 -		if (site.mLevel == LEVEL_ERROR  &&  s->mCrashFunction) +		std::string message_line(message_stream.str()); + +		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/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..82e49540a4 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 @@ -64,6 +71,14 @@  	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];   //   [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; + +#if defined(LL_BUGSPLAT) +	// 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  }  - (void) handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { @@ -179,4 +194,54 @@      return true;  } +#if defined(LL_BUGSPLAT) + +- (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +{ +    infos("Reached applicationLogForBugsplatStartupManager"); +    // Apparently this override method only contributes the User Description +    // field of BugSplat's All Crashes table. Despite the method name, it +    // would seem to be a bad place to try to stuff all of SecondLife.log. +    return [NSString stringWithCString:getFatalMessage().c_str() +                              encoding:NSUTF8StringEncoding]; +} + +- (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugsplatStartupManager +{ +    infos("Reached bugsplatStartupManagerWillSendCrashReport"); +} + +- (BugsplatAttachment *)attachmentForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { +    // We get the *old* log file pathname (for SecondLife.old) because it's on +    // the run *following* the crash that BugsplatStartupManager notices that +    // the previous run crashed and calls this override. By that time, we've +    // already renamed SecondLife.log to SecondLife.old. +    std::string logfile = getOldLogFilePathname(); +    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/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h new file mode 100644 index 0000000000..ac85d7e8c3 --- /dev/null +++ b/indra/newview/llappviewermacosx-for-objc.h @@ -0,0 +1,37 @@ +/** + * @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> + +bool initViewer(); +void handleUrl(const char* url_utf8); +bool pumpMainLoop(); +void handleQuit(); +void cleanupViewer(); +std::string getOldLogFilePathname(); +std::string getFatalMessage(); +std::string getAgentFullname(); +void infos(const std::string& message); + +#endif /* ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) */ diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index d472f8926b..c3a3c3284a 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -36,6 +36,7 @@  #include "llappviewermacosx-objc.h"  #include "llappviewermacosx.h" +#include "llappviewermacosx-for-objc.h"  #include "llwindowmacosx-objc.h"  #include "llcommandlineparser.h" @@ -44,6 +45,8 @@  #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> @@ -147,6 +150,26 @@ void cleanupViewer()  	gViewerAppPtr = NULL;  } +std::string getOldLogFilePathname() +{ +    return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.old"); +} + +std::string getFatalMessage() +{ +    return LLError::getFatalMessage(); +} + +std::string getAgentFullname() +{ +    return gAgentAvatarp? gAgentAvatarp->getFullname() : std::string(); +} + +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 48b3a1c485..adb340d40e 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -66,8 +66,96 @@  #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"))); + +            // We don't have an email address for any user. Hijack this +            // metadata field for the platform identifier. +            sBugSplatSender->setDefaultUserEmail(WCSTR(STRINGIZE("Windows" << ADDRESS_SIZE))); + +            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 +583,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/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..ad6ba3ddff 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) @@ -129,20 +130,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 +171,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 +187,11 @@ class ViewerManifest(LLManifest):                              "Address Size":self.address_size,                              "Update Service":"https://update.secondlife.com/update",                              } +            try: +                build_data_dict["BugSplat DB"] = os.environ["BUGSPLAT_DB"] +            except KeyError: +                # skip the assignment if there's no BUGSPLAT_DB variable +                pass              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) @@ -215,8 +221,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'): @@ -488,19 +493,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 +518,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 +585,16 @@ class WindowsManifest(ViewerManifest):              # Hunspell              self.path("libhunspell.dll") +            # 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 +604,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 @@ -888,6 +905,9 @@ class DarwinManifest(ViewerManifest):          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,12 +917,9 @@ 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")              # the one file in top-level MacOS directory is the trampoline to              # our nested launcher_app @@ -929,10 +946,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 +969,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,36 +990,18 @@ 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")): -                    # Info.plist is just like top-level one... -                    Info = plistlib.readPlist(Info_plist) -                    # except for these replacements: -                    # (CFBundleExecutable may be moot: SL_Launcher directly -                    # runs the executable, instead of launching the app) -                    Info["CFBundleExecutable"] = "Second Life" -                    Info["CFBundleIconFile"] = viewer_icon -                    self.put_in_file( -                        plistlib.writePlistToString(Info), -                        os.path.basename(Info_plist), -                        "Info.plist") - -                    # CEF framework goes inside viewer_app/Contents/Frameworks. -                    # Remember where we parked this car. -                    with self.prefix(src="", dst="Frameworks"): -                        CEF_framework = "Chromium Embedded Framework.framework" -                        self.path2basename(relpkgdir, CEF_framework) -                        CEF_framework = self.dst_path_of(CEF_framework) - +                    # 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 @@ -1015,44 +1014,115 @@ open "%s" --args "$@"                                  # don't move the trampoline script we just made!                                  continue                              fromwhere = os.path.join(toplevel_MacOS, f) -                            towhere   = os.path.join(here, f) +                            towhere   = self.dst_path_of(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. +                        # 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(here, name)) +                                    for name in os.listdir(here)))[-1] +                          if ("package" in self.args['actions'] or                               "unpacked" in self.args['actions']): -                            self.run_command( -                                ['strip', '-S', self.dst_path_of('Second Life')]) +                            # only if we're engaging BugSplat +                            if "BUGSPLAT_DB" in os.environ: +                                # Create a symbol archive BEFORE stripping the +                                # binary. +                                self.run_command(['dsymutil', exepath]) +                                # This should produce a Second Life.dSYM bundle directory. +                                try: +                                    # Now pretend we're Xcode making a .xcarchive file. +                                    # Put it as a sibling of the top-level .app. +                                    # From "Dave" at BugSplat support: +                                    # "More from our Mac lead: I think zipping +                                    # a folder containing the binary and +                                    # symbols would be sufficient. Assuming +                                    # symbol files are created with CMake. I'm +                                    # not sure if CMake strips symbols into +                                    # separate files at build time, and if so +                                    # they're in a supported format." +                                    xcarchive = os.path.join(parentdir, +                                                             exename + '.xcarchive.zip') +                                    with zipfile.ZipFile(xcarchive, 'w', +                                                         compression=zipfile.ZIP_DEFLATED) as zf: +                                        print "Creating {}".format(xcarchive) +                                        for base, dirs, files in os.walk(here): +                                            for fn in files: +                                                fullfn = os.path.join(base, fn) +                                                relfn = os.path.relpath(fullfn, here) +                                                print "  {}".format(relfn) +                                                zf.write(fullfn, relfn) +                                finally: +                                    # Whether or not we were able to create the +                                    # .xcarchive file, clean up the .dSYM bundle +                                    shutil.rmtree(self.dst_path_of(exename + '.dSYM')) + +                            # 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"] = exename +                    Info["CFBundleIconFile"] = viewer_icon +                    try: +                        # https://www.bugsplat.com/docs/platforms/os-x#configuration +                        Info["BugsplatServerURL"] = \ +                            "https://{BUGSPLAT_DB}.bugsplatsoftware.com/".format(**os.environ) +                    except KeyError: +                        # skip the assignment if there's no BUGSPLAT_DB variable +                        pass +                    self.put_in_file( +                        plistlib.writePlistToString(Info), +                        os.path.basename(Info_plist), +                        "Info.plist") + +                    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) + +                        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") @@ -1265,7 +1335,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 +1528,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 +1558,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 +1582,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 +1638,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") | 
