diff options
Diffstat (limited to 'indra')
24 files changed, 660 insertions, 580 deletions
diff --git a/indra/cmake/LLBase.cmake b/indra/cmake/LLBase.cmake deleted file mode 100644 index 76e3c688a3..0000000000 --- a/indra/cmake/LLBase.cmake +++ /dev/null @@ -1,4 +0,0 @@ -# -*- cmake -*- -include(Prebuilt) - -use_prebuilt_binary(llbase) diff --git a/indra/cmake/Requests.cmake b/indra/cmake/Requests.cmake deleted file mode 100644 index b9c729d697..0000000000 --- a/indra/cmake/Requests.cmake +++ /dev/null @@ -1,7 +0,0 @@ -if (DARWIN) -   include (Prebuilt) -   use_prebuilt_binary(requests) -   use_prebuilt_binary(urllib3) -   use_prebuilt_binary(chardet) -   use_prebuilt_binary(idna) -endif (DARWIN) diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 8fb27af6a4..c551413811 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -170,6 +170,26 @@ public:      static bool get_consuming();      /** +     * RAII control of the consuming flag +     */ +    class OverrideConsuming +    { +    public: +        OverrideConsuming(bool consuming): +            mPrevConsuming(get_consuming()) +        { +            set_consuming(consuming); +        } +        ~OverrideConsuming() +        { +            set_consuming(mPrevConsuming); +        } + +    private: +        bool mPrevConsuming; +    }; + +    /**       * Please do NOT directly use boost::dcoroutines::future! It is essential       * to maintain the "current" coroutine at every context switch. This       * Future wraps the essential boost::dcoroutines::future functionality diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index dce97b5411..eedd8c92b5 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -545,10 +545,8 @@ bool LLEventStream::post(const LLSD& event)   *****************************************************************************/  bool LLEventMailDrop::post(const LLSD& event)  { -    bool posted = false; -     -    if (!mSignal->empty()) -        posted = LLEventStream::post(event); +    // forward the call to our base class +    bool posted = LLEventStream::post(event);      if (!posted)      {   // if the event was not handled we will save it for later so that it can  @@ -564,16 +562,25 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,                                      const NameList& after,                                      const NameList& before)  { -    if (!mEventHistory.empty()) +    // Before actually connecting this listener for subsequent post() calls, +    // first feed each of the saved events, in order, to the new listener. +    // Remove any that this listener consumes -- Effective STL, Item 9. +    for (auto hi(mEventHistory.begin()), hend(mEventHistory.end()); hi != hend; )      { -        if (listener(mEventHistory.front())) +        if (listener(*hi))          { -            mEventHistory.pop_front(); +            // new listener consumed this event, erase it +            hi = mEventHistory.erase(hi); +        } +        else +        { +            // listener did not consume this event, just move along +            ++hi;          }      } +    // let base class perform the actual connection      return LLEventStream::listen_impl(name, listener, after, before); -  } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 1d51c660ed..5d60c63810 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -650,15 +650,21 @@ public:   *   LLEventMailDrop   *****************************************************************************/  /** - * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally,  - * however if no listeners return that they have handled the event it is placed in  - * a queue. Subsequent attaching listeners will receive stored events from the queue  - * until a listener indicates that the event has been handled.  In order to receive  - * multiple events from a mail drop the listener must disconnect and reconnect. + * LLEventMailDrop is a specialization of LLEventStream. Events are posted + * normally, however if no listener returns that it has handled the event + * (returns true), it is placed in a queue. Subsequent attaching listeners + * will receive stored events from the queue until some listener indicates + * that the event has been handled. + * + * LLEventMailDrop completely decouples the timing of post() calls from + * listen() calls: every event posted to an LLEventMailDrop is eventually seen + * by all listeners, until some listener consumes it. The caveat is that each + * event *must* eventually reach a listener that will consume it, else the + * queue will grow to arbitrary length.   *    * @NOTE: When using an LLEventMailDrop (or LLEventQueue) with a LLEventTimeout or - * LLEventFilter attaching the filter downstream using Timeout's constructor will - * cause the MailDrop to discharge any of it's stored events. The timeout should  + * LLEventFilter attaching the filter downstream, using Timeout's constructor will + * cause the MailDrop to discharge any of its stored events. The timeout should    * instead be connected upstream using its listen() method.     * See llcoro::suspendUntilEventOnWithTimeout() for an example.   */ diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index f1f4226c40..7f5b9b4ac2 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -2115,6 +2115,9 @@ namespace LLInitParam  			typedef typename super_t::iterator										iterator;  			typedef typename super_t::const_iterator								const_iterator; +			using super_t::operator(); +			using super_t::operator const container_t&; +  			explicit Multiple(const char* name = "")  			:	super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount)  			{} diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index c87d2a3e58..cf8f8cc6a5 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -47,9 +47,9 @@ class LLLeapImpl: public LLLeap      LOG_CLASS(LLLeap);  public:      // Called only by LLLeap::create() -    LLLeapImpl(const std::string& desc, const std::vector<std::string>& plugin): +    LLLeapImpl(const LLProcess::Params& cparams):          // We might reassign mDesc in the constructor body if it's empty here. -        mDesc(desc), +        mDesc(cparams.desc),          // We expect multiple LLLeapImpl instances. Definitely tweak          // mDonePump's name for uniqueness.          mDonePump("LLLeap", true), @@ -67,17 +67,17 @@ public:          // this class or method name.          mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))      { -        // Rule out empty vector -        if (plugin.empty()) +        // Rule out unpopulated Params block +        if (! cparams.executable.isProvided())          {              LLTHROW(Error("no plugin command"));          }          // Don't leave desc empty either, but in this case, if we weren't          // given one, we'll fake one. -        if (desc.empty()) +        if (mDesc.empty())          { -            mDesc = LLProcess::basename(plugin[0]); +            mDesc = LLProcess::basename(cparams.executable);              // how about a toLower() variant that returns the transformed string?!              std::string desclower(mDesc);              LLStringUtil::toLower(desclower); @@ -87,9 +87,9 @@ public:              // notice Python specially: we provide Python LLSD serialization              // support, so there's a pretty good reason to implement plugins              // in that language. -            if (plugin.size() >= 2 && (desclower == "python" || desclower == "python.exe")) +            if (cparams.args.size() && (desclower == "python" || desclower == "python.exe"))              { -                mDesc = LLProcess::basename(plugin[1]); +                mDesc = LLProcess::basename(cparams.args()[0]);              }          } @@ -97,14 +97,10 @@ public:          mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));          // Okay, launch child. -        LLProcess::Params params; +        // Get a modifiable copy of params block to set files and postend. +        LLProcess::Params params(cparams); +        // copy our deduced mDesc back into the params block          params.desc = mDesc; -        std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end()); -        params.executable = *pi++; -        for ( ; pi != pend; ++pi) -        { -            params.args.add(*pi); -        }          params.files.add(LLProcess::FileParam("pipe")); // stdin          params.files.add(LLProcess::FileParam("pipe")); // stdout          params.files.add(LLProcess::FileParam("pipe")); // stderr @@ -429,17 +425,17 @@ private:      boost::scoped_ptr<LLLeapListener> mListener;  }; -// This must follow the declaration of LLLeapImpl, so it may as well be last. -LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc) +// These must follow the declaration of LLLeapImpl, so they may as well be last. +LLLeap* LLLeap::create(const LLProcess::Params& params, bool exc)  {      // If caller is willing to permit exceptions, just instantiate.      if (exc) -        return new LLLeapImpl(desc, plugin); +        return new LLLeapImpl(params);      // Caller insists on suppressing LLLeap::Error. Very well, catch it.      try      { -        return new LLLeapImpl(desc, plugin); +        return new LLLeapImpl(params);      }      catch (const LLLeap::Error&)      { @@ -447,6 +443,23 @@ LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>&      }  } +LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc) +{ +    LLProcess::Params params; +    params.desc = desc; +    std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end()); +    // could validate here, but let's rely on LLLeapImpl's constructor +    if (pi != pend) +    { +        params.executable = *pi++; +    } +    for ( ; pi != pend; ++pi) +    { +        params.args.add(*pi); +    } +    return create(params, exc); +} +  LLLeap* LLLeap::create(const std::string& desc, const std::string& plugin, bool exc)  {      // Use LLStringUtil::getTokens() to parse the command line diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h index 8aac8a64c5..7cecdf2f8f 100644 --- a/indra/llcommon/llleap.h +++ b/indra/llcommon/llleap.h @@ -14,6 +14,7 @@  #include "llinstancetracker.h"  #include "llexception.h" +#include "llprocess.h"  #include <string>  #include <vector> @@ -62,6 +63,19 @@ public:                            bool exc=true);      /** +     * Pass an LLProcess::Params instance to specify desc, executable, args et al. +     * +     * Note that files and postend are set implicitly; any values you set in +     * those fields will be disregarded. +     * +     * Pass exc=false to suppress LLLeap::Error exception. Obviously in that +     * case the caller cannot discover the nature of the error, merely that an +     * error of some kind occurred (because create() returned NULL). Either +     * way, the error is logged. +     */ +    static LLLeap* create(const LLProcess::Params& params, bool exc=true); + +    /**       * Exception thrown for invalid create() arguments, e.g. no plugin       * program. This is more resiliant than an LL_ERRS failure, because the       * string(s) passed to create() might come from an external source. This diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp index b6a32a0e78..be26416cbb 100644 --- a/indra/llui/llnotificationslistener.cpp +++ b/indra/llui/llnotificationslistener.cpp @@ -90,9 +90,12 @@ void LLNotificationsListener::requestAdd(const LLSD& event_data) const  {  	if(event_data.has("reply"))  	{ +		LLSD payload(event_data["payload"]); +		// copy reqid, if provided, to link response with request +		payload["reqid"] = event_data["reqid"];  		mNotifications.add(event_data["name"],   						   event_data["substitutions"],  -						   event_data["payload"], +						   payload,  						   boost::bind(&LLNotificationsListener::NotificationResponder,   									   this,   									   event_data["reply"].asString(),  @@ -112,10 +115,12 @@ void LLNotificationsListener::NotificationResponder(const std::string& reply_pum  										const LLSD& notification,   										const LLSD& response) const  { -	LLSD reponse_event; -	reponse_event["notification"] = notification; -	reponse_event["response"] = response; -	LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event); +	LLSD response_event; +	response_event["notification"] = notification; +	response_event["response"] = response; +	// surface reqid at top level of response for request/response protocol +	response_event["reqid"] = notification["payload"]["reqid"]; +	LLEventPumps::getInstance()->obtain(reply_pump).post(response_event);  }  void LLNotificationsListener::listChannels(const LLSD& params) const diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index 79c4362747..87dc1b9795 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -171,9 +171,9 @@ void LLDir_Mac::initAppDirs(const std::string &app_name,  	if (!app_read_only_data_dir.empty())  	{  		mAppRODataDir = app_read_only_data_dir; -		mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; +		mSkinBaseDir = add(mAppRODataDir, "skins");  	} -	mCAFile = getExpandedFilename(LL_PATH_EXECUTABLE, "../Resources", "ca-bundle.crt"); +	mCAFile = add(mAppRODataDir, "ca-bundle.crt");  }  std::string LLDir_Mac::getCurPath() diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 843294c239..d4afbb15df 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1429,12 +1429,10 @@ static CursorRef gCursors[UI_CURSOR_COUNT];  static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)  {  	// cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif -	std::string fullpath = gDirUtilp->getAppRODataDir(); -	fullpath += gDirUtilp->getDirDelimiter(); -	fullpath += "cursors_mac"; -	fullpath += gDirUtilp->getDirDelimiter(); -	fullpath += cursorIDToName(cursorid); -	fullpath += ".tif"; +	std::string fullpath = gDirUtilp->add( +		gDirUtilp->getAppRODataDir(), +		"cursors_mac", +		cursorIDToName(cursorid) + std::string(".tif"));  	gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);  } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e573b927d7..5c918fc3b2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -23,7 +23,6 @@ include(GLOD)  include(Hunspell)  include(JsonCpp)  include(LLAppearance) -include(LLBase)  include(LLAudio)  include(LLCA)  include(LLCharacter) @@ -50,7 +49,6 @@ include(OPENAL)  include(OpenGL)  include(OpenSSL)  include(PNG) -include(Requests)  include(TemplateCheck)  include(UI)  include(UnixInstall) @@ -2094,13 +2092,6 @@ if (DARWIN)    # magically known to CMake -- it's that these names are referenced in the    # Info-SecondLife.plist file in the configure_file() directive below.    set(product "${VIEWER_CHANNEL}") -  # this is the setting for the Python wrapper, see SL-322 and WRAPPER line in Info-SecondLife.plist -  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 "${VIEWER_CHANNEL}")    set(MACOSX_BUNDLE_ICON_FILE "secondlife.icns")    set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.secondlife.indra.viewer") diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 8aabd6818b..9482f07524 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> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 44aa22b387..8722050569 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4316,6 +4316,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> @@ -16161,7 +16172,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..76187b49a5 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -299,7 +299,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,9 +317,9 @@ 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"' ''
 @@ -327,7 +327,7 @@ CreateShortCut "$INSTDIR\Uninstall $INSTSHORTCUT.lnk" \  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\Linden Research, Inc.\$INSTPROG" "Exe" "$VIEWER_EXE"
  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/"
 @@ -338,10 +338,10 @@ WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninst  WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "EstimatedSize" "0x0001D500"		# ~117 MB
  # from FS:Ansariel
 -WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayIcon" '"$INSTDIR\$INSTEXE"'
 +WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "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 HKEY_LOCAL_MACHINE "Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\$VIEWER_EXE" "DisableExceptionChainValidation" 1
  # Write URL registry info
  WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "(default)" "URL:Second Life"
 @@ -358,9 +358,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"
 @@ -398,8 +397,8 @@ Call un.CloseSecondLife  DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG"
  DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG"
  # 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 HKEY_LOCAL_MACHINE "Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\$VIEWER_EXE"
 +##DeleteRegKey HKEY_CLASSES_ROOT "Applications\$INSTEXE"
  DeleteRegKey HKEY_CLASSES_ROOT "Applications\${VIEWER_EXE}"
  # Clean up shortcuts
 @@ -537,6 +536,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"
 @@ -673,7 +673,7 @@ FunctionEnd  ;; After install completes, launch app
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  Function .onInstSuccess
 -Call CheckWindowsServPack		# Warn if not on the latest SP before asking to launch.
 +        Call CheckWindowsServPack		# Warn if not on the latest SP before asking to launch.
          Push $R0
          Push $0
          ;; MAINT-7812: Only write nsis.winstall file with /marker switch
 @@ -694,8 +694,21 @@ Call CheckWindowsServPack		# Warn if not on the latest SP before asking to launc          Pop $R0
          Push $R0					# Option value, unused# 
          StrCmp $SKIP_AUTORUN "true" +2;
 -# Assumes SetOutPath $INSTDIR
 -	Exec '"$WINDIR\explorer.exe" "$INSTDIR\$INSTSHORTCUT.lnk"'
 +        # 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'
          Pop $R0
  # 
  FunctionEnd
 diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index f55304f30b..1d55537427 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -26,6 +26,8 @@  #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> @@ -271,25 +273,59 @@      infos("bugsplatStartupManagerWillSendCrashReport");  } -- (BugsplatAttachment *)attachmentForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { -    std::string logfile = CrashMetadata_instance().logFilePathname; -    // Still to do: -    // userSettingsPathname -    // staticDebugPathname -    // but the BugsplatMac version 1.0.5 BugsplatStartupManagerDelegate API -    // doesn't yet provide a way to attach more than one file. -    NSString *ns_logfile = [NSString stringWithCString:logfile.c_str() -                                              encoding:NSUTF8StringEncoding]; -    NSData *data = [NSData dataWithContentsOfFile:ns_logfile]; - -    // Apologies for the hard-coded log-file basename, but I do not know the -    // incantation for "$(basename "$logfile")" in this language. -    BugsplatAttachment *attachment =  -        [[BugsplatAttachment alloc] initWithFilename:@"SecondLife.log" -                                      attachmentData:data -                                         contentType:@"text/plain"]; -    infos("attachmentForBugsplatStartupManager attaching " + logfile); -    return attachment; +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 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b3f09a73a2..e527b70b0e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1087,26 +1087,6 @@ bool LLAppViewer::init()  		}  	} -// don't nag developers who need to run the executable directly -#if LL_RELEASE_FOR_DOWNLOAD -	// MAINT-8305: If we're processing a SLURL, skip the launcher check. -	if (gSavedSettings.getString("CmdLineLoginLocation").empty()) -	{ -		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())  	{ @@ -1154,6 +1134,36 @@ 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, "updater.exe"); +#elif LL_DARWIN +	// explicitly run the system Python interpreter on updater.py +	updater.executable = "python"; +	updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", "updater.py")); +#else +	updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "updater"); +#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"))); +	// UpdaterServiceURL +	updater.args.add(gSavedSettings.getString("UpdaterServiceURL")); +	// 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")); @@ -3925,12 +3935,6 @@ void LLAppViewer::requestQuit()  		gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.  	} -	// Try to send last batch of avatar rez metrics. -	if (!gDisconnected && isAgentAvatarValid()) -	{ -		gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent. -	} -  	LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);  	effectp->setPositionGlobal(gAgent.getPositionGlobal());  	effectp->setColor(LLColor4U(gAgent.getEffectColor())); 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 cc02642203..b44d996558 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -480,8 +480,7 @@ bool idle_startup()  			if (!found_template)  			{  				message_template_path = -					gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, -												   "../Resources/app_settings", +					gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,  												   "message_template.msg");  				found_template = LLFile::fopen(message_template_path.c_str(), "r");		/* Flawfinder: ignore */  			}		 diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index b039afa734..dd35ed4c71 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/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9eaa5330c3..e2859603e0 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3841,7 +3841,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 @@ -3851,6 +3850,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/viewer_manifest.py b/indra/newview/viewer_manifest.py index 464f7aa3e9..0aea31d563 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -477,7 +477,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()") @@ -497,7 +497,7 @@ class WindowsManifest(ViewerManifest):              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') +                self.path('updater.exe')                  #IUM is not normally executed directly, just imported.  No exe needed.                  self.path("InstallerUserMessage.py") @@ -509,11 +509,6 @@ class WindowsManifest(ViewerManifest):                      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") -          # Plugin host application          self.path2basename(os.path.join(os.pardir,                                          'llplugin', 'slplugin', self.args['configuration']), @@ -788,7 +783,7 @@ class WindowsManifest(ViewerManifest):          substitution_strings['installer_file'] = installer_file          version_vars = """ -        !define INSTEXE "SL_Launcher.exe" +        !define INSTEXE "updater.exe"          !define VERSION "%(version_short)s"          !define VERSION_LONG "%(version)s"          !define VERSION_DASHES "%(version_dashes)s" @@ -832,7 +827,7 @@ 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", +            "updater.exe",              ):              self.sign(exe) @@ -906,424 +901,276 @@ class DarwinManifest(ViewerManifest):          return bool(set(["package", "unpacked"]).intersection(self.args['actions']))      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 = self.channel()+".app"  -        toplevel_icon = "secondlife.icns" -        launcher_app, launcher_icon = "Second Life Launcher.app", "secondlife.icns" -        viewer_app,   viewer_icon   = "Second Life Viewer.app",   "secondlife.icns" - -        # capture the path to the directory containing toplevel_app -        parentdir = os.path.join(self.get_dst_prefix(), os.pardir) -          # copy over the build result (this is a no-op if run within the xcode script) -        self.path(os.path.join(self.args['configuration'], toplevel_app), dst="") +        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(dst="Contents"):  # everything goes in Contents -            # top-level Info.plist is as generated by CMake -            Info_plist = self.dst_path_of("Info.plist") - -            toplevel_MacOS = self.dst_path_of("MacOS") -            # the one file in top-level MacOS directory is the trampoline to -            # our nested launcher_app -            if not self.is_rearranging(): -                trampoline = "" -            else: -                with self.prefix(dst="MacOS"): -                    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="Contents"):  # everything goes in Contents +            # 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) + +            with self.prefix(dst="MacOS"): +                # 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(self.channel())])              with self.prefix(dst="Resources"): -                # top-level Resources directory should be pretty sparse -                # need .icns file referenced by top-level Info.plist -                with self.prefix(src=self.icon_path()) : -                    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")): -                            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")): -                            # llbase provides our llrest service layer and llsd decoding -                            with self.prefix(src="llbase", dst="llbase"): -                                # (Why is llbase treated specially here? What -                                # DON'T we want to copy out of lib/python/llbase?) -                                self.path("*.py") -                                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()) : -                            self.path(launcher_icon) -                            with self.prefix(dst="vmp_icons"): -                                self.path("secondlife.ico") -                        #VMP Tkinter icons -                        with self.prefix(src_dst="vmp_icons"): -                            self.path("*.png") -                            self.path("*.gif") - -                # -------------------- nested viewer_app --------------------- -                with self.prefix(dst=os.path.join(viewer_app, "Contents")): -                    # defer Info.plist until after MacOS -                    with self.prefix(dst="MacOS"): -                        # CMake constructs the Second Life executable in the -                        # MacOS directory belonging to the top-level Second -                        # 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   = self.dst_path_of(f) -                            fromrel   = self.relpath(fromwhere, relbase) -                            torel     = self.relpath(towhere, relbase) -                            if not self.is_rearranging(): -                                print "Not yet moving {} => {}".format(fromrel, torel) -                            else: -                                print "Moving {} => {}".format(fromrel, torel) -                                # now do it, only without relativizing paths -                                os.rename(fromwhere, towhere) - -                        # If we haven't yet moved executables, find our viewer -                        # executable where it was linked, in toplevel_MacOS. -                        # If we have, find it here. -                        whichdir = here if self.is_rearranging() else toplevel_MacOS -                        # Pick the biggest of the executables as the real viewer. -                        # Make (basename, fullpath) pairs; for each pair, -                        # expand to (size, basename, fullpath) triples; sort -                        # by size; pick the last triple; take the basename and -                        # fullpath from that. -                        _, exename, exepath = \ -                            sorted((os.path.getsize(path), name, path) -                                   for name, path in -                                   ((name, os.path.join(whichdir, name)) -                                    for name in os.listdir(whichdir)))[-1] - -                        if self.is_rearranging(): -                            # NOTE: the -S argument to strip causes it to keep -                            # enough info for annotated backtraces (i.e. function -                            # names in the crash log). 'strip' with no arguments -                            # yields a slightly smaller binary but makes crash -                            # logs mostly useless. This may be desirable for the -                            # final release. Or not. -                            self.run_command(['strip', '-S', exepath]) - -                    # Info.plist is just like top-level one... -                    Info = plistlib.readPlist(Info_plist) -                    # except for these replacements: -                    # (CFBundleExecutable may be moot: SL_Launcher directly -                    # runs the executable, instead of launching the app) -                    Info["CFBundleExecutable"] = exename -                    Info["CFBundleIconFile"] = viewer_icon -                    bugsplat_db = self.args.get('bugsplat') -                    if bugsplat_db: -                        # 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") - -                    with self.prefix(dst="Frameworks"): -                        # CEF framework goes inside viewer_app/Contents/Frameworks. -                        CEF_framework = "Chromium Embedded Framework.framework" -                        self.path2basename(relpkgdir, CEF_framework) -                        # Remember where we parked this car. -                        CEF_framework = self.dst_path_of(CEF_framework) - -                        if self.args.get('bugsplat'): -                            self.path2basename(relpkgdir, "BugsplatMac.framework") - -                    with self.prefix(dst="Resources"): -                        # defer cross-platform file copies until we're in the right -                        # nested Resources directory -                        super(DarwinManifest, self).construct() - -                        with self.prefix(src=self.icon_path()) : -                            self.path(viewer_icon) - -                        with self.prefix(src=relpkgdir): -                            self.path("libndofdev.dylib") -                            self.path("libhunspell-1.3.0.dylib")    - -                        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): -                            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)]) - -                            # 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'): -                                    self.path( "*.dylib" ) -                                    self.path( "plugins.dat" ) +                # 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("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") + +                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") + +                # 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 @@ -1472,10 +1319,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  @@ -1531,12 +1375,8 @@ class LinuxManifest(ViewerManifest):              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"): -                self.path("SL_Launcher") -                self.path("*.py") -            with self.prefix(src=os.path.join("lib", "python", "llbase"), dst="llbase"): +            with self.prefix(src="../viewer_components/manager", dst=""):                  self.path("*.py") -                self.path("_cllsd.so")                   # recurses, packaged again          self.path("res-sdl") @@ -1611,7 +1451,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): diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index c767d52c7b..9193d32b49 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -128,6 +128,15 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params)      LL_DEBUGS("LLLogin") << " connected with  uri '" << uri << "', login_params " << login_params << LL_ENDL;	  } +namespace { +// Instantiate this rendezvous point at namespace scope so it's already +// present no matter how early the updater might post to it. +// Use an LLEventMailDrop, which has future-like semantics: regardless of the +// relative order in which post() or listen() are called, it delivers each +// post() event to its listener(s) until one of them consumes that event. +static LLEventMailDrop sSyncPoint("LoginSync"); +} +  void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)  {      LLSD printable_params = login_params; @@ -219,7 +228,44 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)          }          else          { -            sendProgressEvent("offline", "fail.login", mAuthResponse["responses"]); +            // Synchronize here with the updater. We synchronize here rather +            // than in the fail.login handler, which actually examines the +            // response from login.cgi, because here we are definitely in a +            // coroutine and can definitely use suspendUntilBlah(). Whoever's +            // listening for fail.login might not be. + +            // If the reason for login failure is that we must install a +            // required update, we definitely want to pass control to the +            // updater to manage that for us. We'll handle any other login +            // failure ourselves, as usual. We figure that no matter where you +            // are in the world, or what kind of network you're on, we can +            // reasonably expect the Viewer Version Manager to respond more or +            // less as quickly as login.cgi. This synchronization is only +            // intended to smooth out minor races between the two services. +            // But what if the updater crashes? Use a timeout so that +            // eventually we'll tire of waiting for it and carry on as usual. +            // Given the above, it can be a fairly short timeout, at least +            // from a human point of view. + +            // Since sSyncPoint is an LLEventMailDrop, we DEFINITELY want to +            // consume the posted event. +            LLCoros::OverrideConsuming oc(true); +            // Timeout should produce the isUndefined() object passed here. +            LL_DEBUGS("LLLogin") << "Login failure, waiting for sync from updater" << LL_ENDL; +            LLSD updater = llcoro::suspendUntilEventOnWithTimeout(sSyncPoint, 10, LLSD()); +            if (updater.isUndefined()) +            { +                LL_WARNS("LLLogin") << "Failed to hear from updater, proceeding with fail.login" +                                    << LL_ENDL; +            } +            else +            { +                LL_DEBUGS("LLLogin") << "Got responses from updater and login.cgi" << LL_ENDL; +            } +            // Let the fail.login handler deal with empty updater response. +            LLSD responses(mAuthResponse["responses"]); +            responses["updater"] = updater; +            sendProgressEvent("offline", "fail.login", responses);          }          return;             // Done!      } @@ -249,10 +295,10 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)      // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an      // llsd with no "responses" node. To make the output from an incomplete login symmetrical       // to success, add a data/message and data/reason fields. -    LLSD error_response; -    error_response["reason"] = mAuthResponse["status"]; -    error_response["errorcode"] = mAuthResponse["errorcode"]; -    error_response["message"] = mAuthResponse["error"]; +    LLSD error_response(LLSDMap +                        ("reason",    mAuthResponse["status"]) +                        ("errorcode", mAuthResponse["errorcode"]) +                        ("message",   mAuthResponse["error"]));      if(mAuthResponse.has("certificate"))      {          error_response["certificate"] = mAuthResponse["certificate"];  | 
