summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh32
-rw-r--r--indra/cmake/CMakeLists.txt1
-rw-r--r--indra/cmake/Copy3rdPartyLibs.cmake14
-rw-r--r--indra/cmake/Variables.cmake1
-rw-r--r--indra/cmake/bugsplat.cmake26
-rwxr-xr-xindra/lib/python/indra/util/llmanifest.py51
-rw-r--r--indra/llappearance/lllocaltextureobject.cpp1
-rw-r--r--indra/llcommon/CMakeLists.txt5
-rw-r--r--indra/llcommon/llapp.cpp23
-rw-r--r--indra/llcommon/llerror.cpp33
-rw-r--r--indra/llcommon/llerrorcontrol.h3
-rw-r--r--indra/llcommon/llpreprocessor.h2
-rw-r--r--indra/llcommon/stringize.h52
-rw-r--r--indra/llcommon/tests/wrapllerrs.h14
-rw-r--r--indra/newview/Info-SecondLife.plist2
-rw-r--r--indra/newview/llappdelegate-objc.mm65
-rw-r--r--indra/newview/llappviewermacosx-for-objc.h37
-rw-r--r--indra/newview/llappviewermacosx.cpp23
-rw-r--r--indra/newview/llappviewerwin32.cpp150
-rw-r--r--indra/newview/llversioninfo.cpp7
-rw-r--r--indra/newview/tests/llversioninfo_test.cpp6
-rwxr-xr-xindra/newview/viewer_manifest.py426
22 files changed, 711 insertions, 263 deletions
diff --git a/build.sh b/build.sh
index bb8d66599b..216ce5720a 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"
@@ -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")