diff --git a/autobuild.xml b/autobuild.xml index b3817c9b31..d4347e495f 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -429,6 +429,60 @@ version 1.57 + bugsplat + + copyright + Copyright 2003-2017, BugSplat + description + Bugsplat crash reporting package + license + Proprietary + license_file + LICENSES/BUGSPLAT_LICENSE.txt + name + bugsplat + platforms + + darwin64 + + archive + + hash + 0da51341172a14f06c323e240dd238f5 + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/21954/163369/bugsplat-1.0.2.517972-darwin64-517972.tar.bz2 + + name + darwin64 + + windows + + archive + + hash + d25adf0f2bcdbf59c035def0914fa1d5 + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/21955/163376/bugsplat-3.6.0.4.517972-windows-517972.tar.bz2 + + name + windows + + windows64 + + archive + + hash + 0a4fc1ddec4ea0ad2d75d5422ef1df2b + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/21953/163362/bugsplat-3.6.0.4.517972-windows64-517972.tar.bz2 + + name + windows64 + + + version + 1.0.2.517972 + chardet copyright @@ -3566,9 +3620,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 86f6708f393c162cd4f92426b0a3cde7 + 78e3b5f51554a186f8a62c0161f549d5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/15341/99062/viewer_manager-1.0.513570-darwin64-513570.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/21002/152053/viewer_manager-1.0.517331-darwin64-517331.tar.bz2 name darwin64 @@ -3602,9 +3656,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - c4dec51062ad78c09b11f7432aff4d1d + ccf1e6ba7811897f7d88af2a6fddceb0 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/17857/121832/viewer_manager-1.0.515286-windows-515286.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/21003/152060/viewer_manager-1.0.517331-windows-517331.tar.bz2 name windows @@ -3615,7 +3669,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors source_type hg version - 1.0.515286 + 1.0.517331 vlc-bin diff --git a/build.sh b/build.sh index e0250ce402..490e637a8b 100755 --- a/build.sh +++ b/build.sh @@ -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" @@ -412,7 +424,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 f3cc2697ec..2939f6b90b 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 CMakeCopyIfDifferent.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index e93cc504cd..0e1f50c7c5 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}) + set(release_files ${release_files} growl++.dll growl.dll ) if (FMODSTUDIO) if(ADDRESS_SIZE EQUAL 32) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 78180b03e5..8ef72d68d6 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -213,7 +213,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.") # When using Havok, we have to turn OpenSim support off 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 025347502d..38f720d77b 100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -379,7 +379,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: @@ -389,8 +389,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: @@ -404,34 +417,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: @@ -464,6 +485,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 @@ -628,7 +651,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) @@ -696,7 +718,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): @@ -706,6 +732,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 b7ad5e67cb..3fc406d51d 100644 --- a/indra/llappearance/lllocaltextureobject.cpp +++ b/indra/llappearance/lllocaltextureobject.cpp @@ -211,4 +211,3 @@ void LLLocalTextureObject::setBakedReady(BOOL ready) { mIsBakedReady = ready; } - diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 350305f573..2565651abd 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -281,6 +281,11 @@ list(APPEND llcommon_HEADER_FILES "tea.h" ) 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 a177fba444..94f07c7e78 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -402,7 +402,7 @@ void LLApp::setupErrorHandling(bool second_instance, EMiniDumpType minidump_type #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. @@ -481,8 +481,15 @@ void LLApp::setupErrorHandling(bool second_instance, EMiniDumpType minidump_type 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. // @@ -490,9 +497,11 @@ void LLApp::setupErrorHandling(bool second_instance, EMiniDumpType minidump_type // 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 @@ -525,7 +534,7 @@ void LLApp::setupErrorHandling(bool second_instance, EMiniDumpType minidump_type // installing the handler. installHandler = true; } - #endif + #endif // ! LL_RELEASE_FOR_DOWNLOAD if(installHandler && (mExceptionHandler == 0)) { @@ -541,9 +550,9 @@ void LLApp::setupErrorHandling(bool second_instance, EMiniDumpType minidump_type 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 720faeda39..0a40a91042 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -375,6 +375,7 @@ namespace public: std::ostringstream messageStream; bool messageStreamInUse; + std::string mFatalMessage; void addCallSite(LLError::CallSite&); void invalidateCallSites(); @@ -699,11 +700,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) { @@ -1182,7 +1188,7 @@ namespace LLError std::ostringstream prefix; if( nd::logging::throttle( site.mFile, site.mLine, &prefix ) ) return; - + std::ostringstream message_stream; if (site.mPrintOnce) @@ -1207,14 +1213,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 6958feffd1..14b98f872b 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -208,6 +208,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 -#include #include /** @@ -53,12 +52,7 @@ std::basic_string 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(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(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 -std::string stringize_f(Functor const & f) +template +std::basic_string stringize_f(Functor const & f) { - std::ostringstream out; + std::basic_ostringstream 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([&](std::ostream& out){ out << EXPRESSION; })) +/** + * WSTRINGIZE() is the wstring equivalent of STRINGIZE() + */ +#define WSTRINGIZE(EXPRESSION) (stringize_f([&](std::wostream& out){ out << EXPRESSION; })) /** * destringize(str) * defined for symmetry with stringize - * *NOTE - this has distinct behavior from boost::lexical_cast regarding + * @NOTE - this has distinct behavior from boost::lexical_cast 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 -T destringize(std::string const & str) +template +T destringize(std::basic_string const & str) { - T val; - std::istringstream in(str); - in >> val; + T val; + std::basic_istringstream in(str); + in >> val; return val; } /** * destringize_f(str, functor) */ -template -void destringize_f(std::string const & str, Functor const & f) +template +void destringize_f(std::basic_string const & str, Functor const & f) { - std::istringstream in(str); + std::basic_istringstream 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/CMakeLists.txt b/indra/newview/CMakeLists.txt index 678366fa31..ce13bfc871 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -3,7 +3,14 @@ project(viewer) include(00-Common) +# DON'T move Linking.cmake to its place in the alphabetized list below: it +# sets variables on which the 3p .cmake files depend. +include(Linking) + include(Boost) +if (DEFINED ENV{BUGSPLAT_DB}) + include(bugsplat) +endif (DEFINED ENV{BUGSPLAT_DB}) include(BuildPackagesInfo) include(BuildVersion) include(CMakeCopyIfDifferent) @@ -38,7 +45,6 @@ include(LLUI) include(LLVFS) include(LLWindow) include(LLXML) -include(Linking) include(NDOF) include(NVAPI) include(OPENAL) @@ -110,6 +116,12 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) +if (DEFINED ENV{BUGSPLAT_DB}) + include_directories( + ${BUGSPLAT_INCLUDE_DIR} + ) +endif (DEFINED ENV{BUGSPLAT_DB}) + include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ${LLXML_SYSTEM_INCLUDE_DIRS} @@ -1652,6 +1664,14 @@ if (DARWIN) # This should be compiled with the viewer. LIST(APPEND viewer_SOURCE_FILES llappdelegate-objc.mm) + set_source_files_properties( + llappdelegate-objc.mm + PROPERTIES + COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" + # BugsplatMac is a module, imported with @import. That language feature + # demands these switches. + COMPILE_FLAGS "-fmodules -fcxx-modules" + ) # [FS] Growl libs LIST(APPEND viewer_SOURCE_FILES @@ -1678,6 +1698,12 @@ if (DARWIN) ${COREAUDIO_LIBRARY} ) + if (DEFINED ENV{BUGSPLAT_DB}) + list(APPEND viewer_LIBRARIES + ${BUGSPLAT_LIBRARIES} + ) + endif (DEFINED ENV{BUGSPLAT_DB}) + # Add resource files to the project. set(viewer_RESOURCE_FILES firestorm_icon.icns @@ -1703,6 +1729,11 @@ endif (DARWIN) if (LINUX) LIST(APPEND viewer_SOURCE_FILES llappviewerlinux.cpp) + set_source_files_properties( + llappviewerlinux.cpp + PROPERTIES + COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" + ) LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp) # [FS] Growl support LIST(APPEND viewer_HEADER_FILES desktopnotifierlinux.h growlmanager.h) @@ -1737,6 +1768,11 @@ if (WINDOWS) llappviewerwin32.cpp llwindebug.cpp ) + set_source_files_properties( + llappviewerwin32.cpp + PROPERTIES + COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" + ) list(APPEND viewer_HEADER_FILES llappviewerwin32.h @@ -2075,6 +2111,11 @@ if (SDL_FOUND) ) endif (SDL_FOUND) +if (DEFINED ENV{BUGSPLAT_DB}) + set_property(TARGET ${VIEWER_BINARY_NAME} + PROPERTY COMPILE_DEFINITIONS "LL_BUGSPLAT") +endif (DEFINED ENV{BUGSPLAT_DB}) + # add package files file(GLOB EVENT_HOST_SCRIPT_GLOB_LIST ${CMAKE_CURRENT_SOURCE_DIR}/../viewer_components/*.py) @@ -2232,21 +2273,6 @@ if (WINDOWS) windows-crash-logger ) - # sets the 'working directory' for debugging from visual studio. - if (NOT UNATTENDED) - add_custom_command( - TARGET ${VIEWER_BINARY_NAME} POST_BUILD - COMMAND ${CMAKE_SOURCE_DIR}/tools/vstool/vstool.exe - ARGS - --solution - ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.sln - --workingdir - ${VIEWER_BINARY_NAME} - "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Setting the ${VIEWER_BINARY_NAME} working directory for debugging." - ) - endif (NOT UNATTENDED) - if (PACKAGE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2 @@ -2309,8 +2335,8 @@ else (WINDOWS) endif (WINDOWS) # *NOTE: - this list is very sensitive to ordering, test carefully on all -# platforms if you change the releative order of the entries here. -# In particular, cmake 2.6.4 (when buidling with linux/makefile generators) +# platforms if you change the relative order of the entries here. +# In particular, cmake 2.6.4 (when building with linux/makefile generators) # appears to sometimes de-duplicate redundantly listed dependencies improperly. # To work around this, higher level modules should be listed before the modules # that they depend upon. -brad @@ -2384,6 +2410,12 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLAPPEARANCE_LIBRARIES} ${GROWL_LIBRARY} ) + +if (DEFINED ENV{BUGSPLAT_DB}) + target_link_libraries(${VIEWER_BINARY_NAME} + ${BUGSPLAT_LIBRARIES} + ) +endif (DEFINED ENV{BUGSPLAT_DB}) if (WINDOWS) target_link_libraries(${VIEWER_BINARY_NAME} @@ -2479,25 +2511,35 @@ endif (NOT ENABLE_MEDIA_PLUGINS) endif (LINUX) if (DARWIN) - # These all get set with PROPERTIES - set(product "Firestorm") + # These all get set with PROPERTIES. It's not that the property names are + # magically known to CMake -- it's that these names are referenced in the + # Info-SecondLife.plist file in the configure_file() directive below. + set(product "${VIEWER_CHANNEL}") # this is the setting for the Python wrapper, see SL-322 and WRAPPER line in Info-SecondLife.plist set(MACOSX_WRAPPER_EXECUTABLE_NAME "SL_Launcher") - set(MACOSX_BUNDLE_INFO_STRING "Firestorm Viewer") + set(MACOSX_BUNDLE_INFO_STRING "${VIEWER_CHANNEL}") set(MACOSX_BUNDLE_ICON_FILE "firestorm_icon.icns") set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.phoenixviewer.firestorm.viewer-${ND_VIEWER_FLAVOR}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${VIEWER_CHANNEL} ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") set(MACOSX_BUNDLE_BUNDLE_NAME "Firestorm") - set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${VIEWER_SHORT_VERSION}${VIEWER_MACOSX_PHASE}${VIEWER_REVISION}") set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2010-2018 The Phoenix Firestorm Project, Inc.") set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "Firestorm.nib") set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "LLNSApplication") + # https://blog.kitware.com/upcoming-in-cmake-2-8-12-osx-rpath-support/ + set(CMAKE_MACOSX_RPATH 1) + set_target_properties( ${VIEWER_BINARY_NAME} PROPERTIES OUTPUT_NAME "${product}" + # From Contents/MacOS/SecondLife, look in Contents/Frameworks + INSTALL_RPATH "@loader_path/../Frameworks" + # SIGH, as of 2018-05-24 (cmake 3.11.1) the INSTALL_RPATH property simply + # does not work. Try this: + LINK_FLAGS "-rpath @loader_path/../Frameworks" MACOSX_BUNDLE_INFO_PLIST # Use Firestorm plist #"${CMAKE_CURRENT_SOURCE_DIR}/Info-SecondLife.plist" @@ -2573,7 +2615,7 @@ if (INSTALL) include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake) endif (INSTALL) -if (PACKAGE) +if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND NOT DEFINED ENV{BUGSPLAT_DB}) set(SYMBOL_SEARCH_DIRS "") # Note that the path to VIEWER_SYMBOL_FILE must match that in ../../build.sh if (WINDOWS) @@ -2592,8 +2634,8 @@ if (PACKAGE) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_crash_logger/${CMAKE_CFG_INTDIR}") list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}") set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/firestorm-symbols-darwin-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2") -## set(VIEWER_EXE_GLOBS "'Second Life' SLPlugin mac-crash-logger") - set(VIEWER_EXE_GLOBS "'Firestorm' mac-crash-logger") +## set(VIEWER_EXE_GLOBS "'${product}' SLPlugin mac-crash-logger") + set(VIEWER_EXE_GLOBS "'${product}' mac-crash-logger") set(VIEWER_LIB_GLOB "*.dylib") endif (DARWIN) if (LINUX) @@ -2605,7 +2647,6 @@ if (PACKAGE) set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) endif (LINUX) - if( RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING ) if(CMAKE_CFG_INTDIR STREQUAL ".") set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE}) else(CMAKE_CFG_INTDIR STREQUAL ".") @@ -2632,8 +2673,7 @@ if (PACKAGE) add_dependencies(generate_breakpad_symbols "${VIEWER_COPY_MANIFEST}") endif (WINDOWS OR LINUX) add_dependencies(llpackage generate_breakpad_symbols) - endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) -endif (PACKAGE) +endif () if (LL_TESTS) # To add a viewer unit test, just add the test .cpp file below diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index af4cf26ac6..8aabd6818b 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -21,7 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion diff --git a/indra/newview/fs_viewer_manifest.py b/indra/newview/fs_viewer_manifest.py index 2e73c2f9ad..0e11a52cca 100644 --- a/indra/newview/fs_viewer_manifest.py +++ b/indra/newview/fs_viewer_manifest.py @@ -152,10 +152,10 @@ class FSViewerManifest: from shutil import copyfile self.fs_strip_windows_manifest( "%s/slplugin.exe" % self.args['configuration'].lower() ) self.fs_strip_windows_manifest( "%s/llplugin/dullahan_host.exe" % self.args['configuration'].lower() ) - if self.prefix(src=os.path.join(os.pardir, '..', 'indra', 'tools', 'manifests'), dst=""): + if self.prefix(src=os.path.join(self.args['build'], os.pardir, os.pardir, 'indra', 'tools', 'manifests')): self.path( "compatibility.manifest", "slplugin.exe.manifest" ) self.end_prefix() - if self.prefix(src=os.path.join(os.pardir, '..', 'indra', 'tools', 'manifests'), dst="llplugin"): + if self.prefix(src=os.path.join(self.args['build'], os.pardir, os.pardir, 'indra', 'tools', 'manifests'), dst="llplugin"): self.path( "compatibility.manifest", "dullahan_host.exe.manifest" ) self.end_prefix() diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index 4ed8484226..e4bfba3096 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 () +@end +#endif #include "llwindowmacosx-objc.h" +#include "llappviewermacosx-for-objc.h" #include // Used for Text Input Services ("Safe" API - it's supported) // [Cinder] We need to override sendEvent in NSApplication and force those @@ -79,6 +86,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 { @@ -196,4 +211,54 @@ return ret; } +#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 + +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 #ifdef LL_CARBON_CRASH_HANDLER #include @@ -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 251d48473b..2d49fa4701 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -67,8 +67,96 @@ #endif #include "stringize.h" +#include "lldir.h" +#include "llerrorcontrol.h" +#include #include + +// 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; @@ -569,15 +657,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 d7563cbfe1..3d000d4267 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -105,14 +105,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 357d67e5ee..9257986d80 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 # import shlex @@ -76,7 +77,7 @@ class ViewerManifest(LLManifest,FSViewerManifest): # 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") @@ -100,7 +101,7 @@ class ViewerManifest(LLManifest,FSViewerManifest): # Copy dictionaries to a place where the viewer can find them if ran from visual studio # ... 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") # @@ -149,28 +150,28 @@ class ViewerManifest(LLManifest,FSViewerManifest): 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") self.path("*.xml") # Include firestorm resources - with self.prefix(src="fs_resources"): + with self.prefix(src_dst="fs_resources"): self.path("*.txt") self.path("*.lsl") self.path("*.lsltxt") # skins - with self.prefix(src="skins"): + with self.prefix(src_dst="skins"): self.path("skins.xml") # 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") @@ -208,7 +209,7 @@ class ViewerManifest(LLManifest,FSViewerManifest): # 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") @@ -224,6 +225,11 @@ class ViewerManifest(LLManifest,FSViewerManifest): "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) @@ -253,8 +259,7 @@ class ViewerManifest(LLManifest,FSViewerManifest): 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'): @@ -551,19 +556,19 @@ class WindowsManifest(ViewerManifest): self.path(src='%s/firestorm-bin.exe' % self.args['configuration'], dst=self.final_exe()) # Remove VMP - #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"): @@ -577,8 +582,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: @@ -656,6 +661,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") + # Growl self.path("growl.dll") self.path("growl++.dll") @@ -678,114 +693,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") self.path("VivoxAUP.txt") # Media plugins - CEF - with self.prefix(src='../media_plugins/cef/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_cef.dll") + 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='../media_plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_libvlc.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='../media_plugins/example/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_example.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), 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") + # 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(os.pardir, 'sharedlibs', 'Release'), dst="llplugin"): - self.path("msvcp120.dll") - self.path("msvcr120.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'), 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") + # 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=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, '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'), dst="llplugin"): - self.path("libvlc.dll") - self.path("libvlccore.dll") - self.path("plugins/") + 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 @@ -797,10 +814,10 @@ class WindowsManifest(ViewerManifest): # is a 32bit process and Windows will silently copy the 32bit versions from # the SysWOW64 folder, even if explicitly trying to copy from System32! if (self.address_size == 64): - with self.prefix(src=os.path.join(os.pardir, '..', 'indra', 'newview', 'installers', 'windows_x64'), dst="llplugin"): + with self.prefix(src=os.path.join(self.args['build'], os.pardir, os.pardir, 'indra', 'newview', 'installers', 'windows_x64'), dst="llplugin"): self.path("msvcp120.dll") self.path("msvcr120.dll") - with self.prefix(src=os.path.join(os.pardir, '..', 'indra', 'newview', 'installers', 'windows_x64'), dst=""): + with self.prefix(src=os.path.join(self.args['build'], os.pardir, os.pardir, 'indra', 'newview', 'installers', 'windows_x64')): self.path("msvcp120.dll") self.path("msvcr120.dll") @@ -1017,6 +1034,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="") @@ -1026,12 +1046,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 @@ -1058,10 +1075,10 @@ class DarwinManifest(ViewerManifest): # # 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 -------------------- @@ -1081,15 +1098,15 @@ class DarwinManifest(ViewerManifest): # #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") @@ -1102,36 +1119,18 @@ class DarwinManifest(ViewerManifest): # # 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 @@ -1144,33 +1143,104 @@ class DarwinManifest(ViewerManifest): # # 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')]) + # "unpacked" in self.args['actions']): + # # 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") @@ -1409,7 +1479,7 @@ class DarwinManifest(ViewerManifest): chardetdir = os.path.join(pkgdir, "lib", "python", "chardet") idnadir = os.path.join(pkgdir, "lib", "python", "idna") - with self.prefix(src="", dst="Contents"): # everything goes in Contents + with self.prefix(dst="Contents"): # everything goes in Contents self.path("Info.plist", dst="Info.plist") # copy additional libs in /Contents/MacOS/ @@ -1420,7 +1490,7 @@ class DarwinManifest(ViewerManifest): self.path("../packages/Frameworks/Growl", dst="Frameworks/Growl") # most everything goes in the Resources directory - with self.prefix(src="", dst="Resources"): + with self.prefix(dst="Resources"): super(DarwinManifest, self).construct() with self.prefix("cursors_mac"): @@ -1430,11 +1500,11 @@ class DarwinManifest(ViewerManifest): self.path("featuretable_mac.txt") self.path("VivoxAUP.txt") - with self.prefix(src=pkgdir,dst=""): + with self.prefix(src=pkgdir): self.path("ca-bundle.crt") icon_path = self.icon_path() - with self.prefix(src=icon_path, dst="") : + with self.prefix(src=icon_path) : self.path("firestorm_icon.icns") self.path("Firestorm.nib") @@ -1587,15 +1657,15 @@ class DarwinManifest(ViewerManifest): # Moved from the x86_64 specific version because code # below that does symlinking and path fixup depends on it. - with self.prefix(src="../packages/bin_x86", dst=""): + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'packages', 'bin_x86')): self.path("SLPlugin.app", "SLPlugin.app") - with self.prefix(src = "llplugin", dst="llplugin"): + with self.prefix(src_dst="llplugin"): self.path("media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") self.path("media_plugin_cef.dylib", "media_plugin_cef.dylib") # Dullahan helper apps go inside SLPlugin.app - with self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"): + with self.prefix(dst="SLPlugin.app/Contents/Frameworks"): helperappfile = 'DullahanHelper.app' self.path2basename(relpkgdir, helperappfile) @@ -1611,14 +1681,13 @@ class DarwinManifest(ViewerManifest): self.dst_path_of('DullahanHelper.app/Contents/MacOS/' 'Frameworks/Chromium Embedded Framework.framework') - helperexecutablepath = self.dst_path_of('SLPlugin.app/Contents/Frameworks/DullahanHelper.app/Contents/MacOS/DullahanHelper') self.run_command_shell('install_name_tool -change ' '"@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" ' '"@executable_path/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" "%s"' % helperexecutablepath) # SLPlugin plugins - with self.prefix(src="", dst="llplugin"): + with self.prefix(dst="llplugin"): self.path2basename("../media_plugins/cef/" + self.args['configuration'], "media_plugin_cef.dylib") @@ -1627,14 +1696,13 @@ class DarwinManifest(ViewerManifest): "media_plugin_libvlc.dylib") # copy LibVLC dynamic libraries - with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'release' ), dst="lib"): - self.path( "libvlc*.dylib*" ) + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'packages', 'lib', 'release' ), dst="lib"): + self.path("libvlc*.dylib*") # copy LibVLC plugins folder - with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'release', 'plugins' ), dst="lib"): - self.path( "*.dylib" ) - self.path( "plugins.dat" ) - + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'packages', 'lib', 'release', 'plugins' ), dst="lib"): + self.path("*.dylib") + self.path("plugins.dat") # do this install_name_tool *after* media plugin is copied over dylibexecutablepath = self.dst_path_of('llplugin/media_plugin_cef.dylib') @@ -1642,13 +1710,12 @@ class DarwinManifest(ViewerManifest): '"@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" ' '"@executable_path/../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" "%s"' % dylibexecutablepath) - # Copy in prebuilt framework if it's there - with self.prefix(src="../packages/bin_x86/Frameworks", dst="Frameworks"): + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'packages', 'bin_x86', 'Frameworks'), dst="Frameworks"): self.path("Chromium Embedded Framework.framework") # CEF framework goes inside Second Life.app/Contents/Frameworks - with self.prefix(src="", dst="Frameworks"): + with self.prefix(dst="Frameworks"): frameworkfile="Chromium Embedded Framework.framework" self.path2basename(relpkgdir, frameworkfile) @@ -1921,26 +1988,26 @@ class LinuxManifest(ViewerManifest): self.path("licenses-linux.txt","licenses.txt") self.path("VivoxAUP.txt") self.path("res/firestorm_icon.png","firestorm_icon.png") - with self.prefix("linux_tools", dst=""): + with self.prefix("linux_tools"): self.path("client-readme.txt","README-linux.txt") self.path("FIRESTORM_DESKTOPINSTALL.txt","FIRESTORM_DESKTOPINSTALL.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","firestorm") - 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("firestorm-bin","do-not-directly-run-firestorm-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 # Remove VMP - #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"): @@ -1954,24 +2021,24 @@ 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("firestorm_256.png","firestorm_48.png") - with self.prefix(src="",dst="res-sdl") : + with self.prefix(dst="res-sdl") : self.path("firestorm_256.BMP","ll_icon.BMP") # plugins - with self.prefix(src="../media_plugins", dst="bin/llplugin"): + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins', dst="bin/llplugin")): self.path("gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") self.path2basename("libvlc", "libmedia_plugin_libvlc.so") self.path("cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so" ) - with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): + with self.prefix(src=os.path.join(pkgdir, 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): self.path( "plugins.dat" ) self.path( "*/*.so" ) - with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib' ), dst="lib"): + with self.prefix(src=os.path.join(pkgdir, 'lib' ), dst="lib"): self.path( "libvlc*.so*" ) with self.prefix(src=os.path.join(pkgdir, 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): @@ -1982,14 +2049,14 @@ class LinuxManifest(ViewerManifest): self.path( "libvlc*.so*" ) # CEF files - with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'release'), dst="lib"): + with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"): self.path( "libcef.so" ) self.path( "libllceflib.so" ) - with self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'release', 'swiftshader'), dst=os.path.join("bin", "swiftshader") ): + with self.prefix(src=os.path.join(pkgdir, 'release', 'swiftshader'), dst=os.path.join("bin", "swiftshader") ): self.path( "*.so" ) - with self.prefix(src=os.path.join(os.pardir, 'packages', 'bin', 'release'), dst="bin"): + with self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="bin"): self.path( "chrome-sandbox" ) self.path( "dullahan_host" ) self.path( "natives_blob.bin" ) @@ -1997,7 +2064,7 @@ class LinuxManifest(ViewerManifest): self.path( "v8_context_snapshot.bin" ) self.path( "libffmpegsumo.so" ) - with self.prefix(src=os.path.join(os.pardir, 'packages', 'resources'), dst="bin"): + with self.prefix(src=os.path.join(pkgdir, 'resources'), dst="bin"): self.path( "cef.pak" ) self.path( "cef_extensions.pak" ) self.path( "cef_100_percent.pak" ) @@ -2005,7 +2072,7 @@ class LinuxManifest(ViewerManifest): self.path( "devtools_resources.pak" ) self.path( "icudtl.dat" ) - with self.prefix(src=os.path.join(os.pardir, 'packages', 'resources', 'locales'), dst=os.path.join('bin', 'locales')): + with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('bin', 'locales')): self.path("am.pak") self.path("ar.pak") self.path("bg.pak") @@ -2066,11 +2133,11 @@ class LinuxManifest(ViewerManifest): self.path("featuretable_linux.txt") - with self.prefix(src=pkgdir,dst="bin"): + with self.prefix(src=pkgdir, dst="bin"): self.path("ca-bundle.crt") if self.is_packaging_viewer(): - with self.prefix("../packages/lib/release", dst="lib"): + with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"): self.path("libapr-1.so*") self.path("libaprutil-1.so*") self.path("libboost_context-mt.so*") @@ -2132,11 +2199,11 @@ class LinuxManifest(ViewerManifest): # Vivox runtimes # Currentelly, the 32-bit ones will work with a 64-bit client. - with self.prefix(src="../packages/lib/release", dst="bin"): + with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="bin"): self.path("SLVoice") self.path("win32") - with self.prefix(src="../packages/lib/release", dst="lib"): + with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"): self.path("libortp.so") self.path("libsndfile.so.1") # Vivox wants this library even if it's present already in the viewer @@ -2207,7 +2274,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") @@ -2303,7 +2370,7 @@ class Linux_x86_64_Manifest(LinuxManifest): super(Linux_x86_64_Manifest, self).construct() if self.is_packaging_viewer(): - with self.prefix("../packages/lib/release", dst="lib"): + with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"): self.path("libffi*.so*") # vivox 32-bit hack. # one has to extract libopenal.so from the 32-bit openal package, or official LL viewer, and rename it to libopenal32.so @@ -2329,18 +2396,18 @@ class Linux_x86_64_Manifest(LinuxManifest): print "Skipping libfmod.so - not found" pass - self.prefix(src="../packages/lib/release/x64", dst="lib") + self.prefix(src=os.path.join(pkgdir, 'lib', 'release', 'x64'), dst="lib") try: self.path("libLeap.so") except: print "Leap Motion library not found" self.end_prefix("lib") - with self.prefix(src="", dst="bin"): + with self.prefix(dst="bin"): self.path2basename("../llplugin/slplugin", "SLPlugin") # plugins - with self.prefix(src="", dst="bin/llplugin"): + with self.prefix(dst="bin/llplugin"): self.path2basename("../media_plugins/webkit", "libmedia_plugin_webkit.so") self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so")