diff options
| author | Graham Linden <graham@lindenlab.com> | 2019-02-28 18:28:13 -0800 | 
|---|---|---|
| committer | Graham Linden <graham@lindenlab.com> | 2019-02-28 18:28:13 -0800 | 
| commit | bf8dee73f90f74f3eb9fd544dc9673defa52817f (patch) | |
| tree | 3e8a711051c699fe96afcfead0e3fd4dc4f19db3 /indra/newview | |
| parent | c1d2416826406631807f153e7de9d2b790b0caa5 (diff) | |
| parent | e1a76999c3e8aad337d765fcd6eb50c8191409ea (diff) | |
Merge 6.1.1
Diffstat (limited to 'indra/newview')
35 files changed, 1543 insertions, 827 deletions
| diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9d4b58d216..9933713911 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} @@ -1357,6 +1367,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) @@ -1371,6 +1389,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 @@ -1396,6 +1420,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") @@ -1412,6 +1441,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 @@ -1548,7 +1582,6 @@ if (WINDOWS)          kernel32          odbc32          odbccp32 -        ole32          oleaut32          shell32          Vfw32 @@ -1694,6 +1727,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) @@ -1792,7 +1830,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} @@ -1801,15 +1839,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 @@ -1827,24 +1866,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 @@ -1867,15 +1891,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 @@ -1906,8 +1931,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 @@ -1982,6 +2007,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.") @@ -2005,15 +2036,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} @@ -2027,17 +2059,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} @@ -2055,37 +2088,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. 2007")    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( @@ -2096,15 +2138,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 @@ -2129,15 +2172,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 @@ -2149,67 +2193,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 bb5ff19176..3e8a854df3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4415,6 +4415,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> @@ -13646,7 +13657,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> @@ -14127,17 +14138,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> @@ -16363,7 +16363,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 14c8dba39f..4f9a1b7804 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"
 @@ -660,8 +729,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"
 @@ -673,7 +742,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
 @@ -692,11 +767,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
 @@ -733,10 +821,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
 @@ -755,7 +843,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.nsiBinary files differ index 83e1a3ea94..f462c82078 100644 --- a/indra/newview/installers/windows/lang_da.nsi +++ b/indra/newview/installers/windows/lang_da.nsi diff --git a/indra/newview/installers/windows/lang_de.nsi b/indra/newview/installers/windows/lang_de.nsiBinary files differ index 2a868acc89..8bb20476b3 100644..100755 --- a/indra/newview/installers/windows/lang_de.nsi +++ b/indra/newview/installers/windows/lang_de.nsi diff --git a/indra/newview/installers/windows/lang_en-us.nsi b/indra/newview/installers/windows/lang_en-us.nsiBinary files differ index 00aa47de69..fd4d340816 100644 --- a/indra/newview/installers/windows/lang_en-us.nsi +++ b/indra/newview/installers/windows/lang_en-us.nsi diff --git a/indra/newview/installers/windows/lang_es.nsi b/indra/newview/installers/windows/lang_es.nsiBinary files differ index 1ecf254ffb..8a81110069 100644..100755 --- a/indra/newview/installers/windows/lang_es.nsi +++ b/indra/newview/installers/windows/lang_es.nsi diff --git a/indra/newview/installers/windows/lang_fr.nsi b/indra/newview/installers/windows/lang_fr.nsiBinary files differ index bec5835bed..f038c0e419 100644..100755 --- a/indra/newview/installers/windows/lang_fr.nsi +++ b/indra/newview/installers/windows/lang_fr.nsi diff --git a/indra/newview/installers/windows/lang_it.nsi b/indra/newview/installers/windows/lang_it.nsiBinary files differ index 1d2e150525..bd16d8318f 100644..100755 --- a/indra/newview/installers/windows/lang_it.nsi +++ b/indra/newview/installers/windows/lang_it.nsi diff --git a/indra/newview/installers/windows/lang_ja.nsi b/indra/newview/installers/windows/lang_ja.nsiBinary files differ index 1bd6526670..71edde1992 100644..100755 --- a/indra/newview/installers/windows/lang_ja.nsi +++ b/indra/newview/installers/windows/lang_ja.nsi diff --git a/indra/newview/installers/windows/lang_pl.nsi b/indra/newview/installers/windows/lang_pl.nsiBinary files differ index a172f0cdeb..05977847b9 100644 --- a/indra/newview/installers/windows/lang_pl.nsi +++ b/indra/newview/installers/windows/lang_pl.nsi diff --git a/indra/newview/installers/windows/lang_pt-br.nsi b/indra/newview/installers/windows/lang_pt-br.nsiBinary files differ index 87032fec18..0e7cbeacda 100644..100755 --- a/indra/newview/installers/windows/lang_pt-br.nsi +++ b/indra/newview/installers/windows/lang_pt-br.nsi diff --git a/indra/newview/installers/windows/lang_ru.nsi b/indra/newview/installers/windows/lang_ru.nsiBinary files differ index 019c66123c..d55aacc971 100644..100755 --- a/indra/newview/installers/windows/lang_ru.nsi +++ b/indra/newview/installers/windows/lang_ru.nsi diff --git a/indra/newview/installers/windows/lang_tr.nsi b/indra/newview/installers/windows/lang_tr.nsiBinary files differ index 1c4e2c2f48..4746f84482 100644..100755 --- a/indra/newview/installers/windows/lang_tr.nsi +++ b/indra/newview/installers/windows/lang_tr.nsi diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsiBinary files differ index 355e01a333..397bd0ac81 100644..100755 --- a/indra/newview/installers/windows/lang_zh.nsi +++ b/indra/newview/installers/windows/lang_zh.nsi diff --git a/indra/newview/installers/windows/language_menu.nsi b/indra/newview/installers/windows/language_menu.nsiBinary files differ index 08ad42532f..2f426a0f47 100644 --- a/indra/newview/installers/windows/language_menu.nsi +++ b/indra/newview/installers/windows/language_menu.nsi 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 cac951a3c5..07f85a2dd5 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -706,6 +706,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() @@ -782,13 +798,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 @@ -877,11 +886,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 ); @@ -1084,26 +1088,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())  	{ @@ -1151,6 +1135,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")); @@ -1696,7 +1708,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; @@ -2175,6 +2187,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);  } @@ -3042,14 +3060,11 @@ void LLAppViewer::writeDebugInfo(bool isStatic)          ? getStaticDebugFile()          : getDynamicDebugFile() ); -	LL_INFOS() << "Opening debug file " << *debug_filename << LL_ENDL; -	llofstream out_file(debug_filename->c_str()); +    LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL; +    llofstream out_file(debug_filename->c_str());      isStatic ?  LLSDSerialize::toPrettyXML(gDebugInfo, out_file)               :  LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file); - - -	out_file.close();  }  LLSD LLAppViewer::getViewerInfo() const diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h new file mode 100644 index 0000000000..37e8a3917a --- /dev/null +++ b/indra/newview/llappviewermacosx-for-objc.h @@ -0,0 +1,53 @@ +/** + * @file   llappviewermacosx-for-objc.h + * @author Nat Goodspeed + * @date   2018-06-15 + * @brief  llappviewermacosx.h publishes the C++ API for + *         llappviewermacosx.cpp, just as + *         llappviewermacosx-objc.h publishes the Objective-C++ API for + *         llappviewermacosx-objc.mm. + * + *         This header is intended to publish for Objective-C++ consumers a + *         subset of the C++ API presented by llappviewermacosx.cpp. It's a + *         subset because, if an Objective-C++ consumer were to #include + *         the full llappviewermacosx.h, we would almost surely run into + *         trouble due to the discrepancy between Objective-C++'s BOOL versus + *         classic Microsoft/Linden BOOL. + *  + * $LicenseInfo:firstyear=2018&license=viewerlgpl$ + * Copyright (c) 2018, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) +#define LL_LLAPPVIEWERMACOSX_FOR_OBJC_H + +#include <string> + +void constructViewer(); +bool initViewer(); +void handleUrl(const char* url_utf8); +bool pumpMainLoop(); +void handleQuit(); +void cleanupViewer(); +void infos(const std::string& message); + +// This struct is malleable; it only serves as a way to convey a number of +// fields from llappviewermacosx.cpp's CrashMetadata_instance() function to the +// consuming functions in llappdelegate-objc.mm. As long as both those sources +// are compiled with this same header, the content and order of CrashMetadata +// can change as needed. +struct CrashMetadata +{ +    std::string logFilePathname; +    std::string userSettingsPathname; +    std::string staticDebugPathname; +    std::string OSInfo; +    std::string agentFullname; +    std::string regionName; +    std::string fatalMessage; +}; + +CrashMetadata& CrashMetadata_instance(); + +#endif /* ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) */ diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index d472f8926b..81f04744f8 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -36,20 +36,25 @@  #include "llappviewermacosx-objc.h"  #include "llappviewermacosx.h" +#include "llappviewermacosx-for-objc.h"  #include "llwindowmacosx-objc.h"  #include "llcommandlineparser.h" +#include "llsdserialize.h"  #include "llviewernetwork.h"  #include "llviewercontrol.h"  #include "llmd5.h"  #include "llfloaterworldmap.h"  #include "llurldispatcher.h" +#include "llerrorcontrol.h" +#include "llvoavatarself.h"         // for gAgentAvatarp->getFullname()  #include <ApplicationServices/ApplicationServices.h>  #ifdef LL_CARBON_CRASH_HANDLER  #include <Carbon/Carbon.h>  #endif  #include <vector>  #include <exception> +#include <fstream>  #include "lldir.h"  #include <signal.h> @@ -81,7 +86,7 @@ static void exceptionTerminateHandler()  	gOldTerminateHandler(); // call old terminate() handler  } -bool initViewer() +void constructViewer()  {  	// Set the working dir to <bundle>/Contents/Resources  	if (chdir(gDirUtilp->getAppRODataDir().c_str()) == -1) @@ -97,18 +102,20 @@ bool initViewer()  	gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);  	gViewerAppPtr->setErrorHandler(LLAppViewer::handleViewerCrash); +} -	 +bool initViewer() +{  	bool ok = gViewerAppPtr->init();  	if(!ok)  	{  		LL_WARNS() << "Application init failed." << LL_ENDL;  	} -    else if (!gHandleSLURL.empty()) -    { -        dispatchUrl(gHandleSLURL); -        gHandleSLURL = ""; -    } +	else if (!gHandleSLURL.empty()) +	{ +		dispatchUrl(gHandleSLURL); +		gHandleSLURL = ""; +	}  	return ok;  } @@ -147,6 +154,73 @@ void cleanupViewer()  	gViewerAppPtr = NULL;  } +// The BugsplatMac API is structured as a number of different method +// overrides, each returning a different piece of metadata. But since we +// obtain such metadata by opening and parsing a file, it seems ridiculous to +// reopen and reparse it for every individual string desired. What we want is +// to open and parse the file once, retaining the data for subsequent +// requests. That's why this is an LLSingleton. +// Another approach would be to provide a function that simply returns +// CrashMetadata, storing the struct in LLAppDelegate, but nat doesn't know +// enough Objective-C++ to code that. We'd still have to detect which of the +// method overrides is called first so that the results are order-insensitive. +class CrashMetadataSingleton: public CrashMetadata, public LLSingleton<CrashMetadataSingleton> +{ +    LLSINGLETON(CrashMetadataSingleton); + +    // convenience method to log each metadata field retrieved by constructor +    std::string get_metadata(const LLSD& info, const LLSD::String& key) const +    { +        std::string data(info[key].asString()); +        LL_INFOS() << "  " << key << "='" << data << "'" << LL_ENDL; +        return data; +    } +}; + +// Populate the fields of our public base-class struct. +CrashMetadataSingleton::CrashMetadataSingleton() +{ +    // Note: we depend on being able to read the static_debug_info.log file +    // from the *previous* run before we overwrite it with the new one for +    // *this* run. LLAppViewer initialization must happen in the Right Order. +    staticDebugPathname = *gViewerAppPtr->getStaticDebugFile(); +    std::ifstream static_file(staticDebugPathname); +    LLSD info; +    if (! static_file.is_open()) +    { +        LL_INFOS() << "Can't open '" << staticDebugPathname +                   << "'; no metadata about previous run" << LL_ENDL; +    } +    else if (! LLSDSerialize::deserialize(info, static_file, LLSDSerialize::SIZE_UNLIMITED)) +    { +        LL_INFOS() << "Can't parse '" << staticDebugPathname +                   << "'; no metadata about previous run" << LL_ENDL; +    } +    else +    { +        LL_INFOS() << "Metadata from '" << staticDebugPathname << "':" << LL_ENDL; +        logFilePathname      = get_metadata(info, "SLLog"); +        userSettingsPathname = get_metadata(info, "SettingsFilename"); +        OSInfo               = get_metadata(info, "OSInfo"); +        agentFullname        = get_metadata(info, "LoginName"); +        // Translate underscores back to spaces +        LLStringUtil::replaceChar(agentFullname, '_', ' '); +        regionName           = get_metadata(info, "CurrentRegion"); +        fatalMessage         = get_metadata(info, "FatalMessage"); +    } +} + +// Avoid having to compile all of our LLSingleton machinery in Objective-C++. +CrashMetadata& CrashMetadata_instance() +{ +    return CrashMetadataSingleton::instance(); +} + +void infos(const std::string& message) +{ +    LL_INFOS() << message << LL_ENDL; +} +  int main( int argc, char **argv )   {  	// Store off the command line args for use later. diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 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 bc93fa2c20..126c6be388 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -253,14 +253,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); @@ -276,6 +274,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 @@ -328,17 +334,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" @@ -361,6 +394,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(); @@ -420,7 +466,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 1d24df5886..c5da7dc1b0 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -479,8 +479,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 5844dea1be..1c4f7c7b4d 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" @@ -106,6 +107,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 @@ -253,6 +266,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()) @@ -376,6 +392,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 56111e9495..11cf303753 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 5ed3bd5df5..8d6a787133 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) | 
