summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt349
-rw-r--r--indra/newview/Info-SecondLife.plist6
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml26
-rw-r--r--indra/newview/installers/windows/installer_template.nsi230
-rw-r--r--indra/newview/installers/windows/lang_da.nsibin7816 -> 11760 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_de.nsibin9850 -> 13014 bytes
-rw-r--r--indra/newview/installers/windows/lang_en-us.nsibin8814 -> 11488 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_es.nsibin9872 -> 12744 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_fr.nsibin10258 -> 13262 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_it.nsibin9520 -> 12480 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_ja.nsibin7204 -> 9644 bytes
-rw-r--r--indra/newview/installers/windows/lang_pl.nsibin8136 -> 12080 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_pt-br.nsibin9864 -> 12892 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_ru.nsibin9188 -> 12216 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_tr.nsibin9286 -> 12126 bytes
-rwxr-xr-x[-rw-r--r--]indra/newview/installers/windows/lang_zh.nsibin6846 -> 9082 bytes
-rw-r--r--indra/newview/installers/windows/language_menu.nsibin1448 -> 1464 bytes
-rw-r--r--indra/newview/llappdelegate-objc.mm162
-rw-r--r--indra/newview/llappviewer.cpp91
-rw-r--r--indra/newview/llappviewermacosx-for-objc.h53
-rw-r--r--indra/newview/llappviewermacosx.cpp88
-rw-r--r--indra/newview/llappviewerwin32.cpp157
-rw-r--r--indra/newview/llexternaleditor.cpp7
-rw-r--r--indra/newview/lllogininstance.cpp63
-rw-r--r--indra/newview/lllogininstance.h6
-rw-r--r--indra/newview/llstartup.cpp3
-rw-r--r--indra/newview/llversioninfo.cpp7
-rw-r--r--indra/newview/llviewerregion.cpp19
-rw-r--r--indra/newview/llviewerwindow.cpp3
-rw-r--r--indra/newview/llvoicevivox.cpp9
-rw-r--r--indra/newview/llwebprofile.cpp4
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml39
-rw-r--r--indra/newview/tests/llversioninfo_test.cpp6
-rwxr-xr-xindra/newview/viewer_manifest.py1040
35 files changed, 1543 insertions, 827 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index c389de1232..b5fcb18cfc 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -3,7 +3,14 @@
project(viewer)
include(00-Common)
+# DON'T move Linking.cmake to its place in the alphabetized list below: it
+# sets variables on which the 3p .cmake files depend.
+include(Linking)
+
include(Boost)
+if (BUGSPLAT_DB)
+ include(bugsplat)
+endif (BUGSPLAT_DB)
include(BuildPackagesInfo)
include(BuildVersion)
include(CMakeCopyIfDifferent)
@@ -16,7 +23,6 @@ include(GLOD)
include(Hunspell)
include(JsonCpp)
include(LLAppearance)
-include(LLBase)
include(LLAudio)
include(LLCA)
include(LLCharacter)
@@ -37,14 +43,12 @@ include(LLUI)
include(LLVFS)
include(LLWindow)
include(LLXML)
-include(Linking)
include(NDOF)
include(NVAPI)
include(OPENAL)
include(OpenGL)
include(OpenSSL)
include(PNG)
-include(Requests)
include(TemplateCheck)
include(UI)
include(UnixInstall)
@@ -93,6 +97,12 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
+if (BUGSPLAT_DB)
+ include_directories(
+ ${BUGSPLAT_INCLUDE_DIR}
+ )
+endif (BUGSPLAT_DB)
+
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
${LLXML_SYSTEM_INCLUDE_DIRS}
@@ -1364,6 +1374,14 @@ if (DARWIN)
# This should be compiled with the viewer.
LIST(APPEND viewer_SOURCE_FILES llappdelegate-objc.mm)
+ set_source_files_properties(
+ llappdelegate-objc.mm
+ PROPERTIES
+ COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}"
+ # BugsplatMac is a module, imported with @import. That language feature
+ # demands these switches.
+ COMPILE_FLAGS "-fmodules -fcxx-modules"
+ )
find_library(AGL_LIBRARY AGL)
find_library(APPKIT_LIBRARY AppKit)
@@ -1378,6 +1396,12 @@ if (DARWIN)
${COREAUDIO_LIBRARY}
)
+ if (BUGSPLAT_DB)
+ list(APPEND viewer_LIBRARIES
+ ${BUGSPLAT_LIBRARIES}
+ )
+ endif (BUGSPLAT_DB)
+
# Add resource files to the project.
set(viewer_RESOURCE_FILES
secondlife.icns
@@ -1403,6 +1427,11 @@ endif (DARWIN)
if (LINUX)
LIST(APPEND viewer_SOURCE_FILES llappviewerlinux.cpp)
+ set_source_files_properties(
+ llappviewerlinux.cpp
+ PROPERTIES
+ COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}"
+ )
LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp)
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
@@ -1419,6 +1448,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
@@ -1555,7 +1589,6 @@ if (WINDOWS)
kernel32
odbc32
odbccp32
- ole32
oleaut32
shell32
Vfw32
@@ -1702,6 +1735,11 @@ if (SDL_FOUND)
)
endif (SDL_FOUND)
+if (BUGSPLAT_DB)
+ set_property(TARGET ${VIEWER_BINARY_NAME}
+ PROPERTY COMPILE_DEFINITIONS "LL_BUGSPLAT")
+endif (BUGSPLAT_DB)
+
# add package files
file(GLOB EVENT_HOST_SCRIPT_GLOB_LIST
${CMAKE_CURRENT_SOURCE_DIR}/../viewer_components/*.py)
@@ -1800,7 +1838,7 @@ if (WINDOWS)
${SHARED_LIB_STAGING_DIR}/Debug/fmodexL.dll
)
endif (FMODEX)
-
+
add_custom_command(
OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat
COMMAND ${PYTHON_EXECUTABLE}
@@ -1809,15 +1847,16 @@ if (WINDOWS)
--actions=copy
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
+ "--bugsplat=${BUGSPLAT_DB}"
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
+ "--channel=${VIEWER_CHANNEL}"
--configuration=${CMAKE_CFG_INTDIR}
--dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
--grid=${GRID}
- "--channel=${VIEWER_CHANNEL}"
- --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--source=${CMAKE_CURRENT_SOURCE_DIR}
--touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat
+ --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
stage_third_party_libs
@@ -1835,24 +1874,9 @@ if (WINDOWS)
add_dependencies(${VIEWER_BINARY_NAME}
SLPlugin
- windows-crash-logger
+ windows-crash-logger
)
- # sets the 'working directory' for debugging from visual studio.
- if (NOT UNATTENDED)
- add_custom_command(
- TARGET ${VIEWER_BINARY_NAME} POST_BUILD
- COMMAND ${CMAKE_SOURCE_DIR}/tools/vstool/vstool.exe
- ARGS
- --solution
- ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.sln
- --workingdir
- ${VIEWER_BINARY_NAME}
- "${CMAKE_CURRENT_SOURCE_DIR}"
- COMMENT "Setting the ${VIEWER_BINARY_NAME} working directory for debugging."
- )
- endif (NOT UNATTENDED)
-
if (PACKAGE)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2
@@ -1875,15 +1899,16 @@ if (WINDOWS)
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
+ "--bugsplat=${BUGSPLAT_DB}"
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
"--channel=${VIEWER_CHANNEL}"
- --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--configuration=${CMAKE_CFG_INTDIR}
--dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
--grid=${GRID}
--source=${CMAKE_CURRENT_SOURCE_DIR}
--touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/touched.bat
+ --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
DEPENDS
${VIEWER_BINARY_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
@@ -1914,8 +1939,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
@@ -1990,6 +2015,12 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${LLAPPEARANCE_LIBRARIES}
)
+if (BUGSPLAT_DB)
+ target_link_libraries(${VIEWER_BINARY_NAME}
+ ${BUGSPLAT_LIBRARIES}
+ )
+endif (BUGSPLAT_DB)
+
set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
"Path to artwork files.")
@@ -2013,15 +2044,16 @@ if (LINUX)
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
+ "--bugsplat=${BUGSPLAT_DB}"
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
"--channel=${VIEWER_CHANNEL}"
- --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--configuration=${CMAKE_CFG_INTDIR}
--dest=${CMAKE_CURRENT_BINARY_DIR}/packaged
--grid=${GRID}
--source=${CMAKE_CURRENT_SOURCE_DIR}
--touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched
+ --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
${COPY_INPUT_DEPENDENCIES}
@@ -2035,17 +2067,18 @@ if (LINUX)
COMMAND ${PYTHON_EXECUTABLE}
ARGS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
- --arch=${ARCH}
--actions=copy
+ --arch=${ARCH}
--artwork=${ARTWORK_DIR}
+ "--bugsplat=${BUGSPLAT_DB}"
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
+ "--channel=${VIEWER_CHANNEL}"
--configuration=${CMAKE_CFG_INTDIR}
--dest=${CMAKE_CURRENT_BINARY_DIR}/packaged
--grid=${GRID}
- "--channel=${VIEWER_CHANNEL}"
- --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--source=${CMAKE_CURRENT_SOURCE_DIR}
+ --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
${COPY_INPUT_DEPENDENCIES}
@@ -2063,37 +2096,46 @@ if (LINUX)
endif (LINUX)
if (DARWIN)
- # These all get set with PROPERTIES
- set(product "Second Life")
- # this is the setting for the Python wrapper, see SL-322 and WRAPPER line in Info-SecondLife.plist
- if (PACKAGE)
- set(MACOSX_WRAPPER_EXECUTABLE_NAME "SL_Launcher")
- else (PACKAGE)
- # force the name of the actual executable to allow running it within Xcode for debugging
- set(MACOSX_WRAPPER_EXECUTABLE_NAME "../Resources/Second Life Viewer.app/Contents/MacOS/Second Life")
- endif (PACKAGE)
- set(MACOSX_BUNDLE_INFO_STRING "Second Life Viewer")
+ # 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}")
+ set(MACOSX_EXECUTABLE_NAME "${VIEWER_CHANNEL}")
+ set(MACOSX_BUNDLE_INFO_STRING "${VIEWER_CHANNEL}")
set(MACOSX_BUNDLE_ICON_FILE "secondlife.icns")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.secondlife.indra.viewer")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${VIEWER_CHANNEL} ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}")
set(MACOSX_BUNDLE_BUNDLE_NAME "SecondLife")
- set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}")
+ set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${VIEWER_SHORT_VERSION}${VIEWER_MACOSX_PHASE}${VIEWER_REVISION}")
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © Linden Research, Inc. 2018")
set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "SecondLife.nib")
set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "NSApplication")
+
+ # https://blog.kitware.com/upcoming-in-cmake-2-8-12-osx-rpath-support/
+ set(CMAKE_MACOSX_RPATH 1)
set_target_properties(
${VIEWER_BINARY_NAME}
PROPERTIES
OUTPUT_NAME "${product}"
+ # From Contents/MacOS/SecondLife, look in Contents/Frameworks
+ INSTALL_RPATH "@loader_path/../Frameworks"
+ # SIGH, as of 2018-05-24 (cmake 3.11.1) the INSTALL_RPATH property simply
+ # does not work. Try this:
+ LINK_FLAGS "-rpath @loader_path/../Frameworks"
MACOSX_BUNDLE_INFO_PLIST
"${CMAKE_CURRENT_SOURCE_DIR}/Info-SecondLife.plist"
)
+ set(VIEWER_APP_BUNDLE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app")
+ set(VIEWER_APP_EXE "${VIEWER_APP_BUNDLE}/Contents/MacOS/${product}")
+ set(VIEWER_APP_DSYM "${VIEWER_APP_EXE}.dSYM")
+ set(VIEWER_APP_XCARCHIVE "${VIEWER_APP_BUNDLE}/../${product}.xcarchive.zip")
+
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/Info-SecondLife.plist"
- "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app/Contents/Info.plist"
+ "${VIEWER_APP_BUNDLE}/Contents/Info.plist"
)
add_custom_command(
@@ -2104,15 +2146,16 @@ if (DARWIN)
--actions=copy
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
+ "--bugsplat=${BUGSPLAT_DB}"
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
+ --bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER}
+ "--channel=${VIEWER_CHANNEL}"
--configuration=${CMAKE_CFG_INTDIR}
- --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app
+ --dest=${VIEWER_APP_BUNDLE}
--grid=${GRID}
- "--channel=${VIEWER_CHANNEL}"
- --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
- --bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER}
--source=${CMAKE_CURRENT_SOURCE_DIR}
+ --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
DEPENDS
${VIEWER_BINARY_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
@@ -2137,15 +2180,16 @@ if (DARWIN)
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
+ "--bugsplat=${BUGSPLAT_DB}"
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
+ "--channel=${VIEWER_CHANNEL}"
--configuration=${CMAKE_CFG_INTDIR}
- --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app
+ --dest=${VIEWER_APP_BUNDLE}
--grid=${GRID}
- "--channel=${VIEWER_CHANNEL}"
- --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
--source=${CMAKE_CURRENT_SOURCE_DIR}
--touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched
+ --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
${SIGNING_SETTING}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
@@ -2157,67 +2201,152 @@ if (INSTALL)
include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake)
endif (INSTALL)
-if (PACKAGE)
- set(SYMBOL_SEARCH_DIRS "")
- # Note that the path to VIEWER_SYMBOL_FILE must match that in ../../build.sh
- if (WINDOWS)
- list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
- set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-windows-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2")
- # slplugin.exe failing symbols dump - need to debug, might have to do with updated version of google breakpad
- # set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe")
- set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
- set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}")
- set(VIEWER_COPY_MANIFEST copy_w_viewer_manifest)
- endif (WINDOWS)
- if (DARWIN)
- list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
- # *TODO: Generate these search dirs in the cmake files related to each binary.
- list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/llplugin/slplugin/${CMAKE_CFG_INTDIR}")
- list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_crash_logger/${CMAKE_CFG_INTDIR}")
- list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}")
- set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2")
- set(VIEWER_EXE_GLOBS "'Second Life' SLPlugin mac-crash-logger")
- set(VIEWER_EXE_GLOBS "'Second Life' mac-crash-logger")
- set(VIEWER_LIB_GLOB "*.dylib")
- endif (DARWIN)
- if (LINUX)
- list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged")
- set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-linux-$ENV{AUTOBUILD_ADDRSIZE}.tar.bz2")
- set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin SLPlugin")
- set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin")
- set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*")
- set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)
- endif (LINUX)
-
- if(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING)
- if(CMAKE_CFG_INTDIR STREQUAL ".")
- set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE})
- else(CMAKE_CFG_INTDIR STREQUAL ".")
- # set LLBUILD_CONFIG to be a shell variable evaluated at build time
- # reflecting the configuration we are currently building.
- set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR})
- endif(CMAKE_CFG_INTDIR STREQUAL ".")
- add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
- COMMAND "${PYTHON_EXECUTABLE}"
- ARGS
- "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py"
- "${LLBUILD_CONFIG}"
- "${SYMBOL_SEARCH_DIRS}"
- "${VIEWER_EXE_GLOBS}"
- "${VIEWER_LIB_GLOB}"
- "${AUTOBUILD_INSTALL_DIR}/bin/dump_syms"
- "${VIEWER_SYMBOL_FILE}"
- DEPENDS generate_breakpad_symbols.py
- VERBATIM)
-
- add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}")
- add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}")
- if (WINDOWS OR LINUX)
- add_dependencies(generate_breakpad_symbols "${VIEWER_COPY_MANIFEST}")
- endif (WINDOWS OR LINUX)
- add_dependencies(llpackage generate_breakpad_symbols)
- endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING)
-endif (PACKAGE)
+# Note that the conventional VIEWER_SYMBOL_FILE is set by ../../build.sh
+if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND VIEWER_SYMBOL_FILE)
+ if (NOT BUGSPLAT_DB)
+ # Breakpad symbol-file generation
+ set(SYMBOL_SEARCH_DIRS "")
+ if (WINDOWS)
+ list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
+ # slplugin.exe failing symbols dump - need to debug, might have to do with updated version of google breakpad
+ # set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe")
+ set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
+ set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}")
+ set(VIEWER_COPY_MANIFEST copy_w_viewer_manifest)
+ endif (WINDOWS)
+ if (DARWIN)
+ list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
+ # *TODO: Generate these search dirs in the cmake files related to each binary.
+ list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/llplugin/slplugin/${CMAKE_CFG_INTDIR}")
+ list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_crash_logger/${CMAKE_CFG_INTDIR}")
+ list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}")
+ set(VIEWER_EXE_GLOBS "'${product}' SLPlugin mac-crash-logger")
+ set(VIEWER_EXE_GLOBS "'${product}' mac-crash-logger")
+ set(VIEWER_LIB_GLOB "*.dylib")
+ endif (DARWIN)
+ if (LINUX)
+ list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged")
+ set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin SLPlugin")
+ set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin")
+ set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*")
+ set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)
+ endif (LINUX)
+
+ if(CMAKE_CFG_INTDIR STREQUAL ".")
+ set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE})
+ else(CMAKE_CFG_INTDIR STREQUAL ".")
+ # set LLBUILD_CONFIG to be a shell variable evaluated at build time
+ # reflecting the configuration we are currently building.
+ set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR})
+ endif(CMAKE_CFG_INTDIR STREQUAL ".")
+ add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
+ COMMAND "${PYTHON_EXECUTABLE}"
+ ARGS
+ "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py"
+ "${LLBUILD_CONFIG}"
+ "${SYMBOL_SEARCH_DIRS}"
+ "${VIEWER_EXE_GLOBS}"
+ "${VIEWER_LIB_GLOB}"
+ "${AUTOBUILD_INSTALL_DIR}/bin/dump_syms"
+ "${VIEWER_SYMBOL_FILE}"
+ DEPENDS generate_breakpad_symbols.py
+ VERBATIM)
+
+ add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" ${VIEWER_BINARY_NAME} "${VIEWER_COPY_MANIFEST}")
+ add_dependencies(generate_symbols ${VIEWER_BINARY_NAME})
+ if (WINDOWS OR LINUX)
+ add_dependencies(generate_symbols "${VIEWER_COPY_MANIFEST}")
+ endif (WINDOWS OR LINUX)
+
+ else (NOT BUGSPLAT_DB)
+ # BugSplat symbol-file generation
+ if (WINDOWS)
+ # Just pack up a tarball containing only the .pdb file for the
+ # executable. Because we intend to use cygwin tar, we must render
+ # VIEWER_SYMBOL_FILE in cygwin path syntax.
+ execute_process(COMMAND "cygpath" "-u" "${VIEWER_SYMBOL_FILE}"
+ OUTPUT_VARIABLE VIEWER_SYMBOL_FILE_CYGWIN
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ execute_process(COMMAND "cygpath" "-u" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
+ OUTPUT_VARIABLE PARENT_DIRECTORY_CYGWIN
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
+ # Use of 'tar ...j' here assumes VIEWER_SYMBOL_FILE endswith .tar.bz2;
+ # testing a string suffix is painful enough in CMake language that
+ # we'll continue assuming it until forced to generalize.
+ COMMAND "tar"
+ ARGS
+ "cjf"
+ "${VIEWER_SYMBOL_FILE_CYGWIN}"
+ "-C"
+ "${PARENT_DIRECTORY_CYGWIN}"
+ "secondlife-bin.pdb"
+ DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-bin.pdb"
+ COMMENT "Packing viewer PDB into ${VIEWER_SYMBOL_FILE_CYGWIN}"
+ )
+ add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" ${VIEWER_BINARY_NAME})
+ add_dependencies(generate_symbols ${VIEWER_BINARY_NAME})
+ endif (WINDOWS)
+ if (DARWIN)
+ # Have to run dsymutil first, then pack up the resulting .dSYM directory
+ add_custom_command(OUTPUT "${VIEWER_APP_DSYM}"
+ COMMAND "dsymutil"
+ ARGS
+ ${VIEWER_APP_EXE}
+ COMMENT "Generating ${VIEWER_APP_DSYM}"
+ )
+ add_custom_target(dsym_generate DEPENDS "${VIEWER_APP_DSYM}")
+ add_dependencies(dsym_generate ${VIEWER_BINARY_NAME})
+ add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
+ # See above comments about "tar ...j"
+ COMMAND "tar"
+ ARGS
+ "cjf"
+ "${VIEWER_SYMBOL_FILE}"
+ "-C"
+ "${VIEWER_APP_DSYM}/.."
+ "${product}.dSYM"
+ DEPENDS "${VIEWER_APP_DSYM}"
+ COMMENT "Packing dSYM into ${VIEWER_SYMBOL_FILE}"
+ )
+ add_custom_target(dsym_tarball DEPENDS "${VIEWER_SYMBOL_FILE}")
+ add_dependencies(dsym_tarball dsym_generate)
+ add_custom_command(OUTPUT "${VIEWER_APP_XCARCHIVE}"
+ COMMAND "zip"
+ ARGS
+ "-r"
+ "${VIEWER_APP_XCARCHIVE}"
+ "."
+ WORKING_DIRECTORY "${VIEWER_APP_DSYM}/.."
+ DEPENDS "${VIEWER_APP_DSYM}"
+ COMMENT "Generating xcarchive.zip for upload to BugSplat"
+ )
+ add_custom_target(dsym_xcarchive DEPENDS "${VIEWER_APP_XCARCHIVE}")
+ add_dependencies(dsym_xcarchive dsym_generate)
+ # Have to create a stamp file, and depend on it, to force CMake to run
+ # the cleanup step.
+ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/dsym.stamp"
+ COMMAND rm -rf "${VIEWER_APP_DSYM}"
+ COMMAND touch "${CMAKE_CURRENT_BINARY_DIR}/dsym.stamp"
+ DEPENDS "${VIEWER_SYMBOL_FILE}" "${VIEWER_APP_XCARCHIVE}"
+ COMMENT "Cleaning up dSYM"
+ )
+ add_custom_target(generate_symbols DEPENDS
+ "${VIEWER_APP_DSYM}"
+ "${VIEWER_SYMBOL_FILE}"
+ "${VIEWER_APP_XCARCHIVE}"
+ "${CMAKE_CURRENT_BINARY_DIR}/dsym.stamp"
+ )
+ add_dependencies(generate_symbols dsym_tarball dsym_xcarchive)
+ endif (DARWIN)
+ if (LINUX)
+ # TBD
+ endif (LINUX)
+ endif (NOT BUGSPLAT_DB)
+
+ # for both BUGSPLAT_DB and Breakpad
+ add_dependencies(llpackage generate_symbols)
+endif ()
if (LL_TESTS)
# To add a viewer unit test, just add the test .cpp file below
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index af4cf26ac6..cfe9d991c5 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
- <string>${MACOSX_WRAPPER_EXECUTABLE_NAME}</string>
+ <string>${MACOSX_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
@@ -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>
@@ -32,6 +32,8 @@
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+ <key>NSMicrophoneUsageDescription</key>
+ <string>For voice chat, you must grant permission for Second Life to use the microphone.</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 9b9a244206..f3b5af39e4 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-6.0.2
+6.1.1
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8e525aceda..e8cac7842b 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -4404,6 +4404,17 @@
<key>Value</key>
<real>96.0</real>
</map>
+ <key>ForceAddressSize</key>
+ <map>
+ <key>Comment</key>
+ <string>Force Windows update to 32-bit or 64-bit viewer.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>ForceAssetFail</key>
<map>
<key>Comment</key>
@@ -13598,7 +13609,7 @@
<key>UpdaterServiceURL</key>
<map>
<key>Comment</key>
- <string>Default location for the updater service.</string>
+ <string>Obsolete; no longer used.</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
@@ -14090,17 +14101,6 @@
<key>Value</key>
<integer>1</integer>
</map>
- <key>VerboseLogs</key>
- <map>
- <key>Comment</key>
- <string>Display source file and line number for each log item for debugging purposes</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>Boolean</string>
- <key>Value</key>
- <integer>0</integer>
- </map>
<key>VertexShaderEnable</key>
<map>
<key>Comment</key>
@@ -16282,7 +16282,7 @@
<string>if true, disables running the GPU benchmark at startup
(default to class 1)</string>
<key>Persist</key>
- <integer>0</integer>
+ <integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index d6e4b005df..d1d5301efe 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -18,8 +18,7 @@
;;
;; Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
;;
-;; NSIS Unicode 2.46.5 or higher required
-;; http://www.scratchpaper.com/
+;; NSIS 3 or higher required for Unicode support
;;
;; Author: James Cook, TankMaster Finesmith, Don Kjer, Callum Prentice
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -27,6 +26,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compiler flags
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Unicode true
SetOverwrite on # Overwrite files
SetCompress auto # Compress if saves space
SetCompressor /solid lzma # Compress whole installer as one block
@@ -46,28 +46,33 @@ RequestExecutionLevel admin # For when we write to Program Files
;; (these files are in the same place as the nsi template but the python script generates a new nsi file in the
;; application directory so we have to add a path to these include files)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-!include "%%SOURCE%%\installers\windows\lang_da.nsi"
-!include "%%SOURCE%%\installers\windows\lang_de.nsi"
+;; Ansariel notes: "Under certain circumstances the installer will fall back
+;; to the first defined (aka default) language version. So you want to include
+;; en-us as first language file."
!include "%%SOURCE%%\installers\windows\lang_en-us.nsi"
+
+# Danish and Polish no longer supported by the viewer itself
+##!include "%%SOURCE%%\installers\windows\lang_da.nsi"
+!include "%%SOURCE%%\installers\windows\lang_de.nsi"
!include "%%SOURCE%%\installers\windows\lang_es.nsi"
!include "%%SOURCE%%\installers\windows\lang_fr.nsi"
!include "%%SOURCE%%\installers\windows\lang_ja.nsi"
!include "%%SOURCE%%\installers\windows\lang_it.nsi"
-!include "%%SOURCE%%\installers\windows\lang_pl.nsi"
+##!include "%%SOURCE%%\installers\windows\lang_pl.nsi"
!include "%%SOURCE%%\installers\windows\lang_pt-br.nsi"
!include "%%SOURCE%%\installers\windows\lang_ru.nsi"
!include "%%SOURCE%%\installers\windows\lang_tr.nsi"
!include "%%SOURCE%%\installers\windows\lang_zh.nsi"
# *TODO: Move these into the language files themselves
-LangString LanguageCode ${LANG_DANISH} "da"
+##LangString LanguageCode ${LANG_DANISH} "da"
LangString LanguageCode ${LANG_GERMAN} "de"
LangString LanguageCode ${LANG_ENGLISH} "en"
LangString LanguageCode ${LANG_SPANISH} "es"
LangString LanguageCode ${LANG_FRENCH} "fr"
LangString LanguageCode ${LANG_JAPANESE} "ja"
LangString LanguageCode ${LANG_ITALIAN} "it"
-LangString LanguageCode ${LANG_POLISH} "pl"
+##LangString LanguageCode ${LANG_POLISH} "pl"
LangString LanguageCode ${LANG_PORTUGUESEBR} "pt"
LangString LanguageCode ${LANG_RUSSIAN} "ru"
LangString LanguageCode ${LANG_TURKISH} "tr"
@@ -80,9 +85,12 @@ Name ${INSTNAME}
SubCaption 0 $(LicenseSubTitleSetup) # Override "license agreement" text
+!define MUI_ICON "%%SOURCE%%\installers\windows\install_icon.ico"
+!define MUI_UNICON "%%SOURCE%%\installers\windows\uninstall_icon.ico"
+
BrandingText " " # Bottom of window text
-Icon %%SOURCE%%\installers\windows\install_icon.ico
-UninstallIcon %%SOURCE%%\installers\windows\uninstall_icon.ico
+Icon "${MUI_ICON}"
+UninstallIcon "${MUI_UNICON}"
WindowIcon on # Show our icon in left corner
BGGradient off # No big background window
CRCCheck on # Make sure CRC is OK
@@ -90,20 +98,50 @@ InstProgressFlags smooth colored # New colored smooth look
SetOverwrite on # Overwrite files by default
AutoCloseWindow true # After all files install, close window
-# initial location of install (default when not already installed)
-# note: Now we defer looking for existing install until onInit when we
-# are able to engage the 32/64 registry function
-InstallDir "%%PROGRAMFILES%%\${INSTNAME}"
+# Registry key paths, ours and Microsoft's
+!define LINDEN_KEY "SOFTWARE\Linden Research, Inc."
+!define INSTNAME_KEY "${LINDEN_KEY}\${INSTNAME}"
+!define MSCURRVER_KEY "SOFTWARE\Microsoft\Windows\CurrentVersion"
+!define MSNTCURRVER_KEY "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
+!define MSUNINSTALL_KEY "${MSCURRVER_KEY}\Uninstall\${INSTNAME}"
+
+# from http://nsis.sourceforge.net/Docs/MultiUser/Readme.html
+### Highest level permitted for user: Admin for Admin, Standard for Standard
+##!define MULTIUSER_EXECUTIONLEVEL Highest
+!define MULTIUSER_EXECUTIONLEVEL Admin
+!define MULTIUSER_MUI
+### Look for /AllUsers or /CurrentUser switches
+##!define MULTIUSER_INSTALLMODE_COMMANDLINE
+# appended to $PROGRAMFILES, as affected by MULTIUSER_USE_PROGRAMFILES64
+!define MULTIUSER_INSTALLMODE_INSTDIR "${INSTNAME}"
+# expands to !define MULTIUSER_USE_PROGRAMFILES64 or nothing
+%%PROGRAMFILES%%
+# should make MultiUser.nsh initialization read existing INSTDIR from registry
+## SL-10506: don't
+##!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "${INSTNAME_KEY}"
+##!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME ""
+# Don't set MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY and
+# MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME to cause the installer to
+# write $MultiUser.InstallMode to the registry, because when the user installs
+# multiple viewers with the same channel (same ${INSTNAME}, hence same
+# ${INSTNAME_KEY}), the registry entry is overwritten. Instead we'll write a
+# little file into the install directory -- see .onInstSuccess and un.onInit.
+!include MultiUser.nsh
+!include MUI2.nsh
+!define MUI_BGCOLOR FFFFFF
+!insertmacro MUI_FUNCTION_GUIINIT
UninstallText $(UninstallTextMsg)
DirText $(DirectoryChooseTitle) $(DirectoryChooseSetup)
-Page directory dirPre
-Page instfiles
+##!insertmacro MULTIUSER_PAGE_INSTALLMODE
+!define MUI_PAGE_CUSTOMFUNCTION_PRE dirPre
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-Var INSTPROG
+Var INSTNAME
Var INSTEXE
Var VIEWER_EXE
Var INSTSHORTCUT
@@ -142,17 +180,21 @@ FunctionEnd
;; entry to the language ID selector below
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Function .onInit
+!insertmacro MULTIUSER_INIT
%%ENGAGEREGISTRY%%
-# read the current location of the install for this version
+# SL-10506: Setting MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY and
+# MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME should
+# read the current location of the install for this version into INSTDIR.
+# However, SL-10506 complains about the resulting behavior, so the logic below
+# is adapted from before we introduced MultiUser.nsh.
+
# if $0 is empty, this is the first time for this viewer name
-ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\\Linden Research, Inc.\\${INSTNAME}" ""
+ReadRegStr $0 SHELL_CONTEXT "${INSTNAME_KEY}" ""
-# viewer with this name not installed before
-${If} $0 == ""
- # nothing to do here
-${Else}
+# viewer with this name was installed before
+${If} $0 != ""
# use the value we got from registry as install location
StrCpy $INSTDIR $0
${EndIf}
@@ -181,7 +223,7 @@ Call CheckWindowsVersion # Don't install On unsupported systems
lbl_configure_default_lang:
# If we currently have a version of SL installed, default to the language of that install
# Otherwise don't change $LANGUAGE and it will default to the OS UI language.
- ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage"
+ ReadRegStr $0 SHELL_CONTEXT "${INSTNAME_KEY}" "InstallerLanguage"
IfErrors +2 0 # If error skip the copy instruction
StrCpy $LANGUAGE $0
@@ -191,7 +233,6 @@ lbl_configure_default_lang:
Goto lbl_return
StrCmp $SKIP_DIALOGS "true" lbl_return
-lbl_build_menu:
Push ""
# Use separate file so labels can be UTF-16 but we can still merge changes into this ASCII file. JC
!include "%%SOURCE%%\installers\windows\language_menu.nsi"
@@ -204,7 +245,7 @@ lbl_build_menu:
StrCpy $LANGUAGE $0
# Save language in registry
- WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage" $LANGUAGE
+ WriteRegStr SHELL_CONTEXT "${INSTNAME_KEY}" "InstallerLanguage" $LANGUAGE
lbl_return:
Pop $0
Return
@@ -215,14 +256,32 @@ FunctionEnd
;; Prep Uninstaller Section
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Function un.onInit
+ # Save $INSTDIR -- it appears to have the correct value before
+ # MULTIUSER_UNINIT, but then gets munged by MULTIUSER_UNINIT?!
+ Push $INSTDIR
+ !insertmacro MULTIUSER_UNINIT
+ Pop $INSTDIR
+
+ # Now read InstallMode.txt from $INSTDIR
+ Push $0
+ ClearErrors
+ FileOpen $0 "$INSTDIR\InstallMode.txt" r
+ IfErrors skipread
+ FileRead $0 $MultiUser.InstallMode
+ FileClose $0
+skipread:
+ Pop $0
%%ENGAGEREGISTRY%%
# Read language from registry and set for uninstaller. Key will be removed on successful uninstall
- ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage"
+ ReadRegStr $0 SHELL_CONTEXT "${INSTNAME_KEY}" "InstallerLanguage"
IfErrors lbl_end
StrCpy $LANGUAGE $0
lbl_end:
+
+## MessageBox MB_OK "After restoring:$\n$$INSTDIR = '$INSTDIR'$\n$$MultiUser.InstallMode = '$MultiUser.InstallMode'$\n$$LANGUAGE = '$LANGUAGE'"
+
Return
FunctionEnd
@@ -272,10 +331,10 @@ FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Section ""
-SetShellVarContext all # Install for all users (if you change this, change it in the uninstall as well)
+# SetShellVarContext is set by MultiUser.nsh initialization.
# Start with some default values.
-StrCpy $INSTPROG "${INSTNAME}"
+StrCpy $INSTNAME "${INSTNAME}"
StrCpy $INSTEXE "${INSTEXE}"
StrCpy $VIEWER_EXE "${VIEWER_EXE}"
StrCpy $INSTSHORTCUT "${SHORTCUT}"
@@ -299,7 +358,7 @@ StrCpy $SHORTCUT_LANG_PARAM "--set InstallLanguage $(LanguageCode)"
CreateDirectory "$SMPROGRAMS\$INSTSHORTCUT"
SetOutPath "$INSTDIR"
CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT.lnk" \
- "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$VIEWER_EXE"
+ "$INSTDIR\$VIEWER_EXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$VIEWER_EXE"
WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Create Account.url" \
@@ -317,31 +376,31 @@ CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\Uninstall $INSTSHORTCUT.lnk" \
# Other shortcuts
SetOutPath "$INSTDIR"
CreateShortCut "$DESKTOP\$INSTSHORTCUT.lnk" \
- "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$VIEWER_EXE"
+ "$INSTDIR\$VIEWER_EXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$VIEWER_EXE"
CreateShortCut "$INSTDIR\$INSTSHORTCUT.lnk" \
- "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$VIEWER_EXE"
+ "$INSTDIR\$VIEWER_EXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$VIEWER_EXE"
CreateShortCut "$INSTDIR\Uninstall $INSTSHORTCUT.lnk" \
'"$INSTDIR\uninst.exe"' ''
# Write registry
-WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "" "$INSTDIR"
-WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" "${VERSION_LONG}"
-WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Shortcut" "$INSTSHORTCUT"
-WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Exe" "$INSTEXE"
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "Publisher" "Linden Research, Inc."
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "URLInfoAbout" "http://secondlife.com/whatis/"
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "URLUpdateInfo" "http://secondlife.com/support/downloads/"
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "HelpLink" "https://support.secondlife.com/contact-support/"
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayName" "$INSTPROG"
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "UninstallString" '"$INSTDIR\uninst.exe"'
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayVersion" "${VERSION_LONG}"
-WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "EstimatedSize" "0x0001D500" # ~117 MB
+WriteRegStr SHELL_CONTEXT "${INSTNAME_KEY}" "" "$INSTDIR"
+WriteRegStr SHELL_CONTEXT "${INSTNAME_KEY}" "Version" "${VERSION_LONG}"
+WriteRegStr SHELL_CONTEXT "${INSTNAME_KEY}" "Shortcut" "$INSTSHORTCUT"
+WriteRegStr SHELL_CONTEXT "${INSTNAME_KEY}" "Exe" "$VIEWER_EXE"
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "Publisher" "Linden Research, Inc."
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "URLInfoAbout" "http://secondlife.com/whatis/"
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "URLUpdateInfo" "http://secondlife.com/support/downloads/"
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "HelpLink" "https://support.secondlife.com/contact-support/"
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "DisplayName" "$INSTNAME"
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "UninstallString" '"$INSTDIR\uninst.exe"'
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "DisplayVersion" "${VERSION_LONG}"
+WriteRegDWORD SHELL_CONTEXT "${MSUNINSTALL_KEY}" "EstimatedSize" "0x0001D500" # ~117 MB
# from FS:Ansariel
-WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayIcon" '"$INSTDIR\$INSTEXE"'
+WriteRegStr SHELL_CONTEXT "${MSUNINSTALL_KEY}" "DisplayIcon" '"$INSTDIR\$VIEWER_EXE"'
# BUG-2707 Disable SEHOP for installed viewer.
-WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\$INSTEXE" "DisableExceptionChainValidation" 1
+WriteRegDWORD SHELL_CONTEXT "${MSNTCURRVER_KEY}\Image File Execution Options\$VIEWER_EXE" "DisableExceptionChainValidation" 1
# Write URL registry info
WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "(default)" "URL:Second Life"
@@ -358,9 +417,8 @@ WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" '"$INSTDIR\$
# URL param must be last item passed to viewer, it ignores subsequent params to avoid parameter injection attacks.
WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" '"$INSTDIR\$VIEWER_EXE" -url "%1"'
-# Only allow Launcher to be the icon
-WriteRegStr HKEY_CLASSES_ROOT "Applications\$INSTEXE" "IsHostApp" ""
-WriteRegStr HKEY_CLASSES_ROOT "Applications\${VIEWER_EXE}" "NoStartPage" ""
+WriteRegStr HKEY_CLASSES_ROOT "Applications\$VIEWER_EXE" "IsHostApp" ""
+##WriteRegStr HKEY_CLASSES_ROOT "Applications\${VIEWER_EXE}" "NoStartPage" ""
# Write out uninstaller
WriteUninstaller "$INSTDIR\uninst.exe"
@@ -381,25 +439,32 @@ SectionEnd
Section Uninstall
# Start with some default values.
-StrCpy $INSTPROG "${INSTNAME}"
+StrCpy $INSTNAME "${INSTNAME}"
StrCpy $INSTEXE "${INSTEXE}"
+StrCpy $VIEWER_EXE "${VIEWER_EXE}"
StrCpy $INSTSHORTCUT "${SHORTCUT}"
-# Make sure the user can install/uninstall
-Call un.CheckIfAdministrator
-
-# Uninstall for all users (if you change this, change it in the install as well)
-SetShellVarContext all
+# SetShellVarContext per the mode saved at install time in registry at
+# MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY
+# MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME
+# Couldn't get NSIS to expand $MultiUser.InstallMode into the function name at Call time
+${If} $MultiUser.InstallMode == 'AllUsers'
+##MessageBox MB_OK "Uninstalling for all users"
+ Call un.MultiUser.InstallMode.AllUsers
+${Else}
+##MessageBox MB_OK "Uninstalling for current user"
+ Call un.MultiUser.InstallMode.CurrentUser
+${EndIf}
# Make sure we're not running
Call un.CloseSecondLife
# Clean up registry keys and subkeys (these should all be !defines somewhere)
-DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG"
-DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG"
+DeleteRegKey SHELL_CONTEXT "${INSTNAME_KEY}"
+DeleteRegKey SHELL_CONTEXT "${MSCURRVER_KEY}\Uninstall\$INSTNAME"
# BUG-2707 Remove entry that disabled SEHOP
-DeleteRegKey HKEY_LOCAL_MACHINE "Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\$INSTEXE"
-DeleteRegKey HKEY_CLASSES_ROOT "Applications\$INSTEXE"
+DeleteRegKey SHELL_CONTEXT "${MSNTCURRVER_KEY}\Image File Execution Options\$VIEWER_EXE"
+##DeleteRegKey HKEY_CLASSES_ROOT "Applications\$INSTEXE"
DeleteRegKey HKEY_CLASSES_ROOT "Applications\${VIEWER_EXE}"
# Clean up shortcuts
@@ -537,6 +602,7 @@ Function RemoveProgFilesOnInst
# Remove old SecondLife.exe to invalidate any old shortcuts to it that may be in non-standard locations. See MAINT-3575
Delete "$INSTDIR\$INSTEXE"
+Delete "$INSTDIR\$VIEWER_EXE"
# Remove old shader files first so fallbacks will work. See DEV-5663
RMDir /r "$INSTDIR\app_settings\shaders"
@@ -570,10 +636,10 @@ Push $2
StrCpy $0 0 # Index number used to iterate via EnumRegKey
LOOP:
- EnumRegKey $1 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
+ EnumRegKey $1 SHELL_CONTEXT "${MSNTCURRVER_KEY}\ProfileList" $0
StrCmp $1 "" DONE # No more users
- ReadRegStr $2 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
+ ReadRegStr $2 SHELL_CONTEXT "${MSNTCURRVER_KEY}\ProfileList\$1" "ProfileImagePath"
StrCmp $2 "" CONTINUE 0 # "ProfileImagePath" value is missing
# Required since ProfileImagePath is of type REG_EXPAND_SZ
@@ -603,7 +669,7 @@ Pop $0
# Delete files in ProgramData\Secondlife
Push $0
- ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Common AppData"
+ ReadRegStr $0 SHELL_CONTEXT "${MSCURRVER_KEY}\Explorer\Shell Folders" "Common AppData"
StrCmp $0 "" +2
RMDir /r "$0\SecondLife"
Pop $0
@@ -624,6 +690,9 @@ Function un.ProgramFiles
# This placeholder is replaced by the complete list of files to uninstall by viewer_manifest.py
%%DELETE_FILES%%
+# our InstallMode.txt
+Delete "$INSTDIR\InstallMode.txt"
+
# Optional/obsolete files. Delete won't fail if they don't exist.
Delete "$INSTDIR\autorun.bat"
Delete "$INSTDIR\dronesettings.ini"
@@ -658,8 +727,8 @@ NOFOLDER:
MessageBox MB_YESNO $(DeleteRegistryKeysMB) IDYES DeleteKeys IDNO NoDelete
DeleteKeys:
- DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Classes\x-grid-location-info"
- DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Classes\secondlife"
+ DeleteRegKey SHELL_CONTEXT "SOFTWARE\Classes\x-grid-location-info"
+ DeleteRegKey SHELL_CONTEXT "SOFTWARE\Classes\secondlife"
DeleteRegKey HKEY_CLASSES_ROOT "x-grid-location-info"
DeleteRegKey HKEY_CLASSES_ROOT "secondlife"
@@ -671,7 +740,13 @@ FunctionEnd
;; After install completes, launch app
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Function .onInstSuccess
-Call CheckWindowsServPack # Warn if not on the latest SP before asking to launch.
+ Push $0
+ FileOpen $0 "$INSTDIR\InstallMode.txt" w
+ # No newline -- this is for our use, not for users to read.
+ FileWrite $0 "$MultiUser.InstallMode"
+ FileClose $0
+ Pop $0
+
Push $R0
Push $0
;; MAINT-7812: Only write nsis.winstall file with /marker switch
@@ -690,11 +765,24 @@ Call CheckWindowsServPack # Warn if not on the latest SP before asking to launc
ClearErrors
Pop $0
Pop $R0
- Push $R0 # Option value, unused#
+
+ Call CheckWindowsServPack # Warn if not on the latest SP before asking to launch.
StrCmp $SKIP_AUTORUN "true" +2;
-# Assumes SetOutPath $INSTDIR
- Exec '"$WINDIR\explorer.exe" "$INSTDIR\$INSTSHORTCUT.lnk"'
- Pop $R0
+ # Assumes SetOutPath $INSTDIR
+ # Run INSTEXE (our updater), passing VIEWER_EXE plus the command-line
+ # arguments built into our shortcuts. This gives the updater a chance
+ # to verify that the viewer we just installed is appropriate for the
+ # running system -- or, if not, to download and install a different
+ # viewer. For instance, if a user running 32-bit Windows installs a
+ # 64-bit viewer, it cannot run on this system. But since the updater
+ # is a 32-bit executable even in the 64-bit viewer package, the
+ # updater can detect the problem and adapt accordingly.
+ # Once everything is in order, the updater will run the specified
+ # viewer with the specified params.
+ # Quote the updater executable and the viewer executable because each
+ # must be a distinct command-line token, but DO NOT quote the language
+ # string because it must decompose into separate command-line tokens.
+ Exec '"$INSTDIR\$INSTEXE" precheck "$INSTDIR\$VIEWER_EXE" $SHORTCUT_LANG_PARAM'
#
FunctionEnd
@@ -731,10 +819,10 @@ FunctionEnd
; StrCpy $0 0 # Index number used to iterate via EnumRegKey
;
; LOOP:
-; EnumRegKey $1 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
+; EnumRegKey $1 SHELL_CONTEXT "${MSNTCURRVER_KEY}\ProfileList" $0
; StrCmp $1 "" DONE # no more users
;
-; ReadRegStr $2 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
+; ReadRegStr $2 SHELL_CONTEXT "${MSNTCURRVER_KEY}\ProfileList\$1" "ProfileImagePath"
; StrCmp $2 "" CONTINUE 0 # "ProfileImagePath" value is missing
;
;# Required since ProfileImagePath is of type REG_EXPAND_SZ
@@ -753,7 +841,7 @@ FunctionEnd
;
;# Copy files in Documents and Settings\All Users\SecondLife
;Push $0
-; ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Common AppData"
+; ReadRegStr $0 SHELL_CONTEXT "${MSCURRVER_KEY}\Explorer\Shell Folders" "Common AppData"
; StrCmp $0 "" +2
; RMDir /r "$2\Application Data\SecondLife\"
;Pop $0
diff --git a/indra/newview/installers/windows/lang_da.nsi b/indra/newview/installers/windows/lang_da.nsi
index 83e1a3ea94..f462c82078 100644
--- a/indra/newview/installers/windows/lang_da.nsi
+++ b/indra/newview/installers/windows/lang_da.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_de.nsi b/indra/newview/installers/windows/lang_de.nsi
index 2a868acc89..8bb20476b3 100644..100755
--- a/indra/newview/installers/windows/lang_de.nsi
+++ b/indra/newview/installers/windows/lang_de.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_en-us.nsi b/indra/newview/installers/windows/lang_en-us.nsi
index f854f5db06..fd4d340816 100644
--- a/indra/newview/installers/windows/lang_en-us.nsi
+++ b/indra/newview/installers/windows/lang_en-us.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_es.nsi b/indra/newview/installers/windows/lang_es.nsi
index 1ecf254ffb..8a81110069 100644..100755
--- a/indra/newview/installers/windows/lang_es.nsi
+++ b/indra/newview/installers/windows/lang_es.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_fr.nsi b/indra/newview/installers/windows/lang_fr.nsi
index bec5835bed..f038c0e419 100644..100755
--- a/indra/newview/installers/windows/lang_fr.nsi
+++ b/indra/newview/installers/windows/lang_fr.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_it.nsi b/indra/newview/installers/windows/lang_it.nsi
index 1d2e150525..bd16d8318f 100644..100755
--- a/indra/newview/installers/windows/lang_it.nsi
+++ b/indra/newview/installers/windows/lang_it.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_ja.nsi b/indra/newview/installers/windows/lang_ja.nsi
index 1bd6526670..71edde1992 100644..100755
--- a/indra/newview/installers/windows/lang_ja.nsi
+++ b/indra/newview/installers/windows/lang_ja.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_pl.nsi b/indra/newview/installers/windows/lang_pl.nsi
index a172f0cdeb..05977847b9 100644
--- a/indra/newview/installers/windows/lang_pl.nsi
+++ b/indra/newview/installers/windows/lang_pl.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_pt-br.nsi b/indra/newview/installers/windows/lang_pt-br.nsi
index 87032fec18..0e7cbeacda 100644..100755
--- a/indra/newview/installers/windows/lang_pt-br.nsi
+++ b/indra/newview/installers/windows/lang_pt-br.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_ru.nsi b/indra/newview/installers/windows/lang_ru.nsi
index 019c66123c..d55aacc971 100644..100755
--- a/indra/newview/installers/windows/lang_ru.nsi
+++ b/indra/newview/installers/windows/lang_ru.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_tr.nsi b/indra/newview/installers/windows/lang_tr.nsi
index 1c4e2c2f48..4746f84482 100644..100755
--- a/indra/newview/installers/windows/lang_tr.nsi
+++ b/indra/newview/installers/windows/lang_tr.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsi
index 355e01a333..397bd0ac81 100644..100755
--- a/indra/newview/installers/windows/lang_zh.nsi
+++ b/indra/newview/installers/windows/lang_zh.nsi
Binary files differ
diff --git a/indra/newview/installers/windows/language_menu.nsi b/indra/newview/installers/windows/language_menu.nsi
index 08ad42532f..2f426a0f47 100644
--- a/indra/newview/installers/windows/language_menu.nsi
+++ b/indra/newview/installers/windows/language_menu.nsi
Binary files differ
diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm
index aebae4c434..1d55537427 100644
--- a/indra/newview/llappdelegate-objc.mm
+++ b/indra/newview/llappdelegate-objc.mm
@@ -25,7 +25,16 @@
*/
#import "llappdelegate-objc.h"
+#if defined(LL_BUGSPLAT)
+#include <boost/filesystem.hpp>
+#include <vector>
+@import BugsplatMac;
+// derived from BugsplatMac's BugsplatTester/AppDelegate.m
+@interface LLAppDelegate () <BugsplatStartupManagerDelegate>
+@end
+#endif
#include "llwindowmacosx-objc.h"
+#include "llappviewermacosx-for-objc.h"
#include <Carbon/Carbon.h> // Used for Text Input Services ("Safe" API - it's supported)
@implementation LLAppDelegate
@@ -47,6 +56,25 @@
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
+ // Call constructViewer() first so our logging subsystem is in place. This
+ // risks missing crashes in the LLAppViewerMacOSX constructor, but for
+ // present purposes it's more important to get the startup sequence
+ // properly logged.
+ // Someday I would like to modify the logging system so that calls before
+ // it's initialized are cached in a std::ostringstream and then, once it's
+ // initialized, "played back" into whatever handlers have been set up.
+ constructViewer();
+
+#if defined(LL_BUGSPLAT)
+ // Engage BugsplatStartupManager *before* calling initViewer() to handle
+ // any crashes during initialization.
+ // https://www.bugsplat.com/docs/platforms/os-x#initialization
+ [BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES;
+ [BugsplatStartupManager sharedManager].askUserDetails = NO;
+ [BugsplatStartupManager sharedManager].delegate = self;
+ [[BugsplatStartupManager sharedManager] start];
+#endif
+
frameTimer = nil;
[self languageUpdated];
@@ -179,4 +207,138 @@
return true;
}
+#if defined(LL_BUGSPLAT)
+
+- (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager
+{
+ CrashMetadata& meta(CrashMetadata_instance());
+ // As of BugsplatMac 1.0.6, userName and userEmail properties are now
+ // exposed by the BugsplatStartupManager. Set them here, since the
+ // defaultUserNameForBugsplatStartupManager and
+ // defaultUserEmailForBugsplatStartupManager methods are called later, for
+ // the *current* run, rather than for the previous crashed run whose crash
+ // report we are about to send.
+ infos("applicationLogForBugsplatStartupManager setting userName = '" +
+ meta.agentFullname + '"');
+ bugsplatStartupManager.userName =
+ [NSString stringWithCString:meta.agentFullname.c_str()
+ encoding:NSUTF8StringEncoding];
+ // Use the email field for OS version, just as we do on Windows, until
+ // BugSplat provides more metadata fields.
+ infos("applicationLogForBugsplatStartupManager setting userEmail = '" +
+ meta.OSInfo + '"');
+ bugsplatStartupManager.userEmail =
+ [NSString stringWithCString:meta.OSInfo.c_str()
+ encoding:NSUTF8StringEncoding];
+ // This strangely-named override method's return value contributes the
+ // User Description metadata field.
+ infos("applicationLogForBugsplatStartupManager -> '" + meta.fatalMessage + "'");
+ return [NSString stringWithCString:meta.fatalMessage.c_str()
+ encoding:NSUTF8StringEncoding];
+}
+
+- (NSString *)applicationKeyForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager signal:(NSString *)signal exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason {
+ // TODO: exceptionName, exceptionReason
+
+ // Windows sends location within region as well, but that's because
+ // BugSplat for Windows intercepts crashes during the same run, and that
+ // information can be queried once. On the Mac, any metadata we have is
+ // written (and rewritten) to the static_debug_info.log file that we read
+ // at the start of the next viewer run. It seems ridiculously expensive to
+ // rewrite that file on every frame in which the avatar moves.
+ std::string regionName(CrashMetadata_instance().regionName);
+ infos("applicationKeyForBugsplatStartupManager -> '" + regionName + "'");
+ return [NSString stringWithCString:regionName.c_str()
+ encoding:NSUTF8StringEncoding];
+}
+
+- (NSString *)defaultUserNameForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager {
+ std::string agentFullname(CrashMetadata_instance().agentFullname);
+ infos("defaultUserNameForBugsplatStartupManager -> '" + agentFullname + "'");
+ return [NSString stringWithCString:agentFullname.c_str()
+ encoding:NSUTF8StringEncoding];
+}
+
+- (NSString *)defaultUserEmailForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager {
+ // Use the email field for OS version, just as we do on Windows, until
+ // BugSplat provides more metadata fields.
+ std::string OSInfo(CrashMetadata_instance().OSInfo);
+ infos("defaultUserEmailForBugsplatStartupManager -> '" + OSInfo + "'");
+ return [NSString stringWithCString:OSInfo.c_str()
+ encoding:NSUTF8StringEncoding];
+}
+
+- (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugsplatStartupManager
+{
+ infos("bugsplatStartupManagerWillSendCrashReport");
+}
+
+struct AttachmentInfo
+{
+ AttachmentInfo(const std::string& path, const std::string& type):
+ pathname(path),
+ basename(boost::filesystem::path(path).filename().string()),
+ mimetype(type)
+ {}
+
+ std::string pathname, basename, mimetype;
+};
+
+- (NSArray<BugsplatAttachment *> *)attachmentsForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager
+{
+ const CrashMetadata& metadata(CrashMetadata_instance());
+
+ // Since we must do very similar processing for each of several file
+ // pathnames, start by collecting them into a vector so we can iterate
+ // instead of spelling out the logic for each.
+ std::vector<AttachmentInfo> info{
+ AttachmentInfo(metadata.logFilePathname, "text/plain"),
+ AttachmentInfo(metadata.userSettingsPathname, "text/xml"),
+ AttachmentInfo(metadata.staticDebugPathname, "text/xml")
+ };
+
+ // We "happen to know" that info[0].basename is "SecondLife.old" -- due to
+ // the fact that BugsplatMac only notices a crash during the viewer run
+ // following the crash. Replace .old with .log to reduce confusion.
+ info[0].basename =
+ boost::filesystem::path(info[0].pathname).stem().string() + ".log";
+
+ NSMutableArray *attachments = [[NSMutableArray alloc] init];
+
+ // Iterate over each AttachmentInfo in info vector
+ for (const AttachmentInfo& attach : info)
+ {
+ NSString *nspathname = [NSString stringWithCString:attach.pathname.c_str()
+ encoding:NSUTF8StringEncoding];
+ NSString *nsbasename = [NSString stringWithCString:attach.basename.c_str()
+ encoding:NSUTF8StringEncoding];
+ NSString *nsmimetype = [NSString stringWithCString:attach.mimetype.c_str()
+ encoding:NSUTF8StringEncoding];
+ NSData *nsdata = [NSData dataWithContentsOfFile:nspathname];
+
+ BugsplatAttachment *attachment =
+ [[BugsplatAttachment alloc] initWithFilename:nsbasename
+ attachmentData:nsdata
+ contentType:nsmimetype];
+
+ [attachments addObject:attachment];
+ infos("attachmentsForBugsplatStartupManager attaching " + attach.pathname);
+ }
+
+ return attachments;
+}
+
+- (void)bugsplatStartupManagerDidFinishSendingCrashReport:(BugsplatStartupManager *)bugsplatStartupManager
+{
+ infos("Sent crash report to BugSplat");
+}
+
+- (void)bugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager didFailWithError:(NSError *)error
+{
+ // TODO: message string from NSError
+ infos("Could not send crash report to BugSplat");
+}
+
+#endif // LL_BUGSPLAT
+
@end
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index f8fa06b527..b7e2f17c46 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -707,6 +707,22 @@ LLAppViewer::LLAppViewer()
//
LLLoginInstance::instance().setPlatformInfo(gPlatform, LLOSInfo::instance().getOSVersionString(), LLOSInfo::instance().getOSStringSimple());
+
+ // Under some circumstances we want to read the static_debug_info.log file
+ // from the previous viewer run between this constructor call and the
+ // init() call, which will overwrite the static_debug_info.log file for
+ // THIS run. So setDebugFileNames() early.
+#if LL_BUGSPLAT
+ // MAINT-8917: don't create a dump directory just for the
+ // static_debug_info.log file
+ std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+#else // ! LL_BUGSPLAT
+ // write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.
+ std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
+#endif // ! LL_BUGSPLAT
+ mDumpPath = logdir;
+ setMiniDumpDir(logdir);
+ setDebugFileNames(logdir);
}
LLAppViewer::~LLAppViewer()
@@ -781,13 +797,6 @@ bool LLAppViewer::init()
initMaxHeapSize() ;
LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize"));
- // write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.
- std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
- mDumpPath = logdir;
- setMiniDumpDir(logdir);
- logdir += gDirUtilp->getDirDelimiter();
- setDebugFileNames(logdir);
-
// Although initLoggingAndGetLastDuration() is the right place to mess with
// setFatalFunction(), we can't query gSavedSettings until after
@@ -876,11 +885,6 @@ bool LLAppViewer::init()
mNumSessions++;
gSavedSettings.setS32("NumSessions", mNumSessions);
- if (gSavedSettings.getBOOL("VerboseLogs"))
- {
- LLError::setPrintLocation(true);
- }
-
// LLKeyboard relies on LLUI to know what some accelerator keys are called.
LLKeyboard::setStringTranslatorFunc( LLTrans::getKeyboardString );
@@ -1083,26 +1087,6 @@ bool LLAppViewer::init()
}
}
-// don't nag developers who need to run the executable directly
-#if LL_RELEASE_FOR_DOWNLOAD
- // MAINT-8305: If we're processing a SLURL, skip the launcher check.
- if (gSavedSettings.getString("CmdLineLoginLocation").empty() && !beingDebugged())
- {
- const char* PARENT = getenv("PARENT");
- if (! (PARENT && std::string(PARENT) == "SL_Launcher"))
- {
- // Don't directly run this executable. Please run the launcher, which
- // will run the viewer itself.
- // Naturally we do not consider this bulletproof. The point is to
- // gently remind a user who *inadvertently* finds him/herself in this
- // situation to do things the Right Way. Anyone who intentionally
- // bypasses this mechanism needs no reminder that s/he's shooting
- // him/herself in the foot.
- LLNotificationsUtil::add("RunLauncher");
- }
- }
-#endif
-
#if LL_WINDOWS
if (gGLManager.mGLVersion < LLFeatureManager::getInstance()->getExpectedGLVersion())
{
@@ -1150,6 +1134,34 @@ bool LLAppViewer::init()
gGLActive = FALSE;
+ LLProcess::Params updater;
+ updater.desc = "updater process";
+ // Because it's the updater, it MUST persist beyond the lifespan of the
+ // viewer itself.
+ updater.autokill = false;
+#if LL_WINDOWS
+ updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "SLVersionChecker.exe");
+#elif LL_DARWIN
+ // explicitly run the system Python interpreter on SLVersionChecker.py
+ updater.executable = "python";
+ updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", "SLVersionChecker.py"));
+#else
+ updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "SLVersionChecker");
+#endif
+ // add LEAP mode command-line argument to whichever of these we selected
+ updater.args.add("leap");
+ // UpdaterServiceSettings
+ updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting")));
+ // channel
+ updater.args.add(LLVersionInfo::getChannel());
+ // testok
+ updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest")));
+ // ForceAddressSize
+ updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize")));
+
+ // Run the updater. An exception from launching the updater should bother us.
+ LLLeap::create(updater, true);
+
// Iterate over --leap command-line options. But this is a bit tricky: if
// there's only one, it won't be an array at all.
LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
@@ -1693,7 +1705,7 @@ bool LLAppViewer::cleanup()
release_start_screen(); // just in case
- LLError::logToFixedBuffer(NULL);
+ LLError::logToFixedBuffer(NULL); // stop the fixed buffer recorder
LL_INFOS() << "Cleaning Up" << LL_ENDL;
@@ -2172,6 +2184,12 @@ void errorCallback(const std::string &error_string)
//Set the ErrorActivated global so we know to create a marker file
gLLErrorActivated = true;
+ gDebugInfo["FatalMessage"] = error_string;
+ // We're not already crashing -- we simply *intend* to crash. Since we
+ // haven't actually trashed anything yet, we can afford to write the whole
+ // static info file.
+ LLAppViewer::instance()->writeDebugInfo();
+
LLError::crashAndLoop(error_string);
}
@@ -3039,14 +3057,11 @@ void LLAppViewer::writeDebugInfo(bool isStatic)
? getStaticDebugFile()
: getDynamicDebugFile() );
- LL_INFOS() << "Opening debug file " << *debug_filename << LL_ENDL;
- llofstream out_file(debug_filename->c_str());
+ LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL;
+ llofstream out_file(debug_filename->c_str());
isStatic ? LLSDSerialize::toPrettyXML(gDebugInfo, out_file)
: LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file);
-
-
- out_file.close();
}
LLSD LLAppViewer::getViewerInfo() const
diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h
new file mode 100644
index 0000000000..37e8a3917a
--- /dev/null
+++ b/indra/newview/llappviewermacosx-for-objc.h
@@ -0,0 +1,53 @@
+/**
+ * @file llappviewermacosx-for-objc.h
+ * @author Nat Goodspeed
+ * @date 2018-06-15
+ * @brief llappviewermacosx.h publishes the C++ API for
+ * llappviewermacosx.cpp, just as
+ * llappviewermacosx-objc.h publishes the Objective-C++ API for
+ * llappviewermacosx-objc.mm.
+ *
+ * This header is intended to publish for Objective-C++ consumers a
+ * subset of the C++ API presented by llappviewermacosx.cpp. It's a
+ * subset because, if an Objective-C++ consumer were to #include
+ * the full llappviewermacosx.h, we would almost surely run into
+ * trouble due to the discrepancy between Objective-C++'s BOOL versus
+ * classic Microsoft/Linden BOOL.
+ *
+ * $LicenseInfo:firstyear=2018&license=viewerlgpl$
+ * Copyright (c) 2018, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H)
+#define LL_LLAPPVIEWERMACOSX_FOR_OBJC_H
+
+#include <string>
+
+void constructViewer();
+bool initViewer();
+void handleUrl(const char* url_utf8);
+bool pumpMainLoop();
+void handleQuit();
+void cleanupViewer();
+void infos(const std::string& message);
+
+// This struct is malleable; it only serves as a way to convey a number of
+// fields from llappviewermacosx.cpp's CrashMetadata_instance() function to the
+// consuming functions in llappdelegate-objc.mm. As long as both those sources
+// are compiled with this same header, the content and order of CrashMetadata
+// can change as needed.
+struct CrashMetadata
+{
+ std::string logFilePathname;
+ std::string userSettingsPathname;
+ std::string staticDebugPathname;
+ std::string OSInfo;
+ std::string agentFullname;
+ std::string regionName;
+ std::string fatalMessage;
+};
+
+CrashMetadata& CrashMetadata_instance();
+
+#endif /* ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) */
diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp
index 1db48119aa..3111540a13 100644
--- a/indra/newview/llappviewermacosx.cpp
+++ b/indra/newview/llappviewermacosx.cpp
@@ -36,20 +36,25 @@
#include "llappviewermacosx-objc.h"
#include "llappviewermacosx.h"
+#include "llappviewermacosx-for-objc.h"
#include "llwindowmacosx-objc.h"
#include "llcommandlineparser.h"
+#include "llsdserialize.h"
#include "llviewernetwork.h"
#include "llviewercontrol.h"
#include "llmd5.h"
#include "llfloaterworldmap.h"
#include "llurldispatcher.h"
+#include "llerrorcontrol.h"
+#include "llvoavatarself.h" // for gAgentAvatarp->getFullname()
#include <ApplicationServices/ApplicationServices.h>
#ifdef LL_CARBON_CRASH_HANDLER
#include <Carbon/Carbon.h>
#endif
#include <vector>
#include <exception>
+#include <fstream>
#include "lldir.h"
#include <signal.h>
@@ -81,7 +86,7 @@ static void exceptionTerminateHandler()
gOldTerminateHandler(); // call old terminate() handler
}
-bool initViewer()
+void constructViewer()
{
// Set the working dir to <bundle>/Contents/Resources
if (chdir(gDirUtilp->getAppRODataDir().c_str()) == -1)
@@ -97,18 +102,20 @@ bool initViewer()
gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);
gViewerAppPtr->setErrorHandler(LLAppViewer::handleViewerCrash);
+}
-
+bool initViewer()
+{
bool ok = gViewerAppPtr->init();
if(!ok)
{
LL_WARNS() << "Application init failed." << LL_ENDL;
}
- else if (!gHandleSLURL.empty())
- {
- dispatchUrl(gHandleSLURL);
- gHandleSLURL = "";
- }
+ else if (!gHandleSLURL.empty())
+ {
+ dispatchUrl(gHandleSLURL);
+ gHandleSLURL = "";
+ }
return ok;
}
@@ -147,6 +154,73 @@ void cleanupViewer()
gViewerAppPtr = NULL;
}
+// The BugsplatMac API is structured as a number of different method
+// overrides, each returning a different piece of metadata. But since we
+// obtain such metadata by opening and parsing a file, it seems ridiculous to
+// reopen and reparse it for every individual string desired. What we want is
+// to open and parse the file once, retaining the data for subsequent
+// requests. That's why this is an LLSingleton.
+// Another approach would be to provide a function that simply returns
+// CrashMetadata, storing the struct in LLAppDelegate, but nat doesn't know
+// enough Objective-C++ to code that. We'd still have to detect which of the
+// method overrides is called first so that the results are order-insensitive.
+class CrashMetadataSingleton: public CrashMetadata, public LLSingleton<CrashMetadataSingleton>
+{
+ LLSINGLETON(CrashMetadataSingleton);
+
+ // convenience method to log each metadata field retrieved by constructor
+ std::string get_metadata(const LLSD& info, const LLSD::String& key) const
+ {
+ std::string data(info[key].asString());
+ LL_INFOS() << " " << key << "='" << data << "'" << LL_ENDL;
+ return data;
+ }
+};
+
+// Populate the fields of our public base-class struct.
+CrashMetadataSingleton::CrashMetadataSingleton()
+{
+ // Note: we depend on being able to read the static_debug_info.log file
+ // from the *previous* run before we overwrite it with the new one for
+ // *this* run. LLAppViewer initialization must happen in the Right Order.
+ staticDebugPathname = *gViewerAppPtr->getStaticDebugFile();
+ std::ifstream static_file(staticDebugPathname);
+ LLSD info;
+ if (! static_file.is_open())
+ {
+ LL_INFOS() << "Can't open '" << staticDebugPathname
+ << "'; no metadata about previous run" << LL_ENDL;
+ }
+ else if (! LLSDSerialize::deserialize(info, static_file, LLSDSerialize::SIZE_UNLIMITED))
+ {
+ LL_INFOS() << "Can't parse '" << staticDebugPathname
+ << "'; no metadata about previous run" << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "Metadata from '" << staticDebugPathname << "':" << LL_ENDL;
+ logFilePathname = get_metadata(info, "SLLog");
+ userSettingsPathname = get_metadata(info, "SettingsFilename");
+ OSInfo = get_metadata(info, "OSInfo");
+ agentFullname = get_metadata(info, "LoginName");
+ // Translate underscores back to spaces
+ LLStringUtil::replaceChar(agentFullname, '_', ' ');
+ regionName = get_metadata(info, "CurrentRegion");
+ fatalMessage = get_metadata(info, "FatalMessage");
+ }
+}
+
+// Avoid having to compile all of our LLSingleton machinery in Objective-C++.
+CrashMetadata& CrashMetadata_instance()
+{
+ return CrashMetadataSingleton::instance();
+}
+
+void infos(const std::string& message)
+{
+ LL_INFOS() << message << LL_ENDL;
+}
+
int main( int argc, char **argv )
{
// Store off the command line args for use later.
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 3942613ee0..fff2653c98 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -66,8 +66,101 @@
#endif
#include "stringize.h"
+#include "lldir.h"
+#include "llerrorcontrol.h"
+#include <fstream>
#include <exception>
+
+// Bugsplat (http://bugsplat.com) crash reporting tool
+#ifdef LL_BUGSPLAT
+#include "BugSplat.h"
+#include "reader.h" // JsonCpp
+#include "llagent.h" // for agent location
+#include "llviewerregion.h"
+#include "llvoavatarself.h" // for agent name
+
+namespace
+{
+ // MiniDmpSender's constructor is defined to accept __wchar_t* instead of
+ // plain wchar_t*. That said, wunder() returns std::basic_string<__wchar_t>,
+ // NOT plain __wchar_t*, despite the apparent convenience. Calling
+ // wunder(something).c_str() as an argument expression is fine: that
+ // std::basic_string instance will survive until the function returns.
+ // Calling c_str() on a std::basic_string local to wunder() would be
+ // Undefined Behavior: we'd be left with a pointer into a destroyed
+ // std::basic_string instance. But we can do that with a macro...
+ #define WCSTR(string) wunder(string).c_str()
+
+ // It would be nice if, when wchar_t is the same as __wchar_t, this whole
+ // function would optimize away. However, we use it only for the arguments
+ // to the BugSplat API -- a handful of calls.
+ inline std::basic_string<__wchar_t> wunder(const std::wstring& str)
+ {
+ return { str.begin(), str.end() };
+ }
+
+ // when what we have in hand is a std::string, convert from UTF-8 using
+ // specific wstringize() overload
+ inline std::basic_string<__wchar_t> wunder(const std::string& str)
+ {
+ return wunder(wstringize(str));
+ }
+
+ // Irritatingly, MiniDmpSender::setCallback() is defined to accept a
+ // classic-C function pointer instead of an arbitrary C++ callable. If it
+ // did accept a modern callable, we could pass a lambda that binds our
+ // MiniDmpSender pointer. As things stand, though, we must define an
+ // actual function and store the pointer statically.
+ static MiniDmpSender *sBugSplatSender = nullptr;
+
+ bool bugsplatSendLog(UINT nCode, LPVOID lpVal1, LPVOID lpVal2)
+ {
+ if (nCode == MDSCB_EXCEPTIONCODE)
+ {
+ // send the main viewer log file
+ // widen to wstring, convert to __wchar_t, then pass c_str()
+ sBugSplatSender->sendAdditionalFile(
+ WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.log")));
+
+ sBugSplatSender->sendAdditionalFile(
+ WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "settings.xml")));
+
+ sBugSplatSender->sendAdditionalFile(
+ WCSTR(*LLAppViewer::instance()->getStaticDebugFile()));
+
+ // We don't have an email address for any user. Hijack this
+ // metadata field for the platform identifier.
+ sBugSplatSender->setDefaultUserEmail(
+ WCSTR(STRINGIZE(LLOSInfo::instance().getOSStringSimple() << " ("
+ << ADDRESS_SIZE << "-bit)")));
+
+ if (gAgentAvatarp)
+ {
+ // user name, when we have it
+ sBugSplatSender->setDefaultUserName(WCSTR(gAgentAvatarp->getFullname()));
+ }
+
+ // LL_ERRS message, when there is one
+ sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage()));
+
+ if (gAgent.getRegion())
+ {
+ // region location, when we have it
+ LLVector3 loc = gAgent.getPositionAgent();
+ sBugSplatSender->resetAppIdentifier(
+ WCSTR(STRINGIZE(gAgent.getRegion()->getName()
+ << '/' << loc.mV[0]
+ << '/' << loc.mV[1]
+ << '/' << loc.mV[2])));
+ }
+ } // MDSCB_EXCEPTIONCODE
+
+ return false;
+ }
+}
+#endif // LL_BUGSPLAT
+
namespace
{
void (*gOldTerminateHandler)() = NULL;
@@ -495,15 +588,71 @@ 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"));
+ // Use llifstream instead of std::ifstream because LL_PATH_EXECUTABLE
+ // could contain non-ASCII characters, which std::ifstream doesn't handle.
+ llifstream 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/llexternaleditor.cpp b/indra/newview/llexternaleditor.cpp
index df9c848cb8..776bbf78c2 100644
--- a/indra/newview/llexternaleditor.cpp
+++ b/indra/newview/llexternaleditor.cpp
@@ -31,6 +31,7 @@
#include "llui.h"
#include "llprocess.h"
#include "llsdutil.h"
+#include "llstring.h"
#include <boost/foreach.hpp>
// static
@@ -188,12 +189,12 @@ std::string LLExternalEditor::findCommand(
cmd = LLUI::sSettingGroups["config"]->getString(sSetting);
LL_INFOS() << "Using setting" << LL_ENDL;
}
- else // otherwise use the path specified by the environment variable
+ else // otherwise use the path specified by the environment variable
{
- char* env_var_val = getenv(env_var.c_str());
+ auto env_var_val(LLStringUtil::getoptenv(env_var));
if (env_var_val)
{
- cmd = env_var_val;
+ cmd = *env_var_val;
LL_INFOS() << "Using env var " << env_var << LL_ENDL;
}
}
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index bf4fc029ee..08f41306b0 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -258,14 +258,12 @@ bool LLLoginInstance::handleLoginEvent(const LLSD& event)
mLoginState = event["state"].asString();
mResponseData = event["data"];
-
+
if(event.has("transfer_rate"))
{
mTransferRate = event["transfer_rate"].asReal();
}
-
-
// Call the method registered in constructor, if any, for more specific
// handling
mDispatcher.try_call(event);
@@ -281,6 +279,14 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
// Login has failed.
// Figure out why and respond...
LLSD response = event["data"];
+ LLSD updater = response["updater"];
+
+ // Always provide a response to the updater, if in fact the updater
+ // contacted us, if in fact the ping contains a 'reply' key. Most code
+ // paths tell it not to proceed with updating.
+ ResponsePtr resp(std::make_shared<LLEventAPI::Response>
+ (LLSDMap("update", false), updater));
+
std::string reason_response = response["reason"].asString();
std::string message_response = response["message"].asString();
LL_DEBUGS("LLLogin") << "reason " << reason_response
@@ -333,17 +339,44 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
}
else if(reason_response == "update")
{
- // This shouldn't happen - the viewer manager should have forced an update;
- // possibly the user ran the viewer directly and bypassed the update check
+ // This can happen if the user clicked Login quickly, before we heard
+ // back from the Viewer Version Manager, but login failed because
+ // login.cgi is insisting on a required update. We were called with an
+ // event that bundles both the login.cgi 'response' and the
+ // synchronization event from the 'updater'.
std::string required_version = response["message_args"]["VERSION"];
LL_WARNS("LLLogin") << "Login failed because an update to version " << required_version << " is required." << LL_ENDL;
if (gViewerWindow)
gViewerWindow->setShowProgress(FALSE);
- LLSD data(LLSD::emptyMap());
- data["VERSION"] = required_version;
- LLNotificationsUtil::add("RequiredUpdate", data, LLSD::emptyMap(), boost::bind(&LLLoginInstance::handleLoginDisallowed, this, _1, _2));
+ LLSD args(LLSDMap("VERSION", required_version));
+ if (updater.isUndefined())
+ {
+ // If the updater failed to shake hands, better advise the user to
+ // download the update him/herself.
+ LLNotificationsUtil::add(
+ "RequiredUpdate",
+ args,
+ updater,
+ boost::bind(&LLLoginInstance::handleLoginDisallowed, this, _1, _2));
+ }
+ else
+ {
+ // If we've heard from the updater that an update is required,
+ // then display the prompt that assures the user we'll take care
+ // of it. This is the one case in which we bind 'resp':
+ // instead of destroying our Response object (and thus sending a
+ // negative reply to the updater) as soon as we exit this
+ // function, bind our shared_ptr so it gets passed into
+ // syncWithUpdater. That ensures that the response is delayed
+ // until the user has responded to the notification.
+ LLNotificationsUtil::add(
+ "PauseForUpdate",
+ args,
+ updater,
+ boost::bind(&LLLoginInstance::syncWithUpdater, this, resp, _1, _2));
+ }
}
else if( reason_response == "key"
|| reason_response == "presence"
@@ -366,6 +399,19 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
}
}
+void LLLoginInstance::syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response)
+{
+ LL_INFOS("LLLogin") << "LLLoginInstance::syncWithUpdater" << LL_ENDL;
+ // 'resp' points to an instance of LLEventAPI::Response that will be
+ // destroyed as soon as we return and the notification response functor is
+ // unregistered. Modify it so that it tells the updater to go ahead and
+ // perform the update. Naturally, if we allowed the user a choice as to
+ // whether to proceed or not, this assignment would reflect the user's
+ // selection.
+ (*resp)["update"] = true;
+ attemptComplete();
+}
+
void LLLoginInstance::handleLoginDisallowed(const LLSD& notification, const LLSD& response)
{
attemptComplete();
@@ -425,7 +471,6 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
return true;
}
-
std::string construct_start_string()
{
std::string start;
diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h
index 651ad10afb..b759b43474 100644
--- a/indra/newview/lllogininstance.h
+++ b/indra/newview/lllogininstance.h
@@ -28,8 +28,10 @@
#define LL_LLLOGININSTANCE_H
#include "lleventdispatcher.h"
+#include "lleventapi.h"
#include <boost/scoped_ptr.hpp>
#include <boost/function.hpp>
+#include <memory> // std::shared_ptr
#include "llsecapi.h"
class LLLogin;
class LLEventStream;
@@ -68,6 +70,7 @@ public:
LLNotificationsInterface& getNotificationsInterface() const { return *mNotifications; }
private:
+ typedef std::shared_ptr<LLEventAPI::Response> ResponsePtr;
void constructAuthParams(LLPointer<LLCredential> user_credentials);
void updateApp(bool mandatory, const std::string& message);
bool updateDialogCallback(const LLSD& notification, const LLSD& response);
@@ -77,7 +80,8 @@ private:
void handleLoginSuccess(const LLSD& event);
void handleDisconnect(const LLSD& event);
void handleIndeterminate(const LLSD& event);
- void handleLoginDisallowed(const LLSD& notification, const LLSD& response);
+ void handleLoginDisallowed(const LLSD& notification, const LLSD& response);
+ void syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response);
bool handleTOSResponse(bool v, const std::string& key);
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 37aaece5d6..243ba00457 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -480,8 +480,7 @@ bool idle_startup()
if (!found_template)
{
message_template_path =
- gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE,
- "../Resources/app_settings",
+ gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,
"message_template.msg");
found_template = LLFile::fopen(message_template_path.c_str(), "r"); /* Flawfinder: ignore */
}
diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp
index 375dce485d..4e07223784 100644
--- a/indra/newview/llversioninfo.cpp
+++ b/indra/newview/llversioninfo.cpp
@@ -101,14 +101,11 @@ namespace
{
// LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The
// macro expands to the string name of the channel, but without quotes. We
- // need to turn it into a quoted string. This macro trick does that.
-#define stringize_inner(x) #x
-#define stringize_outer(x) stringize_inner(x)
-
+ // need to turn it into a quoted string. LL_TO_STRING() does that.
/// Storage of the channel name the viewer is using.
// The channel name is set by hardcoded constant,
// or by calling LLVersionInfo::resetChannel()
- std::string sWorkingChannelName(stringize_outer(LL_VIEWER_CHANNEL));
+ std::string sWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL));
// Storage for the "version and channel" string.
// This will get reset too.
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index ba733bb068..0d7e295b9a 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -44,6 +44,7 @@
#include "llagent.h"
#include "llagentcamera.h"
+#include "llappviewer.h"
#include "llavatarrenderinfoaccountant.h"
#include "llcallingcard.h"
#include "llcommandhandler.h"
@@ -105,6 +106,18 @@ typedef std::map<std::string, std::string> CapabilityMap;
static void log_capabilities(const CapabilityMap &capmap);
+namespace
+{
+
+void newRegionEntry(LLViewerRegion& region)
+{
+ LL_INFOS("LLViewerRegion") << "Entering region [" << region.getName() << "]" << LL_ENDL;
+ gDebugInfo["CurrentRegion"] = region.getName();
+ LLAppViewer::instance()->writeDebugInfo();
+}
+
+} // anonymous namespace
+
// support for secondlife:///app/region/{REGION} SLapps
// N.B. this is defined to work exactly like the classic secondlife://{REGION}
// However, the later syntax cannot support spaces in the region name because
@@ -252,6 +265,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
return; // this error condition is not recoverable.
}
+ // record that we just entered a new region
+ newRegionEntry(*regionp);
+
// After a few attempts, continue login. But keep trying to get the caps:
if (mSeedCapAttempts >= mSeedCapMaxAttemptsBeforeLogin &&
STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState())
@@ -375,6 +391,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle)
break; // this error condition is not recoverable.
}
+ // record that we just entered a new region
+ newRegionEntry(*regionp);
+
LLSD capabilityNames = LLSD::emptyArray();
buildCapabilityNames(capabilityNames);
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 3b12816c99..0f086711d9 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -306,6 +306,9 @@ private:
RecordToChatConsole::RecordToChatConsole():
mRecorder(new RecordToChatConsoleRecorder())
{
+ mRecorder->showTags(false);
+ mRecorder->showLocation(false);
+ mRecorder->showMultiline(true);
}
////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index ee333bcee2..cf40058c34 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -770,14 +770,13 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
{
#ifndef VIVOXDAEMON_REMOTEHOST
// Launch the voice daemon
- std::string exe_path = gDirUtilp->getExecutableDir();
- exe_path += gDirUtilp->getDirDelimiter();
+ std::string exe_path = gDirUtilp->getAppRODataDir();
#if LL_WINDOWS
- exe_path += "SLVoice.exe";
+ gDirUtilp->append(exe_path, "SLVoice.exe");
#elif LL_DARWIN
- exe_path += "../Resources/SLVoice";
+ gDirUtilp->append(exe_path, "SLVoice");
#else
- exe_path += "SLVoice";
+ gDirUtilp->append(exe_path, "SLVoice");
#endif
// See if the vivox executable exists
llstat s;
diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp
index 81d4e30a7a..8dcef2c7cd 100644
--- a/indra/newview/llwebprofile.cpp
+++ b/indra/newview/llwebprofile.cpp
@@ -33,6 +33,7 @@
#include "llimagepng.h"
#include "llsdserialize.h"
+#include "llstring.h"
// newview
#include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions
@@ -264,6 +265,5 @@ void LLWebProfile::reportImageUploadStatus(bool ok)
std::string LLWebProfile::getAuthCookie()
{
// This is needed to test image uploads on Linux viewer built with OpenSSL 1.0.0 (0.9.8 works fine).
- const char* debug_cookie = getenv("LL_SNAPSHOT_COOKIE");
- return debug_cookie ? debug_cookie : sAuthCookie;
+ return LLStringUtil::getenv("LL_SNAPSHOT_COOKIE", sAuthCookie);
}
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index efc5a30b63..d4f60078ad 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -3851,7 +3851,6 @@ Finished download of raw terrain file to:
name="RequiredUpdate"
type="alertmodal">
Version [VERSION] is required for login.
-This should have been updated for you but apparently was not.
Please download from https://secondlife.com/support/downloads/
<tag>confirm</tag>
<usetemplate
@@ -3861,6 +3860,44 @@ Please download from https://secondlife.com/support/downloads/
<notification
icon="alertmodal.tga"
+ name="PauseForUpdate"
+ type="alertmodal">
+Version [VERSION] is required for login.
+Click OK to download and install.
+ <tag>confirm</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="OptionalUpdateReady"
+ type="alertmodal">
+Version [VERSION] has been downloaded and is ready to install.
+Click OK to install.
+ <tag>confirm</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="PromptOptionalUpdate"
+ type="alertmodal">
+Version [VERSION] has been downloaded and is ready to install.
+Proceed?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Not Now"
+ name="yesnocancelbuttons"
+ notext="Skip"
+ yestext="Install"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
name="LoginFailedUnknown"
type="alertmodal">
Sorry, login failed for an unrecognized reason.
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..c0f642c852 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -26,19 +26,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
$/LicenseInfo$
"""
-import sys
-import os
-import os.path
-import shutil
import errno
import json
+import os
+import os.path
import plistlib
import random
import re
+import shutil
import stat
import subprocess
+import sys
import tarfile
import time
+import zipfile
viewer_dir = os.path.dirname(__file__)
# Add indra/lib/python to our path so we don't have to muck with PYTHONPATH.
@@ -63,7 +64,7 @@ class ViewerManifest(LLManifest):
self.path(src="../../etc/message.xml", dst="app_settings/message.xml")
if self.is_packaging_viewer():
- with self.prefix(src="app_settings"):
+ with self.prefix(src_dst="app_settings"):
self.exclude("logcontrol.xml")
self.exclude("logcontrol-dev.xml")
self.path("*.ini")
@@ -85,7 +86,7 @@ class ViewerManifest(LLManifest):
# ... and the included spell checking dictionaries
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
- with self.prefix(src=pkgdir,dst=""):
+ with self.prefix(src=pkgdir):
self.path("dictionaries")
# include the extracted packages information (see BuildPackagesInfo.cmake)
@@ -107,17 +108,18 @@ class ViewerManifest(LLManifest):
Type='String',
Value=''))
settings_install = {}
- if 'sourceid' in self.args and self.args['sourceid']:
+ sourceid = self.args.get('sourceid')
+ if sourceid:
settings_install['sourceid'] = settings_template['sourceid'].copy()
- settings_install['sourceid']['Value'] = self.args['sourceid']
- print "Set sourceid in settings_install.xml to '%s'" % self.args['sourceid']
+ settings_install['sourceid']['Value'] = sourceid
+ print "Set sourceid in settings_install.xml to '%s'" % sourceid
- if 'channel_suffix' in self.args and self.args['channel_suffix']:
+ if self.args.get('channel_suffix'):
settings_install['CmdLineChannel'] = settings_template['CmdLineChannel'].copy()
settings_install['CmdLineChannel']['Value'] = self.channel_with_pkg_suffix()
print "Set CmdLineChannel in settings_install.xml to '%s'" % self.channel_with_pkg_suffix()
- if 'grid' in self.args and self.args['grid']:
+ if self.args.get('grid'):
settings_install['CmdLineGridChoice'] = settings_template['CmdLineGridChoice'].copy()
settings_install['CmdLineGridChoice']['Value'] = self.grid()
print "Set CmdLineGridChoice in settings_install.xml to '%s'" % self.grid()
@@ -129,20 +131,20 @@ class ViewerManifest(LLManifest):
src="environment")
- with self.prefix(src="character"):
+ with self.prefix(src_dst="character"):
self.path("*.llm")
self.path("*.xml")
self.path("*.tga")
# Include our fonts
- with self.prefix(src="fonts"):
+ with self.prefix(src_dst="fonts"):
self.path("*.ttf")
self.path("*.txt")
# skins
- with self.prefix(src="skins"):
+ with self.prefix(src_dst="skins"):
# include the entire textures directory recursively
- with self.prefix(src="*/textures"):
+ with self.prefix(src_dst="*/textures"):
self.path("*/*.tga")
self.path("*/*.j2c")
self.path("*/*.jpg")
@@ -170,7 +172,7 @@ class ViewerManifest(LLManifest):
# local_assets dir (for pre-cached textures)
- with self.prefix(src="local_assets"):
+ with self.prefix(src_dst="local_assets"):
self.path("*.j2c")
self.path("*.tga")
@@ -186,6 +188,10 @@ class ViewerManifest(LLManifest):
"Address Size":self.address_size,
"Update Service":"https://update.secondlife.com/update",
}
+ # Only store this if it's both present and non-empty
+ bugsplat_db = self.args.get('bugsplat')
+ if bugsplat_db:
+ build_data_dict["BugSplat DB"] = bugsplat_db
build_data_dict = self.finish_build_data_dict(build_data_dict)
with open(os.path.join(os.pardir,'build_data.json'), 'w') as build_data_handle:
json.dump(build_data_dict,build_data_handle)
@@ -206,8 +212,9 @@ class ViewerManifest(LLManifest):
def channel_with_pkg_suffix(self):
fullchannel=self.channel()
- if 'channel_suffix' in self.args and self.args['channel_suffix']:
- fullchannel+=' '+self.args['channel_suffix']
+ channel_suffix = self.args.get('channel_suffix')
+ if channel_suffix:
+ fullchannel+=' '+channel_suffix
return fullchannel
def channel_variant(self):
@@ -215,8 +222,7 @@ class ViewerManifest(LLManifest):
return self.channel().replace(CHANNEL_VENDOR_BASE, "").strip()
def channel_type(self): # returns 'release', 'beta', 'project', or 'test'
- global CHANNEL_VENDOR_BASE
- channel_qualifier=self.channel().replace(CHANNEL_VENDOR_BASE, "").lower().strip()
+ channel_qualifier=self.channel_variant().lower()
if channel_qualifier.startswith('release'):
channel_type='release'
elif channel_qualifier.startswith('beta'):
@@ -234,11 +240,12 @@ class ViewerManifest(LLManifest):
if self.channel_type() == 'release':
suffix=suffix.replace('Release', '').strip()
# for the base release viewer, suffix will now be null - for any other, append what remains
- if len(suffix) > 0:
- suffix = "_"+ ("_".join(suffix.split()))
+ if suffix:
+ suffix = "_".join([''] + suffix.split())
# the additional_packages mechanism adds more to the installer name (but not to the app name itself)
- if 'channel_suffix' in self.args and self.args['channel_suffix']:
- suffix+='_'+("_".join(self.args['channel_suffix'].split()))
+ # ''.split() produces empty list, so suffix only changes if
+ # channel_suffix is non-empty
+ suffix = "_".join([suffix] + self.args.get('channel_suffix', '').split())
return suffix
def installer_base_name(self):
@@ -424,7 +431,8 @@ class WindowsManifest(ViewerManifest):
def finish_build_data_dict(self, build_data_dict):
#MAINT-7294: Windows exe names depend on channel name, so write that in also
- build_data_dict.update({'Executable':self.final_exe()})
+ build_data_dict['Executable'] = self.final_exe()
+ build_data_dict['AppName'] = self.app_name()
return build_data_dict
def test_msvcrt_and_copy_action(self, src, dst):
@@ -470,7 +478,7 @@ class WindowsManifest(ViewerManifest):
pass
except NoMatchingAssemblyException as err:
pass
-
+
self.ccopy(src,dst)
else:
raise Exception("Directories are not supported by test_CRT_and_copy_action()")
@@ -488,24 +496,17 @@ 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")
+ self.path('SLVersionChecker.exe')
- #VMP Tkinter icons
- with self.prefix("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"):
- self.path("*.py")
- self.path("_cllsd.so")
+ 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")
# Plugin host application
self.path2basename(os.path.join(os.pardir,
@@ -513,8 +514,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 +581,17 @@ class WindowsManifest(ViewerManifest):
# Hunspell
self.path("libhunspell.dll")
+ # BugSplat
+ if self.args.get('bugsplat'):
+ if(self.address_size == 64):
+ self.path("BsSndRpt64.exe")
+ self.path("BugSplat64.dll")
+ self.path("BugSplatRc64.dll")
+ else:
+ self.path("BsSndRpt.exe")
+ self.path("BugSplat.dll")
+ self.path("BugSplatRc.dll")
+
# For google-perftools tcmalloc allocator.
try:
if self.args['configuration'].lower() == 'debug':
@@ -589,116 +601,118 @@ 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/")
-
- # pull in the crash logger and updater from other projects
+ 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 from other projects
# tag:"crash-logger" here as a cue to the exporter
self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'],
dst="win_crash_logger.exe")
@@ -768,7 +782,7 @@ class WindowsManifest(ViewerManifest):
substitution_strings['installer_file'] = installer_file
version_vars = """
- !define INSTEXE "SL_Launcher.exe"
+ !define INSTEXE "SLVersionChecker.exe"
!define VERSION "%(version_short)s"
!define VERSION_LONG "%(version)s"
!define VERSION_DASHES "%(version_dashes)s"
@@ -791,10 +805,10 @@ class WindowsManifest(ViewerManifest):
if(self.address_size == 64):
engage_registry="SetRegView 64"
- program_files="$PROGRAMFILES64"
+ program_files="!define MULTIUSER_USE_PROGRAMFILES64"
else:
engage_registry="SetRegView 32"
- program_files="$PROGRAMFILES32"
+ program_files=""
tempfile = "secondlife_setup_tmp.nsi"
# the following replaces strings in the nsi template
@@ -812,16 +826,15 @@ class WindowsManifest(ViewerManifest):
# note that the enclosing setup exe is signed later, after the makensis makes it.
# Unlike the viewer binary, the VMP filenames are invariant with respect to version, os, etc.
for exe in (
- "SL_Launcher.exe",
+ self.final_exe(),
+ "SLVersionChecker.exe",
):
self.sign(exe)
- # We use the Unicode version of NSIS, available from
- # http://www.scratchpaper.com/
# Check two paths, one for Program Files, and one for Program Files (x86).
# Yay 64bit windows.
for ProgramFiles in 'ProgramFiles', 'ProgramFiles(x86)':
- NSIS_path = os.path.expandvars(r'${%s}\NSIS\Unicode\makensis.exe' % ProgramFiles)
+ NSIS_path = os.path.expandvars(r'${%s}\NSIS\makensis.exe' % ProgramFiles)
if os.path.exists(NSIS_path):
break
installer_created=False
@@ -879,395 +892,327 @@ class DarwinManifest(ViewerManifest):
# darwin requires full app bundle packaging even for debugging.
return True
- def construct(self):
- # These are the names of the top-level application and the embedded
- # applications for the VMP and for the actual viewer, respectively.
- # These names, without the .app suffix, determine the flyover text for
- # their corresponding Dock icons.
- toplevel_app, toplevel_icon = "Second Life.app", "secondlife.icns"
- launcher_app, launcher_icon = "Second Life Launcher.app", "secondlife.icns"
- viewer_app, viewer_icon = "Second Life Viewer.app", "secondlife.icns"
+ def is_rearranging(self):
+ # That said, some stuff should still only be performed once.
+ # Are either of these actions in 'actions'? Is the set intersection
+ # non-empty?
+ return bool(set(["package", "unpacked"]).intersection(self.args['actions']))
+ def construct(self):
# 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="")
+ self.path(os.path.join(self.args['configuration'], self.channel()+".app"), dst="")
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
relpkgdir = os.path.join(pkgdir, "lib", "release")
debpkgdir = os.path.join(pkgdir, "lib", "debug")
- # -------------------- top-level Second Life.app ---------------------
- # top-level Second Life application is only a container
with self.prefix(src="", 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)
-
- # the one file in top-level MacOS directory is the trampoline to
- # our nested launcher_app
+ bugsplat_db = self.args.get('bugsplat')
+ if bugsplat_db:
+ # Inject BugsplatServerURL into Info.plist if provided.
+ Info_plist = self.dst_path_of("Info.plist")
+ Info = plistlib.readPlist(Info_plist)
+ # https://www.bugsplat.com/docs/platforms/os-x#configuration
+ Info["BugsplatServerURL"] = \
+ "https://{}.bugsplat.com/".format(bugsplat_db)
+ self.put_in_file(
+ plistlib.writePlistToString(Info),
+ os.path.basename(Info_plist),
+ "Info.plist")
+
+ # CEF framework goes inside 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)
+
+ if self.args.get('bugsplat'):
+ self.path2basename(relpkgdir, "BugsplatMac.framework")
+
with self.prefix(dst="MacOS"):
- toplevel_MacOS = self.get_dst_prefix()
- trampoline = self.put_in_file("""\
-#!/bin/bash
-open "%s" --args "$@"
-""" %
- # up one directory from MacOS to its sibling Resources directory
- os.path.join('$(dirname "$0")', os.pardir, 'Resources', launcher_app),
- "SL_Launcher", # write this file
- "trampoline") # flag to add to list of copied files
- # Script must be executable
- self.run_command(["chmod", "+x", trampoline])
-
- # Make a symlink to a nested app Frameworks directory that doesn't
- # yet exist. We shouldn't need this; the only things that need
- # Frameworks are nested apps under viewer_app, and they should
- # simply find its Contents/Frameworks by relative pathnames. But
- # empirically, we do: if we omit this symlink, CEF doesn't work --
- # the login splash screen doesn't even display. SIIIIGH.
- # We're passing a path that's already relative, hence symlinkf()
- # rather than relsymlinkf().
- self.symlinkf(os.path.join("Resources", viewer_app, "Contents", "Frameworks"))
-
- with self.prefix(src="", dst="Resources"):
- # top-level Resources directory should be pretty sparse
- # need .icns file referenced by top-level Info.plist
+ executable = self.dst_path_of(self.channel())
+ if self.args.get('bugsplat'):
+ # According to Apple Technical Note TN2206:
+ # https://developer.apple.com/library/archive/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG207
+ # "If an app uses @rpath or an absolute path to link to a
+ # dynamic library outside of the app, the app will be
+ # rejected by Gatekeeper. ... Neither the codesign nor the
+ # spctl tool will show the error."
+ # (Thanks, Apple. Maybe fix spctl to warn?)
+ # The BugsplatMac framework embeds @rpath, which is
+ # causing scary Gatekeeper popups at viewer start. Work
+ # around this by changing the reference baked into our
+ # viewer. The install_name_tool -change option needs the
+ # previous value. Instead of guessing -- which might
+ # silently be defeated by a BugSplat SDK update that
+ # changes their baked-in @rpath -- ask for the path
+ # stamped into the framework.
+ # Let exception, if any, propagate -- if this doesn't
+ # work, we need the build to noisily fail!
+ oldpath = subprocess.check_output(
+ ['objdump', '-macho', '-dylib-id', '-non-verbose',
+ os.path.join(relpkgdir, "BugsplatMac.framework", "BugsplatMac")]
+ ).splitlines()[-1] # take the last line of output
+ self.run_command(
+ ['install_name_tool', '-change', oldpath,
+ '@executable_path/../Frameworks/BugsplatMac.framework/BugsplatMac',
+ executable])
+
+ # NOTE: the -S argument to strip causes it to keep
+ # enough info for annotated backtraces (i.e. function
+ # names in the crash log). 'strip' with no arguments
+ # yields a slightly smaller binary but makes crash
+ # logs mostly useless. This may be desirable for the
+ # final release. Or not.
+ if ("package" in self.args['actions'] or
+ "unpacked" in self.args['actions']):
+ self.run_command(
+ ['strip', '-S', executable])
+
+ with self.prefix(dst="Resources"):
+ # defer cross-platform file copies until we're in the
+ # nested Resources directory
+ super(DarwinManifest, self).construct()
+
+ # need .icns file referenced by Info.plist
with self.prefix(src=self.icon_path(), dst="") :
- self.path(toplevel_icon)
-
- # ------------------- nested launcher_app --------------------
- with self.prefix(dst=os.path.join(launcher_app, "Contents")):
- # Info.plist is just like top-level one...
- Info = plistlib.readPlist(Info_plist)
- # except for these replacements:
- Info["CFBundleExecutable"] = "SL_Launcher"
- Info["CFBundleIconFile"] = launcher_icon
- self.put_in_file(
- plistlib.writePlistToString(Info),
- os.path.basename(Info_plist),
- "Info.plist")
-
- # copy VMP libs to MacOS
- with self.prefix(dst="MacOS"):
- #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=""):
- 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=""):
- # llbase provides our llrest service layer and llsd decoding
- with self.prefix("llbase"):
- # (Why is llbase treated specially here? What
- # DON'T we want to copy out of lib/python/llbase?)
- self.path("*.py")
- self.path("_cllsd.so")
- #requests module needed by llbase/llrest.py
- #this is only needed on POSIX, because in Windows
- #we compile it into the EXE
- for pypkg in "chardet", "idna", "requests", "urllib3":
- self.path(pypkg)
-
- # launcher_app/Contents/Resources
- with self.prefix(dst="Resources"):
- with self.prefix(src=self.icon_path(), dst="") :
- self.path(launcher_icon)
- with self.prefix(dst="vmp_icons"):
- self.path("secondlife.ico")
- #VMP Tkinter icons
- with self.prefix("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)
-
- with self.prefix(dst="MacOS"):
- # CMake constructs the Second Life executable in the
- # MacOS directory belonging to the top-level Second
- # Life.app. Move it here.
- here = self.get_dst_prefix()
- relbase = os.path.realpath(os.path.dirname(Info_plist))
- self.cmakedirs(here)
- for f in os.listdir(toplevel_MacOS):
- if f == os.path.basename(trampoline):
- # don't move the trampoline script we just made!
- continue
- fromwhere = os.path.join(toplevel_MacOS, f)
- towhere = os.path.join(here, f)
- print "Moving %s => %s" % \
- (self.relpath(fromwhere, relbase),
- self.relpath(towhere, relbase))
- # now do it, only without relativizing paths
- os.rename(fromwhere, towhere)
-
- # NOTE: the -S argument to strip causes it to keep
- # enough info for annotated backtraces (i.e. function
- # names in the crash log). 'strip' with no arguments
- # yields a slightly smaller binary but makes crash
- # logs mostly useless. This may be desirable for the
- # final release. Or not.
- if ("package" in self.args['actions'] or
- "unpacked" in self.args['actions']):
- self.run_command(
- ['strip', '-S', self.dst_path_of('Second Life')])
-
- 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="") :
- self.path(viewer_icon)
-
- with self.prefix(src=relpkgdir, dst=""):
- self.path("libndofdev.dylib")
- self.path("libhunspell-1.3.0.dylib")
-
- with self.prefix("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=""):
- self.path("ca-bundle.crt")
-
- self.path("SecondLife.nib")
-
- # Translations
- self.path("English.lproj/language.txt")
- self.replace_in(src="English.lproj/InfoPlist.strings",
- dst="English.lproj/InfoPlist.strings",
- searchdict={'%%VERSION%%':'.'.join(self.args['version'])}
- )
- self.path("German.lproj")
- self.path("Japanese.lproj")
- self.path("Korean.lproj")
- self.path("da.lproj")
- self.path("es.lproj")
- self.path("fr.lproj")
- self.path("hu.lproj")
- self.path("it.lproj")
- self.path("nl.lproj")
- self.path("pl.lproj")
- self.path("pt.lproj")
- self.path("ru.lproj")
- self.path("tr.lproj")
- self.path("uk.lproj")
- self.path("zh-Hans.lproj")
-
- def path_optional(src, dst):
- """
- For a number of our self.path() calls, not only do we want
- to deal with the absence of src, we also want to remember
- which were present. Return either an empty list (absent)
- or a list containing dst (present). Concatenate these
- return values to get a list of all libs that are present.
- """
- # This was simple before we started needing to pass
- # wildcards. Fortunately, self.path() ends up appending a
- # (source, dest) pair to self.file_list for every expanded
- # file processed. Remember its size before the call.
- oldlen = len(self.file_list)
- self.path(src, dst)
- # The dest appended to self.file_list has been prepended
- # with self.get_dst_prefix(). Strip it off again.
- added = [os.path.relpath(d, self.get_dst_prefix())
- for s, d in self.file_list[oldlen:]]
- if not added:
- print "Skipping %s" % dst
- return added
-
- # dylibs is a list of all the .dylib files we expect to need
- # in our bundled sub-apps. For each of these we'll create a
- # symlink from sub-app/Contents/Resources to the real .dylib.
- # Need to get the llcommon dll from any of the build directories as well.
- libfile_parent = self.get_dst_prefix()
- libfile = "libllcommon.dylib"
- dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir,
- "llcommon",
- self.args['configuration'],
- libfile),
- os.path.join(relpkgdir, libfile)),
- dst=libfile)
-
- for libfile in (
- "libapr-1.0.dylib",
- "libaprutil-1.0.dylib",
- "libcollada14dom.dylib",
- "libexpat.1.dylib",
- "libexception_handler.dylib",
- "libGLOD.dylib",
- # libnghttp2.dylib is a symlink to
- # libnghttp2.major.dylib, which is a symlink to
- # libnghttp2.version.dylib. Get all of them.
- "libnghttp2.*dylib",
- ):
- dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
-
- # SLVoice and vivox lols, no symlinks needed
- for libfile in (
- 'libortp.dylib',
- 'libsndfile.dylib',
- 'libvivoxoal.dylib',
- 'libvivoxsdk.dylib',
- 'libvivoxplatform.dylib',
- 'SLVoice',
- ):
- self.path2basename(relpkgdir, libfile)
-
- # dylibs that vary based on configuration
- if self.args['configuration'].lower() == 'debug':
- for libfile in (
- "libfmodexL.dylib",
- ):
- dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile)
- else:
- for libfile in (
- "libfmodex.dylib",
- ):
- dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
-
- # our apps
- executable_path = {}
- for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"),
- # plugin launcher
- (os.path.join("llplugin", "slplugin"), "SLPlugin.app"),
- ):
- self.path2basename(os.path.join(os.pardir,
- app_bld_dir, self.args['configuration']),
- app)
- executable_path[app] = \
- self.dst_path_of(os.path.join(app, "Contents", "MacOS"))
-
- # our apps dependencies on shared libs
- # for each app, for each dylib we collected in dylibs,
- # create a symlink to the real copy of the dylib.
- with self.prefix(dst=os.path.join(app, "Contents", "Resources")):
- for libfile in dylibs:
- self.relsymlinkf(os.path.join(libfile_parent, libfile))
-
- # Dullahan helper apps go inside SLPlugin.app
- with self.prefix(dst=os.path.join(
- "SLPlugin.app", "Contents", "Frameworks")):
-
- frameworkname = 'Chromium Embedded Framework'
-
- # This code constructs a relative symlink from the
- # target framework folder back to the real CEF framework.
- # It needs to be relative so that the symlink still works when
- # (as is normal) the user moves the app bundle out of the DMG
- # and into the /Applications folder. Note we pass catch=False,
- # letting the uncaught exception terminate the process, since
- # without this symlink, Second Life web media can't possibly work.
-
- # It might seem simpler just to symlink Frameworks back to
- # the parent of Chromimum Embedded Framework.framework. But
- # that would create a symlink cycle, which breaks our
- # packaging step. So make a symlink from Chromium Embedded
- # Framework.framework to the directory of the same name, which
- # is NOT an ancestor of the symlink.
-
- # from SLPlugin.app/Contents/Frameworks/Chromium Embedded
- # Framework.framework back to
- # $viewer_app/Contents/Frameworks/Chromium Embedded Framework.framework
- SLPlugin_framework = self.relsymlinkf(CEF_framework, catch=False)
-
- # copy DullahanHelper.app
- self.path2basename(relpkgdir, 'DullahanHelper.app')
-
- # and fix that up with a Frameworks/CEF symlink too
- with self.prefix(dst=os.path.join(
- 'DullahanHelper.app', 'Contents', 'Frameworks')):
- # from Dullahan Helper.app/Contents/Frameworks/Chromium Embedded
- # Framework.framework back to
- # SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework
- # Since SLPlugin_framework is itself a
- # symlink, don't let relsymlinkf() resolve --
- # explicitly call relpath(symlink=True) and
- # create that symlink here.
- DullahanHelper_framework = \
- self.symlinkf(self.relpath(SLPlugin_framework, symlink=True),
- catch=False)
-
- # change_command includes install_name_tool, the
- # -change subcommand and the old framework rpath
- # stamped into the executable. To use it with
- # run_command(), we must still append the new
- # framework path and the pathname of the
- # executable to change.
- change_command = [
- 'install_name_tool', '-change',
- '@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework']
-
- with self.prefix(dst=os.path.join(
- 'DullahanHelper.app', 'Contents', 'MacOS')):
- # Now self.get_dst_prefix() is, at runtime,
- # @executable_path. Locate the helper app
- # framework (which is a symlink) from here.
- newpath = os.path.join(
- '@executable_path',
- self.relpath(DullahanHelper_framework, symlink=True),
- frameworkname)
- # and restamp the DullahanHelper executable
- self.run_command(
- change_command +
- [newpath, self.dst_path_of('DullahanHelper')])
-
- # SLPlugin plugins
- with self.prefix(dst="llplugin"):
- dylibexecutable = 'media_plugin_cef.dylib'
- self.path2basename("../media_plugins/cef/" + self.args['configuration'],
- dylibexecutable)
-
- # Do this install_name_tool *after* media plugin is copied over.
- # Locate the framework lib executable -- relative to
- # SLPlugin.app/Contents/MacOS, which will be our
- # @executable_path at runtime!
- newpath = os.path.join(
- '@executable_path',
- self.relpath(SLPlugin_framework, executable_path["SLPlugin.app"],
- symlink=True),
- frameworkname)
- # restamp media_plugin_cef.dylib
- self.run_command(
- change_command +
- [newpath, self.dst_path_of(dylibexecutable)])
+ self.path("secondlife.icns")
+
+ # Copy in the updater script and helper modules
+ self.path(src=os.path.join(pkgdir, 'VMP'), dst="updater")
+
+ with self.prefix(src="", dst=os.path.join("updater", "icons")):
+ self.path2basename(self.icon_path(), "secondlife.ico")
+ with self.prefix(src="vmp_icons", dst=""):
+ self.path("*.png")
+ self.path("*.gif")
- # copy LibVLC plugin itself
- self.path2basename("../media_plugins/libvlc/" + self.args['configuration'],
- "media_plugin_libvlc.dylib")
+ with self.prefix(src=relpkgdir, dst=""):
+ self.path("libndofdev.dylib")
+ self.path("libhunspell-1.3.0.dylib")
- # copy LibVLC dynamic libraries
- with self.prefix(src=relpkgdir, dst="lib"):
- self.path( "libvlc*.dylib*" )
- # copy LibVLC plugins folder
- with self.prefix(src='plugins', dst=""):
- self.path( "*.dylib" )
- self.path( "plugins.dat" )
+ 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=""):
+ self.path("ca-bundle.crt")
+
+ # Translations
+ self.path("English.lproj/language.txt")
+ self.replace_in(src="English.lproj/InfoPlist.strings",
+ dst="English.lproj/InfoPlist.strings",
+ searchdict={'%%VERSION%%':'.'.join(self.args['version'])}
+ )
+ self.path("German.lproj")
+ self.path("Japanese.lproj")
+ self.path("Korean.lproj")
+ self.path("da.lproj")
+ self.path("es.lproj")
+ self.path("fr.lproj")
+ self.path("hu.lproj")
+ self.path("it.lproj")
+ self.path("nl.lproj")
+ self.path("pl.lproj")
+ self.path("pt.lproj")
+ self.path("ru.lproj")
+ self.path("tr.lproj")
+ self.path("uk.lproj")
+ self.path("zh-Hans.lproj")
+
+ def path_optional(src, dst):
+ """
+ For a number of our self.path() calls, not only do we want
+ to deal with the absence of src, we also want to remember
+ which were present. Return either an empty list (absent)
+ or a list containing dst (present). Concatenate these
+ return values to get a list of all libs that are present.
+ """
+ # This was simple before we started needing to pass
+ # wildcards. Fortunately, self.path() ends up appending a
+ # (source, dest) pair to self.file_list for every expanded
+ # file processed. Remember its size before the call.
+ oldlen = len(self.file_list)
+ self.path(src, dst)
+ # The dest appended to self.file_list has been prepended
+ # with self.get_dst_prefix(). Strip it off again.
+ added = [os.path.relpath(d, self.get_dst_prefix())
+ for s, d in self.file_list[oldlen:]]
+ if not added:
+ print "Skipping %s" % dst
+ return added
+
+ # dylibs is a list of all the .dylib files we expect to need
+ # in our bundled sub-apps. For each of these we'll create a
+ # symlink from sub-app/Contents/Resources to the real .dylib.
+ # Need to get the llcommon dll from any of the build directories as well.
+ libfile_parent = self.get_dst_prefix()
+ libfile = "libllcommon.dylib"
+ dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir,
+ "llcommon",
+ self.args['configuration'],
+ libfile),
+ os.path.join(relpkgdir, libfile)),
+ dst=libfile)
+
+ for libfile in (
+ "libapr-1.0.dylib",
+ "libaprutil-1.0.dylib",
+ "libcollada14dom.dylib",
+ "libexpat.1.dylib",
+ "libexception_handler.dylib",
+ "libGLOD.dylib",
+ # libnghttp2.dylib is a symlink to
+ # libnghttp2.major.dylib, which is a symlink to
+ # libnghttp2.version.dylib. Get all of them.
+ "libnghttp2.*dylib",
+ ):
+ dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+
+ # SLVoice and vivox lols, no symlinks needed
+ for libfile in (
+ 'libortp.dylib',
+ 'libsndfile.dylib',
+ 'libvivoxoal.dylib',
+ 'libvivoxsdk.dylib',
+ 'libvivoxplatform.dylib',
+ 'SLVoice',
+ ):
+ self.path2basename(relpkgdir, libfile)
+
+ # dylibs that vary based on configuration
+ if self.args['configuration'].lower() == 'debug':
+ for libfile in (
+ "libfmodexL.dylib",
+ ):
+ dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile)
+ else:
+ for libfile in (
+ "libfmodex.dylib",
+ ):
+ dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+
+ # our apps
+ executable_path = {}
+ for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"),
+ # plugin launcher
+ (os.path.join("llplugin", "slplugin"), "SLPlugin.app"),
+ ):
+ self.path2basename(os.path.join(os.pardir,
+ app_bld_dir, self.args['configuration']),
+ app)
+ executable_path[app] = \
+ self.dst_path_of(os.path.join(app, "Contents", "MacOS"))
+
+ # our apps dependencies on shared libs
+ # for each app, for each dylib we collected in dylibs,
+ # create a symlink to the real copy of the dylib.
+ with self.prefix(dst=os.path.join(app, "Contents", "Resources")):
+ for libfile in dylibs:
+ self.relsymlinkf(os.path.join(libfile_parent, libfile))
+
+ # Dullahan helper apps go inside SLPlugin.app
+ with self.prefix(dst=os.path.join(
+ "SLPlugin.app", "Contents", "Frameworks")):
+
+ frameworkname = 'Chromium Embedded Framework'
+
+ # This code constructs a relative symlink from the
+ # target framework folder back to the real CEF framework.
+ # It needs to be relative so that the symlink still works when
+ # (as is normal) the user moves the app bundle out of the DMG
+ # and into the /Applications folder. Note we pass catch=False,
+ # letting the uncaught exception terminate the process, since
+ # without this symlink, Second Life web media can't possibly work.
+
+ # It might seem simpler just to symlink Frameworks back to
+ # the parent of Chromimum Embedded Framework.framework. But
+ # that would create a symlink cycle, which breaks our
+ # packaging step. So make a symlink from Chromium Embedded
+ # Framework.framework to the directory of the same name, which
+ # is NOT an ancestor of the symlink.
+
+ # from SLPlugin.app/Contents/Frameworks/Chromium Embedded
+ # Framework.framework back to
+ # $viewer_app/Contents/Frameworks/Chromium Embedded Framework.framework
+ SLPlugin_framework = self.relsymlinkf(CEF_framework, catch=False)
+
+ # copy DullahanHelper.app
+ self.path2basename(relpkgdir, 'DullahanHelper.app')
+
+ # and fix that up with a Frameworks/CEF symlink too
+ with self.prefix(dst=os.path.join(
+ 'DullahanHelper.app', 'Contents', 'Frameworks')):
+ # from Dullahan Helper.app/Contents/Frameworks/Chromium Embedded
+ # Framework.framework back to
+ # SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework
+ # Since SLPlugin_framework is itself a
+ # symlink, don't let relsymlinkf() resolve --
+ # explicitly call relpath(symlink=True) and
+ # create that symlink here.
+ DullahanHelper_framework = \
+ self.symlinkf(self.relpath(SLPlugin_framework, symlink=True),
+ catch=False)
+
+ # change_command includes install_name_tool, the
+ # -change subcommand and the old framework rpath
+ # stamped into the executable. To use it with
+ # run_command(), we must still append the new
+ # framework path and the pathname of the
+ # executable to change.
+ change_command = [
+ 'install_name_tool', '-change',
+ '@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework']
+
+ with self.prefix(dst=os.path.join(
+ 'DullahanHelper.app', 'Contents', 'MacOS')):
+ # Now self.get_dst_prefix() is, at runtime,
+ # @executable_path. Locate the helper app
+ # framework (which is a symlink) from here.
+ newpath = os.path.join(
+ '@executable_path',
+ self.relpath(DullahanHelper_framework, symlink=True),
+ frameworkname)
+ # and restamp the DullahanHelper executable
+ self.run_command(
+ change_command +
+ [newpath, self.dst_path_of('DullahanHelper')])
+
+ # SLPlugin plugins
+ with self.prefix(dst="llplugin"):
+ dylibexecutable = 'media_plugin_cef.dylib'
+ self.path2basename("../media_plugins/cef/" + self.args['configuration'],
+ dylibexecutable)
+
+ # Do this install_name_tool *after* media plugin is copied over.
+ # Locate the framework lib executable -- relative to
+ # SLPlugin.app/Contents/MacOS, which will be our
+ # @executable_path at runtime!
+ newpath = os.path.join(
+ '@executable_path',
+ self.relpath(SLPlugin_framework, executable_path["SLPlugin.app"],
+ symlink=True),
+ frameworkname)
+ # restamp media_plugin_cef.dylib
+ self.run_command(
+ change_command +
+ [newpath, self.dst_path_of(dylibexecutable)])
+
+ # copy LibVLC plugin itself
+ self.path2basename("../media_plugins/libvlc/" + self.args['configuration'],
+ "media_plugin_libvlc.dylib")
+
+ # copy LibVLC dynamic libraries
+ with self.prefix(src=relpkgdir, dst="lib"):
+ self.path( "libvlc*.dylib*" )
+ # copy LibVLC plugins folder
+ with self.prefix(src='plugins', dst=""):
+ self.path( "*.dylib" )
+ self.path( "plugins.dat" )
def package_finish(self):
global CHANNEL_VENDOR_BASE
@@ -1416,10 +1361,7 @@ open "%s" --args "$@"
else:
print >> sys.stderr, "Maximum codesign attempts exceeded; giving up"
raise
- self.run_command(['spctl', '-a', '-texec', '-vv', app_in_dmg])
-
- imagename="SecondLife_" + '_'.join(self.args['version'])
-
+ self.run_command(['spctl', '-a', '-texec', '-vvvv', app_in_dmg])
finally:
# Unmount the image even if exceptions from any of the above
@@ -1458,29 +1400,25 @@ 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=""):
- self.path("SL_Launcher")
self.path("*.py")
- with self.prefix(src=os.path.join("lib", "python", "llbase"), dst="llbase"):
- self.path("*.py")
- self.path("_cllsd.so")
# recurses, packaged again
self.path("res-sdl")
@@ -1488,9 +1426,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 +1450,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):
@@ -1555,7 +1493,7 @@ class LinuxManifest(ViewerManifest):
self.run_command(
["find"] +
[os.path.join(self.get_dst_prefix(), dir) for dir in ('bin', 'lib')] +
- ['-type', 'f', '!', '-name', '*.py', '!', '-name', 'SL_Launcher',
+ ['-type', 'f', '!', '-name', '*.py',
'!', '-name', 'update_install', '-exec', 'strip', '-S', '{}', ';'])
class Linux_i686_Manifest(LinuxManifest):
@@ -1568,7 +1506,7 @@ class Linux_i686_Manifest(LinuxManifest):
relpkgdir = os.path.join(pkgdir, "lib", "release")
debpkgdir = os.path.join(pkgdir, "lib", "debug")
- with self.prefix(relpkgdir, dst="lib"):
+ with self.prefix(src=relpkgdir, dst="lib"):
self.path("libapr-1.so")
self.path("libapr-1.so.0")
self.path("libapr-1.so.0.4.5")
@@ -1654,4 +1592,8 @@ class Linux_x86_64_Manifest(LinuxManifest):
################################################################
if __name__ == "__main__":
- main()
+ extra_arguments = [
+ dict(name='bugsplat', description="""BugSplat database to which to post crashes,
+ if BugSplat crash reporting is desired""", default=''),
+ ]
+ main(extra=extra_arguments)