From d2b1fb8b5d82582d09116e00485fd292a3a21938 Mon Sep 17 00:00:00 2001 From: Pytak Date: Sun, 3 Mar 2019 21:36:20 +0100 Subject: [PATCH 01/91] Rename PyFX Water presets to include [PyFX] for consistence with WindLights --- ...%2B%20Texture.xml => %5BPyFX%5D%20Default%20%2B%20Texture.xml} | 0 .../water/{Py%27s%20Default.xml => %5BPyFX%5D%20Default.xml} | 0 ...20%2B%20Texture.xml => %5BPyFX%5D%20Ocean%20%2B%20Texture.xml} | 0 ...B%20Texture.xml => %5BPyFX%5D%20Ocean%202%20%2B%20Texture.xml} | 0 .../water/{Py%27s%20Ocean%202.xml => %5BPyFX%5D%20Ocean%202.xml} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename indra/newview/app_settings/windlight/water/{Py%27s%20Default%20%2B%20Texture.xml => %5BPyFX%5D%20Default%20%2B%20Texture.xml} (100%) rename indra/newview/app_settings/windlight/water/{Py%27s%20Default.xml => %5BPyFX%5D%20Default.xml} (100%) rename indra/newview/app_settings/windlight/water/{Py%27s%20Ocean%20%2B%20Texture.xml => %5BPyFX%5D%20Ocean%20%2B%20Texture.xml} (100%) rename indra/newview/app_settings/windlight/water/{Py%27s%20Ocean%202%20%2B%20Texture.xml => %5BPyFX%5D%20Ocean%202%20%2B%20Texture.xml} (100%) rename indra/newview/app_settings/windlight/water/{Py%27s%20Ocean%202.xml => %5BPyFX%5D%20Ocean%202.xml} (100%) diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Default%20%2B%20Texture.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Default%20%2B%20Texture.xml similarity index 100% rename from indra/newview/app_settings/windlight/water/Py%27s%20Default%20%2B%20Texture.xml rename to indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Default%20%2B%20Texture.xml diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Default.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Default.xml similarity index 100% rename from indra/newview/app_settings/windlight/water/Py%27s%20Default.xml rename to indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Default.xml diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%20%2B%20Texture.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ocean%20%2B%20Texture.xml similarity index 100% rename from indra/newview/app_settings/windlight/water/Py%27s%20Ocean%20%2B%20Texture.xml rename to indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ocean%20%2B%20Texture.xml diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202%20%2B%20Texture.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ocean%202%20%2B%20Texture.xml similarity index 100% rename from indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202%20%2B%20Texture.xml rename to indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ocean%202%20%2B%20Texture.xml diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ocean%202.xml similarity index 100% rename from indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202.xml rename to indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ocean%202.xml From 4a59efadeb06a63e409ac9bcc9bac77f3c0de4ed Mon Sep 17 00:00:00 2001 From: Damian Zhaoying Date: Fri, 12 Apr 2019 22:24:50 -0300 Subject: [PATCH 02/91] Fix spaming messages in assert and is_trivially_copyable on GCC 4 --- indra/llcommon/llerror.h | 2 +- indra/llcommon/lluuid.h | 2 +- indra/llcommon/stdtypes.h | 12 ++++++++++++ indra/llmath/llmatrix4a.h | 2 +- indra/llmath/llquaternion.h | 2 +- indra/llmath/llquaternion2.h | 2 +- indra/llmath/llsimdtypes.h | 4 ++-- indra/llmath/llvector4a.h | 2 +- indra/llmath/m3math.h | 2 +- indra/llmath/m4math.h | 2 +- indra/llmath/v2math.h | 2 +- indra/llmath/v3color.h | 2 +- indra/llmath/v3dmath.h | 2 +- indra/llmath/v3math.h | 2 +- indra/llmath/v4color.h | 2 +- indra/llmath/v4coloru.h | 2 +- indra/llmath/v4math.h | 2 +- indra/llprimitive/llmaterialid.h | 2 +- 18 files changed, 30 insertions(+), 18 deletions(-) diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index dc7274312..7bf467621 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -72,7 +72,7 @@ const int LL_ERR_NOERR = 0; #endif // !_DEBUG -static const std::string liru_assert_strip(const std::string& file) { return file.substr(1+file.substr(0, file.find_last_of("/\\")).find_last_of("/\\")); } //return foo/bar.cpp or perhaps foo\bar.cpp +inline const std::string liru_assert_strip(const std::string& file) { return file.substr(1+file.substr(0, file.find_last_of("/\\")).find_last_of("/\\")); } //return foo/bar.cpp or perhaps foo\bar.cpp #define llassert_always_msg(func, msg) if (LL_UNLIKELY(!(func))) LL_ERRS() << "ASSERT (" << msg << ")\nfile:" << liru_assert_strip(__FILE__) << " line:" << std::dec << __LINE__ << LL_ENDL diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index 66a845738..7923d50db 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -271,7 +271,7 @@ inline U32 LLUUID::getCRC32() const // } -static_assert(std::is_trivially_copyable{}, "LLUUID must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLUUID must be a trivially copyable type"); typedef std::vector uuid_vec_t; typedef boost::unordered_set uuid_set_t; diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h index b5079f849..4b7d95bb1 100644 --- a/indra/llcommon/stdtypes.h +++ b/indra/llcommon/stdtypes.h @@ -109,3 +109,15 @@ typedef U8 LLPCode; #define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) ) #endif + +#if __GNUG__ && __GNUC__ < 5 +namespace std +{ + template + struct is_trivially_copyable + { + static const bool value = __has_trivial_copy(T); + operator bool() { return value; } + }; +} +#endif diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h index 7334eec52..301fec84d 100644 --- a/indra/llmath/llmatrix4a.h +++ b/indra/llmath/llmatrix4a.h @@ -709,5 +709,5 @@ inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m) void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents); -static_assert(std::is_trivially_copyable{}, "LLMatrix4a must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLMatrix4a must be a trivially copyable type"); #endif diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h index 87b272008..5cf7e20c8 100644 --- a/indra/llmath/llquaternion.h +++ b/indra/llmath/llquaternion.h @@ -191,7 +191,7 @@ inline void LLQuaternion::setValue(const LLSD& sd) mQ[3] = sd[3].asReal(); } -static_assert(std::is_trivially_copyable{}, "LLQuaternion must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLQuaternion must be a trivially copyable type"); // checker inline BOOL LLQuaternion::isFinite() const diff --git a/indra/llmath/llquaternion2.h b/indra/llmath/llquaternion2.h index b93d0471b..6d616a9b3 100644 --- a/indra/llmath/llquaternion2.h +++ b/indra/llmath/llquaternion2.h @@ -105,6 +105,6 @@ protected: } LL_ALIGN_POSTFIX(16); -static_assert(std::is_trivially_copyable{}, "LLQuaternion2 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLQuaternion2 must be a trivially copyable type"); #endif diff --git a/indra/llmath/llsimdtypes.h b/indra/llmath/llsimdtypes.h index 2861fa9b0..86430b917 100644 --- a/indra/llmath/llsimdtypes.h +++ b/indra/llmath/llsimdtypes.h @@ -121,7 +121,7 @@ private: LLQuad mQ; }; -static_assert(std::is_trivially_copyable{}, "LLBool32 must be a trivially copyable type"); -static_assert(std::is_trivially_copyable{}, "LLSimdScalar must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLBool32 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLSimdScalar must be a trivially copyable type"); #endif //LL_SIMD_TYPES_H diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h index 2b7cf10cb..04e2de2e2 100644 --- a/indra/llmath/llvector4a.h +++ b/indra/llmath/llvector4a.h @@ -346,5 +346,5 @@ inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v) return s; } -static_assert(std::is_trivially_copyable{}, "LLVector4a must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLVector4a must be a trivially copyable type"); #endif diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h index 5822ff044..a25f3e906 100644 --- a/indra/llmath/m3math.h +++ b/indra/llmath/m3math.h @@ -144,7 +144,7 @@ class LLMatrix3 friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a }; -static_assert(std::is_trivially_copyable{}, "LLMatrix3 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLMatrix3 must be a trivially copyable type"); inline LLMatrix3::LLMatrix3(void) { diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h index 01739218b..c4d37d0f7 100644 --- a/indra/llmath/m4math.h +++ b/indra/llmath/m4math.h @@ -246,7 +246,7 @@ public: friend std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a); // Stream a }; -static_assert(std::is_trivially_copyable{}, "LLMatrix4 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLMatrix4 must be a trivially copyable type"); inline const LLMatrix4& LLMatrix4::setIdentity() { diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h index cc00096cc..d6fb00438 100644 --- a/indra/llmath/v2math.h +++ b/indra/llmath/v2math.h @@ -110,7 +110,7 @@ class LLVector2 friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a }; -static_assert(std::is_trivially_copyable{}, "LLVector2 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLVector2 must be a trivially copyable type"); // Non-member functions diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index d9a7f6286..b8a5fb95e 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -140,7 +140,7 @@ public: inline void exp(); // Do an exponential on the color }; -static_assert(std::is_trivially_copyable{}, "LLColor3 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLColor3 must be a trivially copyable type"); LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u); diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h index eed1ed061..2efa5a500 100644 --- a/indra/llmath/v3dmath.h +++ b/indra/llmath/v3dmath.h @@ -130,7 +130,7 @@ class LLVector3d }; -static_assert(std::is_trivially_copyable{}, "LLVector3d must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLVector3d must be a trivially copyable type"); typedef LLVector3d LLGlobalVec; diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h index 65122b894..35acead65 100644 --- a/indra/llmath/v3math.h +++ b/indra/llmath/v3math.h @@ -149,7 +149,7 @@ class LLVector3 static BOOL parseVector3(const std::string& buf, LLVector3* value); }; -static_assert(std::is_trivially_copyable{}, "LLVector3 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLVector3 must be a trivially copyable type"); typedef LLVector3 LLSimLocalVec; diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index e23d51075..845160d3d 100644 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -222,7 +222,7 @@ class LLColor4 inline void clamp(); }; -static_assert(std::is_trivially_copyable{}, "LLColor4 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLColor4 must be a trivially copyable type"); // Non-member functions F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h index 7eb9a2929..3a313759d 100644 --- a/indra/llmath/v4coloru.h +++ b/indra/llmath/v4coloru.h @@ -139,7 +139,7 @@ public: static LLColor4U blue; }; -static_assert(std::is_trivially_copyable{}, "LLColor4U must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLColor4U must be a trivially copyable type"); // Non-member functions F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h index 5cfed4dc7..735636805 100644 --- a/indra/llmath/v4math.h +++ b/indra/llmath/v4math.h @@ -137,7 +137,7 @@ class LLVector4 friend LLVector4 operator-(const LLVector4 &a); // Return vector -a }; -static_assert(std::is_trivially_copyable{}, "LLVector4 must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLVector4 must be a trivially copyable type"); // Non-member functions F32 angle_between(const LLVector4 &a, const LLVector4 &b); // Returns angle (radians) between a and b diff --git a/indra/llprimitive/llmaterialid.h b/indra/llprimitive/llmaterialid.h index 8036db2d9..275939032 100644 --- a/indra/llprimitive/llmaterialid.h +++ b/indra/llprimitive/llmaterialid.h @@ -73,7 +73,7 @@ private: } ; static_assert(sizeof(LLMaterialID) == MATERIAL_ID_SIZE, "LLMaterialID must be sizeof(mID)"); -static_assert(std::is_trivially_copyable{}, "LLMaterialID must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLMaterialID must be a trivially copyable type"); #endif // LL_LLMATERIALID_H From 947cf571007e96e59c8a8de7a19973a50dcbe752 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 13 Apr 2019 02:53:01 -0400 Subject: [PATCH 03/91] And then I remembered windows is windows. --- indra/cmake/BuildVersion.cmake | 2 +- indra/newview/llimpanel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 8c441d650..2f36e3ad1 100644 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -46,7 +46,7 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n set(VIEWER_CHANNEL_VERSION_DEFINES "LL_VIEWER_CHANNEL=\"${VIEWER_CHANNEL}\"" - "LL_VIEWER_CHANNEL_GRK=\"${VIEWER_CHANNEL_GRK}\"" + "LL_VIEWER_CHANNEL_GRK=L\"${VIEWER_CHANNEL_GRK}\"" "LL_VIEWER_VERSION_MAJOR=${VIEWER_VERSION_MAJOR}" "LL_VIEWER_VERSION_MINOR=${VIEWER_VERSION_MINOR}" "LL_VIEWER_VERSION_PATCH=${VIEWER_VERSION_PATCH}" diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 29324f87f..98c4d6581 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1362,7 +1362,7 @@ void LLFloaterIMPanel::onSendMsg() if (mSessionType == SUPPORT_SESSION && getChildView("Support Check")->getValue()) { - utf8_text.insert(action ? 3 : 0, llformat(action ? " (%d%s)" : "(%d%s): ", LL_VIEWER_VERSION_BUILD, LL_VIEWER_CHANNEL_GRK)); + utf8_text.insert(action ? 3 : 0, llformat(action ? " (%d%s)" : "(%d%s): ", LL_VIEWER_VERSION_BUILD, wstring_to_utf8str(LL_VIEWER_CHANNEL_GRK))); } if ( mSessionInitialized ) From c78770138d6560e08b8cb61583f865b40901f156 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 13 Apr 2019 03:02:26 -0400 Subject: [PATCH 04/91] If a log file doesn't exist, just let the user know with a sound Also a warning, in the log, displaying the path. --- indra/newview/llimpanel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 98c4d6581..c708c056e 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1163,6 +1163,12 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) void show_log_browser(const std::string& name, const std::string& id) { const std::string file(LLLogChat::makeLogFileName(name)); + if (!LLFile::isfile(file)) + { + make_ui_sound("UISndBadKeystroke"); + LL_WARNS() << "File not present: " << file << LL_ENDL; + return; + } if (gSavedSettings.getBOOL("LiruLegacyLogLaunch")) { if (!LLWindow::ShellEx(file)) // 0 = success, otherwise fallback on internal browser. From 5228451c9cbd92b52fb17fe7a7dc6953a944f06a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 13 Apr 2019 03:09:12 -0400 Subject: [PATCH 05/91] Fix a lag bug of chat vanishing at the start, 'cause that's wrong. This might break chat, but I'm sleepy, so you get it untested... do the math yourself if it's wrong. --- indra/llui/lltexteditor.h | 4 +++- indra/newview/llimpanel.cpp | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 1bac9702c..8aa058936 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -415,8 +415,10 @@ protected: S32 removeChar(S32 pos); void removeWord(bool prev); S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op); +public: S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op); - +protected: + // Direct operations S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted S32 removeStringNoUndo(S32 pos, S32 length); diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index c708c056e..6851a476a 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1490,11 +1490,7 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) mVoiceChannel->updateSessionID(session_id); mSessionInitialized = true; - //we assume the history editor hasn't moved at all since - //we added the starting session message - //so, we count how many characters to remove - S32 chars_to_remove = mHistoryEditor->getWText().length() - mSessionStartMsgPos; - mHistoryEditor->removeTextFromEnd(chars_to_remove); + mHistoryEditor->remove(mSessionStartMsgPos, sSessionStartString.size(), true); //and now, send the queued msg for (LLSD::array_iterator iter = mQueuedMsgsForInit.beginArray(); From 5e12a9d05745b2fad39c6e2ca99b7c8497ca432c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Liru=20F=C3=A6rs?= Date: Sat, 13 Apr 2019 10:07:34 -0400 Subject: [PATCH 06/91] Scream! Thanks for looking into this Router --- indra/llcommon/stdtypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h index 4b7d95bb1..7eef3695e 100644 --- a/indra/llcommon/stdtypes.h +++ b/indra/llcommon/stdtypes.h @@ -108,8 +108,6 @@ typedef U8 LLPCode; #define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) ) -#endif - #if __GNUG__ && __GNUC__ < 5 namespace std { @@ -121,3 +119,5 @@ namespace std }; } #endif + +#endif From fd6b367746c564fd1469df6d8c55f2e0c9b5bc0a Mon Sep 17 00:00:00 2001 From: Router Gray Date: Sat, 13 Apr 2019 15:43:47 -0500 Subject: [PATCH 07/91] Fix missed changes for is_trivially_copyable and make gcc happy about them (#4) --- indra/llmath/llmatrix3a.h | 4 ++-- indra/newview/llimpanel.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llmath/llmatrix3a.h b/indra/llmath/llmatrix3a.h index ea0c99237..40632644b 100644 --- a/indra/llmath/llmatrix3a.h +++ b/indra/llmath/llmatrix3a.h @@ -127,6 +127,6 @@ public: inline bool isOkRotation() const; } LL_ALIGN_POSTFIX(16); -static_assert(std::is_trivially_copyable{}, "LLMatrix3a must be a trivially copyable type"); -static_assert(std::is_trivially_copyable{}, "LLRotation must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLMatrix3a must be a trivially copyable type"); +static_assert(std::is_trivially_copyable::value, "LLRotation must be a trivially copyable type"); #endif diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 6851a476a..e40970565 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1368,7 +1368,7 @@ void LLFloaterIMPanel::onSendMsg() if (mSessionType == SUPPORT_SESSION && getChildView("Support Check")->getValue()) { - utf8_text.insert(action ? 3 : 0, llformat(action ? " (%d%s)" : "(%d%s): ", LL_VIEWER_VERSION_BUILD, wstring_to_utf8str(LL_VIEWER_CHANNEL_GRK))); + utf8_text.insert(action ? 3 : 0, llformat(action ? " (%d%s)" : "(%d%s): ", LL_VIEWER_VERSION_BUILD, wstring_to_utf8str(LL_VIEWER_CHANNEL_GRK).data())); } if ( mSessionInitialized ) From 41c56f706a87ce127a5abb9e3b541e7aa162da6a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 12:32:47 -0400 Subject: [PATCH 08/91] It's all Greek to me Thanks, Router! --- indra/cmake/Variables.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 016f031ea..3a668ffa5 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -223,11 +223,11 @@ string(REPLACE " " "" VIEWER_CHANNEL_ONEWORD ${VIEWER_CHANNEL}) option(VIEWER_CHANNEL_GRK "Greek character(s) to represent the viewer channel for support purposes, override only for special branches" "") if (NOT VIEWER_CHANNEL_GRK) if (VIEWER_CHANNEL_BASE MATCHES "Test") - set(VIEWER_CHANNEL_GRK "τ") + set(VIEWER_CHANNEL_GRK "\\u03C4") # "τ" elseif (VIEWER_CHANNEL_BASE MATCHES "Alpha") - set(VIEWER_CHANNEL_GRK "α") + set(VIEWER_CHANNEL_GRK "\\u03B1") # "α" elseif (VIEWER_CHANNEL_BASE MATCHES "Beta") - set(VIEWER_CHANNEL_GRK "β") + set(VIEWER_CHANNEL_GRK "\\u03B2") # "β" endif () endif (NOT VIEWER_CHANNEL_GRK) From 68a4975fe1583008fe25d2632bb0c4856f26d96b Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 15:01:48 -0400 Subject: [PATCH 09/91] Actually fix the removal of Starting session Thanks for the assistance and testing, Router~! --- indra/newview/llimpanel.cpp | 8 +++++--- indra/newview/llimpanel.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index e40970565..6334a7f62 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -288,7 +288,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mInputEditor(NULL), mHistoryEditor(NULL), mSessionInitialized(false), - mSessionStartMsgPos(0), + mSessionStartMsgPos({0, 0}), mSessionType(P2P_SESSION), mSessionUUID(session_id), mLogLabel(log_label), @@ -396,12 +396,14 @@ LLFloaterIMPanel::LLFloaterIMPanel( LLUIString session_start = sSessionStartString; session_start.setArg("[NAME]", getTitle()); - mSessionStartMsgPos = mHistoryEditor->getWText().length(); + mSessionStartMsgPos.first = mHistoryEditor->getLength(); addHistoryLine( session_start, gSavedSettings.getColor4("SystemChatColor"), false); + + mSessionStartMsgPos.second = mHistoryEditor->getLength() - mSessionStartMsgPos.first; } } } @@ -1490,7 +1492,7 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) mVoiceChannel->updateSessionID(session_id); mSessionInitialized = true; - mHistoryEditor->remove(mSessionStartMsgPos, sSessionStartString.size(), true); + mHistoryEditor->remove(mSessionStartMsgPos.first, mSessionStartMsgPos.second, true); //and now, send the queued msg for (LLSD::array_iterator iter = mQueuedMsgsForInit.beginArray(); diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 832c0ddee..1fddf5603 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -191,8 +191,8 @@ private: bool mSessionInitialized; - // Where does the "Starting session..." line start? - S32 mSessionStartMsgPos; + // Where does the "Starting session..." line start and how long is it? + std::pair mSessionStartMsgPos; SType mSessionType; From 2df95bb4bd63fe485f69ed7bdff5fb3fda6da9e6 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 17:57:22 -0400 Subject: [PATCH 10/91] Floater About VLC support --- indra/newview/llfloaterabout.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index cc9628d2a..a08362714 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -71,6 +71,9 @@ #endif #include "cef/dullahan.h" +#if VLCPLUGIN +#include "vlc/libvlc_version.h" +#endif // LL_WINDOWS extern LLMemoryInfo gSysMemory; extern U32 gPacketsIn; @@ -295,9 +298,20 @@ LLFloaterAbout::LLFloaterAbout() << DULLAHAN_VERSION_BUILD << " / CEF: " << CEF_VERSION - << " / Chrome: " << CHROME_VERSION_MAJOR; + << " / Chrome: " << CHROME_VERSION_MAJOR + << '\n'; + +#if VLCPLUGIN + supportstrm << "LibVLC: "; + supportstrm << LIBVLC_VERSION_MAJOR; + supportstrm << '.'; + supportstrm << LIBVLC_VERSION_MINOR; + supportstrm << '.'; + supportstrm << LIBVLC_VERSION_REVISION; + supportstrm << '\n'; +#endif + support += supportstrm.str(); - support += '\n'; if (gPacketsIn > 0) { From 7c40f3332f9cb8348094f7efaf96bd620ee45b7c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 18:07:32 -0400 Subject: [PATCH 11/91] Myan --- indra/newview/viewer_manifest.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 57eb2bb41..5e47b1a45 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1061,11 +1061,11 @@ class LinuxManifest(ViewerManifest): self.end_prefix(icon_path) # plugins - if self.prefix(src="", dst="bin/llplugin"): - self.path2basename("../plugins/filepicker", "libbasic_plugin_filepicker.so") - self.path("../plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") - self.path("../plugins/cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so") - self.end_prefix("bin/llplugin") + if self.prefix(src="../plugins", dst="bin/llplugin"): + self.path2basename("filepicker", "libbasic_plugin_filepicker.so") + self.path("gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") + self.path("cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so") + self.end_prefix() # CEF files if self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="bin"): From 7c41dc2e7e2ae5d786216e4e5f6811310487466e Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 18:09:39 -0400 Subject: [PATCH 12/91] It's a syncing ship Ha... get it? I'm so lonely... --- indra/cmake/OpenGL.cmake | 4 ++++ indra/newview/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/cmake/OpenGL.cmake b/indra/cmake/OpenGL.cmake index abf908f23..c77295f63 100644 --- a/indra/cmake/OpenGL.cmake +++ b/indra/cmake/OpenGL.cmake @@ -1,4 +1,6 @@ # -*- cmake -*- + +include(Variables) include(Prebuilt) if (NOT (STANDALONE OR DARWIN)) @@ -8,3 +10,5 @@ if (NOT (STANDALONE OR DARWIN)) ${LIBS_PREBUILT_LEGACY_DIR}/include ) endif (NOT (STANDALONE OR DARWIN)) + +include(FindOpenGL) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d457aa430..413ccc056 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -11,7 +11,6 @@ include(CMakeCopyIfDifferent) include(DBusGlib) include(FMODSTUDIO) include(GLOD) -include(FindOpenGL) include(Hunspell) include(Json) include(LLAddBuildTest) @@ -36,6 +35,7 @@ include(Linking) include(NDOF) include(NVAPI) include(OPENAL) +include(OpenGL) include(OpenSSL) include(StateMachine) include(TemplateCheck) From f670c774d373d226978da94016ebfa6be3d0a206 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 18:28:22 -0400 Subject: [PATCH 13/91] Oh hey, we can do this part of VLC merge right now too! --- indra/newview/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 413ccc056..341d75cbd 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1434,6 +1434,12 @@ endif (WINDOWS) set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") +if (LIBVLCPLUGIN) + set_source_files_properties(llfloaterabout.cpp PROPERTIES COMPILE_DEFINITIONS "VLCPLUGIN=1") +else (LIBVLCPLUGIN) + set_source_files_properties(llfloaterabout.cpp PROPERTIES COMPILE_DEFINITIONS "VLCPLUGIN=0") +endif (LIBVLCPLUGIN) + list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES}) set_source_files_properties(${viewer_HEADER_FILES} From e5f3d1d8bc0f8021c023fb5f46d84a73e755833d Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 17 Apr 2019 18:41:13 -0400 Subject: [PATCH 14/91] Break Linux? Fix volume catcher? Only time will tell --- indra/plugins/cef/linux_volume_catcher.cpp | 119 +++++++-------------- indra/plugins/cef/mac_volume_catcher.cpp | 104 ++++++------------ 2 files changed, 72 insertions(+), 151 deletions(-) diff --git a/indra/plugins/cef/linux_volume_catcher.cpp b/indra/plugins/cef/linux_volume_catcher.cpp index 91be3a89e..57e2e5bd6 100644 --- a/indra/plugins/cef/linux_volume_catcher.cpp +++ b/indra/plugins/cef/linux_volume_catcher.cpp @@ -155,55 +155,40 @@ extern "C" { } -class VolumeCatcherImpl +class VolumeCatcherImpl : public LLSingleton { -public: + friend LLSingleton; VolumeCatcherImpl(); + // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance. ~VolumeCatcherImpl(); +public: + void setVolume(F32 volume); - void pump(void); + void setPan(F32 pan) {}; // for internal use - can't be private because used from our C callbacks - bool loadsyms(std::string pulse_dso_name); - void init(); - void cleanup(); - - void update_all_volumes(F32 volume); void update_index_volume(U32 index, F32 volume); void connected_okay(); std::set mSinkInputIndices; std::map mSinkInputNumChannels; - F32 mDesiredVolume; + F32 mVolume; pa_glib_mainloop *mMainloop; pa_context *mPAContext; bool mConnected; bool mGotSyms; + F32 mPan; }; VolumeCatcherImpl::VolumeCatcherImpl() - : mDesiredVolume(0.0f), - mMainloop(NULL), - mPAContext(NULL), + : mVolume(0.0f), + mMainloop(nullptr), + mPAContext(nullptr), mConnected(false), - mGotSyms(false) -{ - init(); -} - -VolumeCatcherImpl::~VolumeCatcherImpl() -{ - cleanup(); -} - -bool VolumeCatcherImpl::loadsyms(std::string pulse_dso_name) -{ - return grab_pa_syms(pulse_dso_name); -} - -void VolumeCatcherImpl::init() + mGotSyms(false), + mPan(0.f) // default pan is centered { // try to be as defensive as possible because PA's interface is a // bit fragile and (for our purposes) we'd rather simply not function @@ -213,7 +198,7 @@ void VolumeCatcherImpl::init() // libpulse.so.0 - this isn't a great assumption, and the two DSOs should // probably be loaded separately. Our Linux DSO framework needs refactoring, // we do this sort of thing a lot with practically identical logic... - mGotSyms = loadsyms("libpulse-mainloop-glib.so.0"); + mGotSyms = grab_pa_syms("libpulse-mainloop-glib.so.0"); if (!mGotSyms) return; // better make double-sure glib itself is initialized properly. @@ -245,7 +230,7 @@ void VolumeCatcherImpl::init() // PA context to a PA daemon. if (mPAContext) { - llpa_context_set_state_callback(mPAContext, callback_context_state, this); + llpa_context_set_state_callback(mPAContext, callback_context_state, nullptr); pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN? if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0) { @@ -260,7 +245,7 @@ void VolumeCatcherImpl::init() } } -void VolumeCatcherImpl::cleanup() +VolumeCatcherImpl::~VolumeCatcherImpl() { mConnected = false; @@ -269,33 +254,30 @@ void VolumeCatcherImpl::cleanup() llpa_context_disconnect(mPAContext); llpa_context_unref(mPAContext); } - mPAContext = NULL; + mPAContext = nullptr; if (mGotSyms && mMainloop) { llpa_glib_mainloop_free(mMainloop); } - mMainloop = NULL; + mMainloop = nullptr; } void VolumeCatcherImpl::setVolume(F32 volume) { - mDesiredVolume = volume; + mVolume = volume; if (!mGotSyms) return; if (mConnected && mPAContext) { - update_all_volumes(mDesiredVolume); + for (auto& input : mSinkInputIndices) + { + update_index_volume(input, mVolume); + } } - pump(); -} - -void VolumeCatcherImpl::pump() -{ - gboolean may_block = FALSE; - g_main_context_iteration(g_main_context_default(), may_block); + g_main_context_iteration(g_main_context_default(), /*may_block = */false); } void VolumeCatcherImpl::connected_okay() @@ -305,7 +287,7 @@ void VolumeCatcherImpl::connected_okay() // fetch global list of existing sinkinputs if ((op = llpa_context_get_sink_input_info_list(mPAContext, callback_discovered_sinkinput, - this))) + nullptr))) { llpa_operation_unref(op); } @@ -313,7 +295,7 @@ void VolumeCatcherImpl::connected_okay() // subscribe to future global sinkinput changes llpa_context_set_subscribe_callback(mPAContext, callback_subscription_alert, - this); + nullptr); if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK_INPUT), NULL, NULL))) @@ -322,15 +304,6 @@ void VolumeCatcherImpl::connected_okay() } } -void VolumeCatcherImpl::update_all_volumes(F32 volume) -{ - for (std::set::iterator it = mSinkInputIndices.begin(); - it != mSinkInputIndices.end(); ++it) - { - update_index_volume(*it, volume); - } -} - void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume) { static pa_cvolume cvol; @@ -340,10 +313,10 @@ void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume) pa_context *c = mPAContext; uint32_t idx = index; const pa_cvolume *cvolumep = &cvol; - pa_context_success_cb_t cb = NULL; // okay as null - void *userdata = NULL; // okay as null + pa_context_success_cb_t cb = nullptr; // okay as null + void* userdata = nullptr; // okay as null - pa_operation *op; + pa_operation* op; if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata))) { llpa_operation_unref(op); @@ -353,9 +326,6 @@ void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume) void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata) { - VolumeCatcherImpl *impl = dynamic_cast((VolumeCatcherImpl*)userdata); - llassert(impl); - if (0 == eol) { pa_proplist *proplist = sii->proplist; @@ -363,6 +333,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info if (sinkpid == getpid()) // does the discovered sinkinput belong to this process? { + auto impl = VolumeCatcherImpl::getInstance(); bool is_new = (impl->mSinkInputIndices.find(sii->index) == impl->mSinkInputIndices.end()); @@ -371,12 +342,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info if (is_new) { - // new! - impl->update_index_volume(sii->index, impl->mDesiredVolume); - } - else - { - // seen it already, do nothing. + impl->update_index_volume(sii->index, impl->mVolume); } } } @@ -384,8 +350,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) { - VolumeCatcherImpl *impl = dynamic_cast((VolumeCatcherImpl*)userdata); - llassert(impl); + VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SINK_INPUT: @@ -401,15 +366,11 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type { // ask for more info about this new sinkinput pa_operation *op; - if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl))) + if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, nullptr))) { llpa_operation_unref(op); } } - else - { - // property change on this sinkinput - we don't care. - } break; default:; @@ -418,8 +379,7 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type void callback_context_state(pa_context *context, void *userdata) { - VolumeCatcherImpl *impl = dynamic_cast((VolumeCatcherImpl*)userdata); - llassert(impl); + VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); switch (llpa_context_get_state(context)) { @@ -441,28 +401,25 @@ void callback_context_state(pa_context *context, void *userdata) VolumeCatcher::VolumeCatcher() { - pimpl = new VolumeCatcherImpl(); + pimpl = VolumeCatcherImpl::getInstance(); } VolumeCatcher::~VolumeCatcher() { - delete pimpl; - pimpl = NULL; + // Let the instance persist until exit. } void VolumeCatcher::setVolume(F32 volume) { - llassert(pimpl); pimpl->setVolume(volume); } void VolumeCatcher::setPan(F32 pan) { - // TODO: implement this (if possible) + pimpl->setPan(pan); } void VolumeCatcher::pump() { - llassert(pimpl); - pimpl->pump(); + g_main_context_iteration(g_main_context_default(), /*may_block = */false); } diff --git a/indra/plugins/cef/mac_volume_catcher.cpp b/indra/plugins/cef/mac_volume_catcher.cpp index dddb9c207..0560dbef6 100644 --- a/indra/plugins/cef/mac_volume_catcher.cpp +++ b/indra/plugins/cef/mac_volume_catcher.cpp @@ -45,67 +45,47 @@ struct VolumeCatcherStorage; -class VolumeCatcherImpl +class VolumeCatcherImpl : public LLSingleton { + friend LLSingleton; + VolumeCatcherImpl(); + // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance. + ~VolumeCatcherImpl(); + public: void setVolume(F32 volume); void setPan(F32 pan); - void setInstanceVolume(VolumeCatcherStorage *instance); - std::list mComponentInstances; Component mOriginalDefaultOutput; Component mVolumeAdjuster; - - static VolumeCatcherImpl *getInstance(); + private: - // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance. - VolumeCatcherImpl(); - static VolumeCatcherImpl *sInstance; - - // The singlar instance of this class is expected to last until the process exits. - // To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link. - ~VolumeCatcherImpl(); - F32 mVolume; F32 mPan; }; -VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;; - struct VolumeCatcherStorage { - ComponentInstance self; - ComponentInstance delegate; + ComponentInstance self, delegate; }; static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage); static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self); static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self); -VolumeCatcherImpl *VolumeCatcherImpl::getInstance() -{ - if(!sInstance) - { - sInstance = new VolumeCatcherImpl; - } - - return sInstance; -} - VolumeCatcherImpl::VolumeCatcherImpl() +: mVolume(1.0f), // default volume is max + mPan(0.f) // default pan is centered { - mVolume = 1.0; // default to full volume - mPan = 0.0; // and center pan - ComponentDescription desc; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; - + // Find the original default output component mOriginalDefaultOutput = FindNextComponent(NULL, &desc); @@ -114,7 +94,6 @@ VolumeCatcherImpl::VolumeCatcherImpl() // Capture the original component, so we always get found instead. CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster); - } static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage) @@ -146,20 +125,18 @@ static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, H static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self) { - ComponentResult result = noErr; VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); storage = new VolumeCatcherStorage; storage->self = self; - storage->delegate = NULL; + storage->delegate = nullptr; - result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate)); + ComponentResult result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate)); if(result != noErr) { // std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl; - // If we failed to open the delagate component, our open is going to fail. Clean things up. delete storage; } @@ -172,7 +149,7 @@ static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *stora impl->mComponentInstances.push_back(storage); // and set up the initial volume - impl->setInstanceVolume(storage); + impl->setVolume(storage); } return result; @@ -180,14 +157,12 @@ static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *stora static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self) { - ComponentResult result = noErr; - if(storage) { if(storage->delegate) { CloseComponent(storage->delegate); - storage->delegate = NULL; + storage->delegate = nullptr; } VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); @@ -195,18 +170,30 @@ static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *stor delete[] storage; } - return result; + return noErr; } void VolumeCatcherImpl::setVolume(F32 volume) { - VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); - impl->mVolume = volume; - + mVolume = volume; + // Iterate through all known instances, setting the volume on each. - for(std::list::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter) + for(auto& instance : mComponentInstances) { - impl->setInstanceVolume(*iter); +// std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl; + if (instance && instance->delegate) + { + if (OSStatus err = AudioUnitSetParameter( + instance->delegate, + kHALOutputParam_Volume, + kAudioUnitScope_Global, + 0, + mVolume, + 0)) + { +// std::cerr << " AudioUnitSetParameter returned " << err << std::endl; + } + } } } @@ -214,35 +201,12 @@ void VolumeCatcherImpl::setPan(F32 pan) { VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance(); impl->mPan = pan; - + // TODO: implement this. // This will probably require adding a "panner" audio unit to the chain somehow. // There's also a "3d mixer" component that we might be able to use... } -void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance) -{ -// std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl; - - OSStatus err = noErr; - - if(instance && instance->delegate) - { - err = AudioUnitSetParameter( - instance->delegate, - kHALOutputParam_Volume, - kAudioUnitScope_Global, - 0, - mVolume, - 0); - } - - if(err) - { -// std::cerr << " AudioUnitSetParameter returned " << err << std::endl; - } -} - ///////////////////////////////////////////////////// VolumeCatcher::VolumeCatcher() From 07569d008d6de1ebd4258138d4509f211af4bd6c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 09:19:20 -0400 Subject: [PATCH 15/91] Fix compile issues, thanks Router! --- indra/cmake/Variables.cmake | 2 +- indra/newview/llvovolume.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 3a668ffa5..cbaf571b5 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -39,7 +39,7 @@ option(UNATTENDED "Disable use of uneeded tooling for automated builds" OFF) # Media Plugins option(ENABLE_MEDIA_PLUGINS "Turn off building media plugins if they are imported by third-party library mechanism" ON) -option(LIBVLCPLUGIN "Turn off building support for libvlc plugin" ON) +option(LIBVLCPLUGIN "Turn off building support for libvlc plugin" OFF) if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(LIBVLCPLUGIN OFF) endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 7f3c5ea97..3ad3c2565 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1529,7 +1529,7 @@ void LLVOVolume::regenFaces() BOOL LLVOVolume::genBBoxes(BOOL force_global) { - BOOL res = TRUE; + bool res = true; LLVector4a min,max; From 41ebd51fca736dd019b6f3e201f695d0ee1d90cd Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 10:16:48 -0400 Subject: [PATCH 16/91] C'mon, reallly? --- indra/llui/lltexteditor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 8aa058936..ee7296b5d 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -310,10 +310,10 @@ protected: LLHandle mPopupMenuHandle; - S32 getLength() const { return mWText.length(); } void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const; void drawPreeditMarker(); public: + S32 getLength() const { return mWText.length(); } void updateLineStartList(S32 startpos = 0); protected: void updateScrollFromCursor(); From 289d1055e53f22be42ef265a358713e081c2e81a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 13:55:56 -0400 Subject: [PATCH 17/91] Fix bad md5sum? --- autobuild.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autobuild.xml b/autobuild.xml index 0a281b49b..f178e298d 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -630,7 +630,7 @@ archive hash - 2ecc71350b30a1057091b9cd7af18b1c + c3e093d40e93ff242e9b9c953ae7316c url https://depot.alchemyviewer.org/pub/windows/lib-vc142/dullahan-1.1.930-3.3282.1733.g9091548-windows-201802202358.tar.bz2 From 8979f12111b35b68eb0411c4c3c54ca75d60fcbd Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 14:45:21 -0400 Subject: [PATCH 18/91] Fix the chat bug, I think... also momre text base sync, yay! --- indra/llui/lltexteditor.cpp | 78 +++++++++++++++++++++++++++++++++++++ indra/llui/lltexteditor.h | 4 ++ 2 files changed, 82 insertions(+) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 1b0501973..73327d2e3 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4690,8 +4690,58 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) { + auto seg_iter = getSegIterContaining(pos); + while(seg_iter != mSegments.end()) + { + LLTextSegmentPtr segmentp = *seg_iter; + S32 end = pos + length; + if (segmentp->getStart() < pos) + { + // deleting from middle of segment + if (segmentp->getEnd() > end) + { + segmentp->setEnd(segmentp->getEnd() - length); + } + // truncating segment + else + { + segmentp->setEnd(pos); + } + } + else if (segmentp->getStart() < end) + { + // deleting entire segment + if (segmentp->getEnd() <= end) + { + // remove segment + auto seg_to_erase(seg_iter++); + mSegments.erase(seg_to_erase); + continue; + } + // deleting head of segment + else + { + segmentp->setStart(pos); + segmentp->setEnd(segmentp->getEnd() - length); + } + } + else + { + // shifting segments backward to fill deleted portion + segmentp->setStart(segmentp->getStart() - length); + segmentp->setEnd(segmentp->getEnd() - length); + } + ++seg_iter; + } + mWText.erase(pos, length); + + // recreate default segment in case we erased everything + createDefaultSegment(); + mTextIsUpToDate = FALSE; + needsReflow(/*pos*/); + return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length } @@ -4966,6 +5016,34 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) } +LLTextEditor::segment_list_t::iterator LLTextEditor::getSegIterContaining(S32 index) +{ + S32 text_len = getLength(); + + if (index > text_len) { return mSegments.end(); } + + // when there are no segments, we return the end iterator, which must be checked by caller + if (mSegments.size() <= 1) { return mSegments.begin(); } + + LLPointer index_segment = new LLTextSegment(index); + auto it = std::upper_bound(mSegments.begin(), mSegments.end(), index_segment); + return it; +} + +LLTextEditor::segment_list_t::const_iterator LLTextEditor::getSegIterContaining(S32 index) const +{ + S32 text_len = getLength(); + + if (index > text_len) { return mSegments.end(); } + + // when there are no segments, we return the end iterator, which must be checked by caller + if (mSegments.size() <= 1) { return mSegments.begin(); } + + LLPointer index_segment = new LLTextSegment(index); + auto it = std::upper_bound(mSegments.begin(), mSegments.end(), index_segment); + return it; +} + // Finds the text segment (if any) at the give local screen position LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index ee7296b5d..0d92803b4 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -465,6 +465,10 @@ protected: BOOL mParseHighlights; typedef std::vector segment_list_t; + + segment_list_t::iterator getSegIterContaining(S32 index); + segment_list_t::const_iterator getSegIterContaining(S32 index) const; + segment_list_t mSegments; LLTextSegmentPtr mHoverSegment; From 6c034506c33b85c3c70898c8d7dc4097267eec0d Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 16:02:24 -0400 Subject: [PATCH 19/91] Does this fix the issue with recent items not updating? --- indra/newview/llinventorypanel.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index a8a155436..b16879113 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -562,6 +562,11 @@ void LLInventoryPanel::modelChanged(U32 mask) } // Singu note: Needed to propagate name change to wearables. view_item->nameOrDescriptionChanged(); + LLFolderViewFolder* parent = view_item->getParentFolder(); + if(parent) + { + parent->dirtyFilter(); + } } } @@ -629,11 +634,13 @@ void LLInventoryPanel::modelChanged(U32 mask) { // Add the UI element for this item. buildNewViews(item_id); + if (auto parent = view_item->getParentFolder()) parent->dirtyFilter(); // Select any newly created object that has the auto rename at top of folder root set. if(mFolderRoot.get()->getRoot()->needsAutoRename()) { setSelection(item_id, FALSE); } + //updateFolderLabel(model_item->getParentUUID()); } ////////////////////////////// @@ -645,6 +652,7 @@ void LLInventoryPanel::modelChanged(U32 mask) // Don't process the item if it is the root if (view_item->getRoot() != view_item) { + //auto* viewmodel_folder = old_parent->getListener(); LLFolderViewFolder* new_parent = (LLFolderViewFolder*)getItemByID(model_item->getParentUUID()); // Item has been moved. if (old_parent != new_parent) @@ -663,6 +671,7 @@ void LLInventoryPanel::modelChanged(U32 mask) setSelection(item_id, FALSE); } } + //updateFolderLabel(model_item->getParentUUID()); } else { @@ -674,6 +683,11 @@ void LLInventoryPanel::modelChanged(U32 mask) // doesn't include trash). Just remove the item's UI. view_item->destroyView(); } + /*if(viewmodel_folder) + { + updateFolderLabel(viewmodel_folder->getUUID()); + }*/ + old_parent->dirtyFilter(); } } } @@ -684,9 +698,18 @@ void LLInventoryPanel::modelChanged(U32 mask) else if (!model_item && view_item) { // Remove the item's UI. - //LLFolderViewFolder* parent = view_item->getParentFolder(); + LLFolderViewFolder* parent = view_item->getParentFolder(); removeItemID(view_item->getListener()->getUUID()); view_item->destroyView(); + if(parent) + { + parent->dirtyFilter(); + /*auto* viewmodel_folder = parent->getListener(); + if(viewmodel_folder) + { + updateFolderLabel(viewmodel_folder->getUUID()); + }*/ + } } } } From d933472a167ee9de797c89c47f22265f2539ec3f Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 23:29:28 -0400 Subject: [PATCH 20/91] Wooops, I'm sick and blind --- indra/newview/llinventorypanel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index b16879113..b7a2c95b2 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -634,7 +634,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { // Add the UI element for this item. buildNewViews(item_id); - if (auto parent = view_item->getParentFolder()) parent->dirtyFilter(); + if (auto parent = getItemByID(model_item->getParentUUID())) parent->dirtyFilter(); // Select any newly created object that has the auto rename at top of folder root set. if(mFolderRoot.get()->getRoot()->needsAutoRename()) { From b28313559dc983fba7c308ebdd6ed6d79325a0ab Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 18 Apr 2019 23:49:47 -0400 Subject: [PATCH 21/91] Update to VS2019, you now need autobuild-1.1 on windows --- autobuild.xml | 219 ++++++++++++---------------- indra/cmake/BuildPackagesInfo.cmake | 11 +- indra/cmake/Variables.cmake | 165 ++++++++++----------- indra/newview/viewer_manifest.py | 38 ++++- scripts/packages-formatter.py | 23 ++- 5 files changed, 225 insertions(+), 231 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index f178e298d..fa8a1c307 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -585,6 +585,40 @@ version 1 + discord-rpc + + canonical_repo + https://bitbucket.org/alchemyviewer/3p-discord-rpc + copyright + Copyright 2017 Discord, Inc. + description + Discord Remote Procedure Call library + license + MIT + license_file + LICENSES/discord-rpc.txt + name + discord-rpc + platforms + + windows64 + + archive + + hash + 76bd78e14034e9c518c1a201074d568a + hash_algorithm + md5 + url + http://depot.alchemyviewer.org/pub/packages/windows64/msvc-1916/discord_rpc-3.4.0.190570542-windows64-190570542.tar.bz2 + + name + windows64 + + + version + 3.4.0.190570542 + dullahan copyright @@ -768,7 +802,7 @@ fmodstudio copyright - FMOD Studio, copyright © Firelight Technologies Pty, Ltd., 1994-2015. + FMOD Studio, copyright (c) Firelight Technologies Pty, Ltd., 2012-2017. description FMOD Studio audio system library license @@ -784,11 +818,11 @@ archive hash - a2badcf46277f7547bab0963fe262696 + a3ccf7916d1e0b6f01370978b661c139 hash_algorithm md5 url - https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/fmodstudio-1.08.01-darwin-201604080928.tar.bz2 + https://depot.alchemyviewer.org/pub/darwin/lib/fmodstudio-1.10.02-darwin-201712262058.tar.bz2 name darwin @@ -812,11 +846,11 @@ archive hash - be09dc1f144c936790236b94190815b4 + 54dbd41322a08a1fc333ca6d96af5502 hash_algorithm md5 url - https://depot.alchemyviewer.org/pub/linux64/lib/fmodstudio-1.08.04-linux64-201605301500.tar.bz2 + https://depot.alchemyviewer.org/pub/linux64/lib/fmodstudio-1.10.00-linux64-201709282320.tar.bz2 name linux64 @@ -840,18 +874,18 @@ archive hash - c2c45c2ebd9fd930098aa2a3b85bbe5f + 779ae33c6b84656295bd6cf50a69c830 hash_algorithm md5 url - https://depot.alchemyviewer.org/pub/windows64/lib/fmodstudio-1.10.02-windows64-201712251435.tar.bz2 + http://depot.alchemyviewer.org/pub/packages/windows64/msvc/fmodstudio-1.10.10.190360453-windows64-190360453.tar.bz2 name windows64 version - 1.10.02 + 1.10.10.190360453 fontconfig @@ -1128,13 +1162,13 @@ glod copyright - Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia. All rights reserved. + Copyright 2003 Jonathan Cohen, Nat Duca, David Luebke, Brenden Schubert - Johns Hopkins University and University of Virginia description Geometric Level of Detail library license - GLOD Open-Source License 1.0 + GLOD Open-Source License Version 1.0 license_file - LICENSES/glod.txt + LICENSES/GLOD.txt name glod platforms @@ -1144,23 +1178,37 @@ archive hash - 6c6b3b06a4305c2da0aad1de132e6a07 + 71e678d70e276fc42a56926fc28a7abd hash_algorithm md5 url - https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/glod-1.0pre4-darwin-201511222034.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/glod_3p-update-glod/rev/296895/arch/Darwin/installer/glod-1.0pre4.296895-darwin-296895.tar.bz2 name darwin + darwin64 + + archive + + hash + 017ef34ddf14293099a90c6eaa3615ca + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1626/3627/glod-1.0pre3.501614-darwin64-501614.tar.bz2 + + name + darwin64 + linux archive hash - 176736c52b3cde6ca8e7d9e173d91731 + 58113bcbbacbaeb2d278f745867ae6f0 + hash_algorithm + md5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-glod/rev/268002/arch/Linux/installer/glod-1.0pre4-linux-20121212.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-glod/rev/314201/arch/Linux/installer/glod-1.0pre4.314201-linux-314201.tar.bz2 name linux @@ -1170,11 +1218,9 @@ archive hash - f7172928ba2201d4b9aeb2c70d7a55b9 - hash_algorithm - md5 + 9aef5cd576ace19568da01d9bc3db29c url - http://depot.alchemyviewer.org/pub/linux64/lib-trusty/glod-1.0pre4-linux64-201603240031.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1625/3628/glod-1.0pre3.501614-linux64-501614.tar.bz2 name linux64 @@ -1184,11 +1230,11 @@ archive hash - 37035aad7d066dfe6f26dc009aa9dcea + 573e68f46f825a1c040daa4994ee2a61 hash_algorithm md5 url - https://depot.alchemyviewer.org/pub/windows/lib-vc141/glod-1.0pre4-windows-201703090622.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1627/3633/glod-1.0pre3.501614-windows-501614.tar.bz2 name windows @@ -1198,18 +1244,18 @@ archive hash - c5c04c164ff057a43928de2f2b83470e + c122ba644e8ec146b4742428b799d749 hash_algorithm md5 url - https://depot.alchemyviewer.org/pub/windows64/lib-vc142/glod-1.0pre4-windows64-201712251340.tar.bz2 + http://depot.alchemyviewer.org/pub/packages/windows64/msvc-1916/glod-1.0pre3.190390340-windows64-190390340.tar.bz2 name windows64 version - 1.0pre4 + 1.0pre3.190390340 google_breakpad @@ -1218,7 +1264,7 @@ description Breakpad is a crossplatform library for capturing crash callstacks and runtime data. license - BSD + bsd license_file LICENSES/google_breakpad.txt name @@ -1239,6 +1285,18 @@ name darwin + darwin64 + + archive + + hash + 2d43c6a149cd9c89ba19e884579b1e25 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1836/4096/google_breakpad-1413.501824-darwin64-501824.tar.bz2 + + name + darwin64 + linux archive @@ -2821,7 +2879,7 @@ options -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo - -DWORD_SIZE:STRING=32 + -DWORD_SIZE:STRING=$AUTOBUILD_ADDRSIZE -DROOT_PROJECT_NAME:STRING=Singularity -DINSTALL_PROPRIETARY=FALSE @@ -2842,7 +2900,7 @@ options -DCMAKE_BUILD_TYPE:STRING=Release - -DWORD_SIZE:STRING=32 + -DWORD_SIZE:STRING=$AUTOBUILD_ADDRSIZE -DROOT_PROJECT_NAME:STRING=Singularity -DINSTALL_PROPRIETARY=FALSE @@ -3031,7 +3089,7 @@ windows build_directory - build-vc141 + build-vc$AUTOBUILD_WIN_VSVER-$AUTOBUILD_ADDRSIZE configurations RelWithDebInfo @@ -3063,9 +3121,11 @@ options -G - "Visual Studio 15" - -DCMAKE_SYSTEM_VERSION="10.0.16299.0" - -DINSTALL_PROPRIETARY=FALSE + ${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN} + -A + ${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} + -T + host=${AUTOBUILD_WIN_VSHOST|NOTWIN} default @@ -3086,7 +3146,7 @@ options /p:Configuration=Release - /p:Platform=Win32 + /p:Platform=${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} /t:Build /p:useenv=true /verbosity:normal @@ -3102,96 +3162,11 @@ options -G - "Visual Studio 15" - -DCMAKE_SYSTEM_VERSION="10.0.16299.0" - -DINSTALL_PROPRIETARY=FALSE - - - name - Release - - - name - windows - - windows64 - - build_directory - build-vc141-x86_64 - configurations - - RelWithDebInfo - - build - - arguments - - Singularity.sln - - command - msbuild.exe - options - - /p:Configuration=RelWithDebInfo - /p:Platform=x64 - /t:Build - /p:useenv=true - /verbosity:normal - /m - - - configure - - arguments - - ..\indra - - options - - -G - "Visual Studio 15 Win64" - -DCMAKE_SYSTEM_VERSION="10.0.16299.0" - -DWORD_SIZE:STRING=64 - -DINSTALL_PROPRIETARY=FALSE - - - default - True - name - RelWithDebInfo - - Release - - build - - arguments - - Singularity.sln - - command - msbuild.exe - options - - /p:Configuration=Release - /p:Platform=x64 - /t:Build - /p:useenv=true - /verbosity:normal - /m - - - configure - - arguments - - ..\indra - - options - - -G - "Visual Studio 15 Win64" - -DCMAKE_SYSTEM_VERSION="10.0.16299.0" - -DWORD_SIZE:STRING=64 + ${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN} + -A + ${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} + -T + host=${AUTOBUILD_WIN_VSHOST|NOTWIN} -DINSTALL_PROPRIETARY=FALSE diff --git a/indra/cmake/BuildPackagesInfo.cmake b/indra/cmake/BuildPackagesInfo.cmake index a764e9b42..6b2410ea5 100644 --- a/indra/cmake/BuildPackagesInfo.cmake +++ b/indra/cmake/BuildPackagesInfo.cmake @@ -3,9 +3,16 @@ include(Python) include(Variables) +# packages-formatter.py runs autobuild install --versions, which needs to know +# the build_directory, which (on Windows) depends on AUTOBUILD_ADDRSIZE. +# Within an autobuild build, AUTOBUILD_ADDRSIZE is already set. But when +# building in an IDE, it probably isn't. Set it explicitly using +# run_build_test.py. add_custom_command(OUTPUT packages-info.txt - COMMENT "Generating packages-info.txt for the about box" + COMMENT Generating packages-info.txt for the about box MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/../autobuild.xml DEPENDS ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py -p=${AUTOBUILD_PLATFORM_NAME} > packages-info.txt + ${CMAKE_SOURCE_DIR}/../autobuild.xml + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py "${VIEWER_CHANNEL}" "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}" > packages-info.txt ) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index cbaf571b5..f522e66cf 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -24,14 +24,6 @@ set(VIEWER_PREFIX) set(INTEGRATION_TESTS_PREFIX) option(LL_TESTS "Build and run unit and integration tests (disable for build timing runs to reduce variation" OFF) -# Compiler and toolchain options -option(USESYSTEMLIBS "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF) -option(STANDALONE "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF) -if (USESYSTEMLIBS) - set(STANDALONE ON) -elseif (STANDALONE) - set(USESYSTEMLIBS ON) -endif (USESYSTEMLIBS) option(INCREMENTAL_LINK "Use incremental linking on win32 builds (enable for faster links on some machines)" OFF) option(USE_PRECOMPILED_HEADERS "Enable use of precompiled header directives where supported." ON) option(USE_LTO "Enable Whole Program Optimization and related folding and binary reduction routines" OFF) @@ -48,7 +40,7 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Dar set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds automatically disable TCMalloc)") set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.") # Audio Engines -option(FMODSTUDIO "Build with support for the FMOD Studio audio engine" OFF) +option(FMODSTUDIO "Build with support for the FMOD Studio audio engine" ON) # Window implementation option(LLWINDOW_SDL2 "Use SDL2 for window and input handling" OFF) @@ -88,61 +80,56 @@ if (NOT CMAKE_BUILD_TYPE) "Build type. One of: Debug Release RelWithDebInfo" FORCE) endif (NOT CMAKE_BUILD_TYPE) +# If someone has specified an address size, use that to determine the +# architecture. Otherwise, let the architecture specify the address size. +set(ADDRESS_SIZE ${WORD_SIZE}) +if (ADDRESS_SIZE EQUAL 32) + #message(STATUS "ADDRESS_SIZE is 32") + set(ARCH i686) +elseif (ADDRESS_SIZE EQUAL 64) + #message(STATUS "ADDRESS_SIZE is 64") + set(ARCH x86_64) +else (ADDRESS_SIZE EQUAL 32) + #message(STATUS "ADDRESS_SIZE is UNRECOGNIZED: '${ADDRESS_SIZE}'") + # Use Python's platform.machine() since uname -m isn't available everywhere. + # Even if you can assume cygwin uname -m, the answer depends on whether + # you're running 32-bit cygwin or 64-bit cygwin! But even 32-bit Python will + # report a 64-bit processor. + execute_process(COMMAND + "${Python2_EXECUTABLE}" "-c" + "import platform; print platform.machine()" + OUTPUT_VARIABLE ARCH OUTPUT_STRIP_TRAILING_WHITESPACE) + # We expect values of the form i386, i686, x86_64, AMD64. + # In CMake, expressing ARCH.endswith('64') is awkward: + string(LENGTH "${ARCH}" ARCH_LENGTH) + math(EXPR ARCH_LEN_2 "${ARCH_LENGTH} - 2") + string(SUBSTRING "${ARCH}" ${ARCH_LEN_2} 2 ARCH_LAST_2) + if (ARCH_LAST_2 STREQUAL 64) + #message(STATUS "ARCH is detected as 64; ARCH is ${ARCH}") + set(ADDRESS_SIZE 64) + else() + #message(STATUS "ARCH is detected as 32; ARCH is ${ARCH}") + set(ADDRESS_SIZE 32) + endif () +endif (ADDRESS_SIZE EQUAL 32) if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(WINDOWS ON BOOL FORCE) - if (WORD_SIZE EQUAL 64) - set(ARCH x86_64 CACHE STRING "Viewer Architecture") - set(LL_ARCH ${ARCH}_win64) - set(LL_ARCH_DIR ${ARCH}-win64) - set(WORD_SIZE 64) - set(AUTOBUILD_PLATFORM_NAME "windows64") - else (WORD_SIZE EQUAL 64) - set(ARCH i686 CACHE STRING "Viewer Architecture") set(LL_ARCH ${ARCH}_win32) set(LL_ARCH_DIR ${ARCH}-win32) - set(WORD_SIZE 32) - set(AUTOBUILD_PLATFORM_NAME "windows") - endif (WORD_SIZE EQUAL 64) endif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(LINUX ON BOOL FORCE) - # If someone has specified a word size, use that to determine the - # architecture. Otherwise, let the architecture specify the word size. - if (WORD_SIZE EQUAL 32) - #message(STATUS "WORD_SIZE is 32") - set(ARCH i686) - set(AUTOBUILD_PLATFORM_NAME "linux") - elseif (WORD_SIZE EQUAL 64) - #message(STATUS "WORD_SIZE is 64") - set(ARCH x86_64) - set(AUTOBUILD_PLATFORM_NAME "linux64") - else (WORD_SIZE EQUAL 32) - #message(STATUS "WORD_SIZE is UNDEFINED") - if (CMAKE_SIZEOF_VOID_P EQUAL 8) - message(STATUS "Size of void pointer is detected as 8; ARCH is 64-bit") - set(WORD_SIZE 64) - set(AUTOBUILD_PLATFORM_NAME "linux64") - elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) - message(STATUS "Size of void pointer is detected as 4; ARCH is 32-bit") - set(WORD_SIZE 32) - set(AUTOBUILD_PLATFORM_NAME "linux") - else() - message(FATAL_ERROR "Unknown Architecture!") - endif (CMAKE_SIZEOF_VOID_P EQUAL 8) - endif (WORD_SIZE EQUAL 32) - - if (NOT STANDALONE AND MULTIARCH_HACK) - if (WORD_SIZE EQUAL 32) + if (ADDRESS_SIZE EQUAL 32) set(DEB_ARCHITECTURE i386) set(FIND_LIBRARY_USE_LIB64_PATHS OFF) set(CMAKE_SYSTEM_LIBRARY_PATH /usr/lib32 ${CMAKE_SYSTEM_LIBRARY_PATH}) - else (WORD_SIZE EQUAL 32) + else (ADDRESS_SIZE EQUAL 32) set(DEB_ARCHITECTURE amd64) set(FIND_LIBRARY_USE_LIB64_PATHS ON) - endif (WORD_SIZE EQUAL 32) + endif (ADDRESS_SIZE EQUAL 32) execute_process(COMMAND dpkg-architecture -a${DEB_ARCHITECTURE} -qDEB_HOST_MULTIARCH RESULT_VARIABLE DPKG_RESULT @@ -155,7 +142,6 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") endif (DPKG_RESULT EQUAL 0) include(ConfigurePkgConfig) - endif (NOT STANDALONE AND MULTIARCH_HACK) set(LL_ARCH ${ARCH}_linux) set(LL_ARCH_DIR ${ARCH}-linux) @@ -164,49 +150,42 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(DARWIN 1) - if (XCODE_VERSION LESS 8.0.0) - message( FATAL_ERROR "Xcode 8.0.0 or greater is required." ) - endif (XCODE_VERSION LESS 8.0.0) - message( "Building with " ${CMAKE_OSX_SYSROOT} ) - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12) + # Architecture + set(CMAKE_OSX_SYSROOT macosx10.14) + set(CMAKE_OSX_ARCHITECTURES "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS "x86_64") + # Build Options set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0") - set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL 3) - set(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO) - set(CMAKE_XCODE_ATTRIBUTE_GCC_FAST_MATH NO) - set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") - set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") - if (USE_AVX2) - set(CMAKE_XCODE_ATTRIBUTE_CLANG_X86_VECTOR_INSTRUCTIONS avx2) - elseif (USE_AVX) - set(CMAKE_XCODE_ATTRIBUTE_CLANG_X86_VECTOR_INSTRUCTIONS avx) - else () + set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Debug] "dwarf") + set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym") + + # Deployment + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13) + + # Linking + set(CMAKE_XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING YES) + + # Apple Clang - Code Gen + set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] YES) set(CMAKE_XCODE_ATTRIBUTE_CLANG_X86_VECTOR_INSTRUCTIONS sse4.1) - endif () + set(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO) + set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL[variant=Debug] 0) + set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL[variant=Release] 3) + set(CMAKE_XCODE_ATTRIBUTE_GCC_FAST_MATH NO) - if (${CMAKE_BUILD_TYPE} STREQUAL "Release") - set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf-with-dsym) - else (${CMAKE_BUILD_TYPE} STREQUAL "Release") - set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf) - endif (${CMAKE_BUILD_TYPE} STREQUAL "Release") + # Apple Clang - Custom Compiler Flags + set(CMAKE_XCODE_ATTRIBUTE_WARNING_CFLAGS "-Wall -Wextra -Wno-reorder -Wno-sign-compare -Wno-ignored-qualifiers -Wno-unused-local-typedef -Wno-unused-parameter") - set(ADDRESS_SIZE 64) - if (NOT CMAKE_OSX_ARCHITECTURES) - if (ADDRESS_SIZE EQUAL 64) - set(CMAKE_OSX_ARCHITECTURES x86_64) - else (ADDRESS_SIZE EQUAL 64) - set(CMAKE_OSX_ARCHITECTURES i386) - endif (ADDRESS_SIZE EQUAL 64) - endif (NOT CMAKE_OSX_ARCHITECTURES) + # Apple Clang - Language - C++ + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD c++14) + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + + # Apple Clang - Warning Policies + set(CMAKE_XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS YES) - if (ADDRESS_SIZE EQUAL 64) - set(ARCH x86_64) - else (ADDRESS_SIZE EQUAL 64) - set(ARCH i386) - endif (ADDRESS_SIZE EQUAL 64) set(LL_ARCH ${ARCH}_darwin) set(LL_ARCH_DIR universal-darwin) - set(AUTOBUILD_PLATFORM_NAME "darwin" CACHE STRING "Autobuild Platform Name") endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Default deploy grid @@ -245,13 +224,21 @@ set(VIEWER_CHANNEL_NOSPACE ${VIEWER_CHANNEL_ONEWORD} CACHE STRING "Prefix used f set(VIEWER_BRANDING_ID "singularity" CACHE STRING "Viewer branding id") -set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside") - option(ENABLE_SIGNING "Enable signing the viewer" OFF) set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if necessary.") +set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside") +# Compiler and toolchain options +option(USESYSTEMLIBS "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF) +option(STANDALONE "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF) +if (USESYSTEMLIBS) + set(STANDALONE ON) +elseif (STANDALONE) + set(USESYSTEMLIBS ON) +endif (USESYSTEMLIBS) + + + source_group("CMake Rules" FILES CMakeLists.txt) -mark_as_advanced(AUTOBUILD_PLATFORM_NAME) - endif(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 5e47b1a45..0b66e7691 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -258,6 +258,8 @@ class WindowsManifest(ViewerManifest): # This is used to test a dll manifest. # It is used as a temporary override during the construct method from test_win32_manifest import test_assembly_binding + # TODO: This is redundant with LLManifest.copy_action(). Why aren't we + # calling copy_action() in conjunction with test_assembly_binding()? if src and (os.path.exists(src) or os.path.islink(src)): # ensure that destination path exists self.cmakedirs(os.path.dirname(dst)) @@ -278,6 +280,8 @@ class WindowsManifest(ViewerManifest): # It is used as a temporary override during the construct method from test_win32_manifest import test_assembly_binding from test_win32_manifest import NoManifestException, NoMatchingAssemblyException + # TODO: This is redundant with LLManifest.copy_action(). Why aren't we + # calling copy_action() in conjunction with test_assembly_binding()? if src and (os.path.exists(src) or os.path.islink(src)): # ensure that destination path exists self.cmakedirs(os.path.dirname(dst)) @@ -289,9 +293,9 @@ class WindowsManifest(ViewerManifest): else: test_assembly_binding(src, "Microsoft.VC80.CRT", "") raise Exception("Unknown condition") - except NoManifestException, err: + except NoManifestException as err: pass - except NoMatchingAssemblyException, err: + except NoMatchingAssemblyException as err: pass self.ccopy(src,dst) @@ -327,17 +331,26 @@ class WindowsManifest(ViewerManifest): self.path('libaprutil-1.dll') self.path('libapriconv-1.dll') - except RuntimeError, err: + except RuntimeError as err: print err.message print "Skipping llcommon.dll (assuming llcommon was linked statically)" # Mesh 3rd party libs needed for auto LOD and collada reading try: self.path("glod.dll") - except RuntimeError, err: + except RuntimeError as err: print err.message print "Skipping GLOD library (assumming linked statically)" + # Get fmodstudio dll, continue if missing + try: + if(self.is_win64()): + self.path("fmod64.dll") + else: + self.path("fmod.dll") + except: + print "Skipping fmodstudio audio library(assuming other audio engine)" + # Vivox runtimes self.path("SLVoice.exe") self.path("vivoxsdk.dll") @@ -345,6 +358,23 @@ class WindowsManifest(ViewerManifest): self.path("libsndfile-1.dll") self.path("vivoxoal.dll") self.path("ca-bundle.crt") + + # Security + if(self.is_win64()): + self.path("libcrypto-1_1-x64.dll") + self.path("libssl-1_1-x64.dll") + if not self.is_packaging_viewer(): + self.path("libcrypto-1_1-x64.pdb") + self.path("libssl-1_1-x64.pdb") + else: + self.path("libcrypto-1_1.dll") + self.path("libssl-1_1.dll") + if not self.is_packaging_viewer(): + self.path("libcrypto-1_1.pdb") + self.path("libssl-1_1.pdb") + + # HTTP/2 + self.path("nghttp2.dll") # Hunspell self.path("libhunspell.dll") diff --git a/scripts/packages-formatter.py b/scripts/packages-formatter.py index 1c7c000bd..ac65f545b 100644 --- a/scripts/packages-formatter.py +++ b/scripts/packages-formatter.py @@ -31,13 +31,10 @@ import re import subprocess import argparse -parser = argparse.ArgumentParser() -parser.add_argument( - '-p', '--platform', - default=None, - dest='platform', - help='Override the automatically determined platform.') -parsedargs = parser.parse_args() +parser = argparse.ArgumentParser(description='Format dependency version and copyright information for the viewer About box content') +parser.add_argument('channel', help='viewer channel name') +parser.add_argument('version', help='viewer version number') +args = parser.parse_args() _autobuild=os.getenv('AUTOBUILD', 'autobuild') @@ -49,10 +46,7 @@ def autobuild(*args): Return its stdout pipe from which the caller can read. """ # subprocess wants a list, not a tuple - temp = list(args) - if parsedargs.platform is not None : - temp.append("-p=" + str(parsedargs.platform)) - command = [_autobuild] + temp + command = [_autobuild] + list(args) try: child = subprocess.Popen(command, stdin=None, stdout=subprocess.PIPE, @@ -62,14 +56,15 @@ def autobuild(*args): # Don't attempt to interpret anything but ENOENT raise # Here it's ENOENT: subprocess can't find the autobuild executable. - print >>sys.stderr, "packages-formatter on %s: can't run autobuild:\n%s\n%s" % \ - (sys.platform, ' '.join(command), err) - sys.exit(1) + sys.exit("packages-formatter on %s: can't run autobuild:\n%s\n%s" % \ + (sys.platform, ' '.join(command), err)) # no exceptions yet, let caller read stdout return child.stdout version={} + + versions=autobuild('install', '--versions') for line in versions: pkg_info = pkg_line.match(line) From f26425f17aa09257d67374723b7a0f05b9fc90c5 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 00:05:49 -0400 Subject: [PATCH 22/91] VLC instead of QuickTime I dunno if it's actually in there yet... meh --- indra/cmake/CMakeLists.txt | 2 +- indra/cmake/LibVLCPlugin.cmake | 30 ++++++++++++++++ indra/cmake/QuickTimePlugin.cmake | 57 ------------------------------- indra/cmake/Variables.cmake | 4 ++- indra/newview/viewer_manifest.py | 13 +++++-- indra/plugins/CMakeLists.txt | 13 ++++--- 6 files changed, 50 insertions(+), 69 deletions(-) create mode 100644 indra/cmake/LibVLCPlugin.cmake delete mode 100644 indra/cmake/QuickTimePlugin.cmake diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index cacb7fd05..215f7a300 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -85,7 +85,6 @@ set(cmake_SOURCE_FILES Prebuilt.cmake PulseAudio.cmake Python.cmake - QuickTimePlugin.cmake RunBuildTest.cmake StateMachine.cmake TemplateCheck.cmake @@ -96,6 +95,7 @@ set(cmake_SOURCE_FILES Variables.cmake ViewerMiscLibs.cmake WinManifest.cmake + LibVLCPlugin.cmake XmlRpcEpi.cmake ZLIB.cmake ) diff --git a/indra/cmake/LibVLCPlugin.cmake b/indra/cmake/LibVLCPlugin.cmake new file mode 100644 index 000000000..0787edd2c --- /dev/null +++ b/indra/cmake/LibVLCPlugin.cmake @@ -0,0 +1,30 @@ +# -*- cmake -*- +include(Linking) +include(Prebuilt) +include(Variables) + +if (LIBVLCPLUGIN) +if (USESYSTEMLIBS) +else (USESYSTEMLIBS) + use_prebuilt_binary(vlc-bin) + set(VLC_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/vlc) +endif (USESYSTEMLIBS) + +if (WINDOWS) + set(VLC_PLUGIN_LIBRARIES + libvlc.lib + libvlccore.lib + ) +elseif (DARWIN) + set(VLC_PLUGIN_LIBRARIES + libvlc.dylib + libvlccore.dylib + ) +elseif (LINUX) + # Specify a full path to make sure we get a static link + set(VLC_PLUGIN_LIBRARIES + ${LIBS_PREBUILT_DIR}/lib/libvlc.a + ${LIBS_PREBUILT_DIR}/lib/libvlccore.a + ) +endif (WINDOWS) +endif (LIBVLCPLUGIN) diff --git a/indra/cmake/QuickTimePlugin.cmake b/indra/cmake/QuickTimePlugin.cmake deleted file mode 100644 index 899a043e8..000000000 --- a/indra/cmake/QuickTimePlugin.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# -*- cmake -*- - -if(INSTALL_PROPRIETARY OR USE_QUICKTIME_PREBUILT) - include(Prebuilt) - if (WINDOWS) - use_prebuilt_binary(quicktime) - endif (WINDOWS) -endif(INSTALL_PROPRIETARY OR USE_QUICKTIME_PREBUILT) - -if (DARWIN) - include(CMakeFindFrameworks) - find_library(QUICKTIME_LIBRARY QuickTime) -elseif (WINDOWS AND WORD_SIZE EQUAL 32) - SET(program_files "ProgramFiles(x86)") - SET(program_files $ENV{${program_files}}) - if(NOT program_files) - SET(program_files $ENV{ProgramW6432}) - endif(NOT program_files) - if(NOT program_files) - SET(program_files $ENV{ProgramFiles}) - endif(NOT program_files) - - set(QUICKTIME_SDK_DIR "${program_files}/QuickTime SDK" - CACHE PATH "Location of the QuickTime SDK.") - - find_library(DEBUG_QUICKTIME_LIBRARY qtmlclient - PATHS - ${ARCH_PREBUILT_DIRS_DEBUG} - "${QUICKTIME_SDK_DIR}\\libraries" - ) - - find_library(RELEASE_QUICKTIME_LIBRARY qtmlclient - PATHS - ${ARCH_PREBUILT_DIRS_RELEASE} - "${QUICKTIME_SDK_DIR}\\libraries" - ) - - if (DEBUG_QUICKTIME_LIBRARY AND RELEASE_QUICKTIME_LIBRARY) - set(QUICKTIME_LIBRARY - optimized ${RELEASE_QUICKTIME_LIBRARY} - debug ${DEBUG_QUICKTIME_LIBRARY} - ) - - endif (DEBUG_QUICKTIME_LIBRARY AND RELEASE_QUICKTIME_LIBRARY) - - include_directories( - ${LIBS_PREBUILT_DIR}/include/quicktime - "${QUICKTIME_SDK_DIR}\\CIncludes" - ) -endif (DARWIN) - -mark_as_advanced(QUICKTIME_LIBRARY) - -if (QUICKTIME_LIBRARY) - set(QUICKTIME ON CACHE BOOL "Build with QuickTime streaming media support.") -endif (QUICKTIME_LIBRARY) - diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index f522e66cf..f2613035f 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -31,7 +31,8 @@ option(UNATTENDED "Disable use of uneeded tooling for automated builds" OFF) # Media Plugins option(ENABLE_MEDIA_PLUGINS "Turn off building media plugins if they are imported by third-party library mechanism" ON) -option(LIBVLCPLUGIN "Turn off building support for libvlc plugin" OFF) +option(LIBVLCPLUGIN "Turn off building support for libvlc plugin" ON) + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(LIBVLCPLUGIN OFF) endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -39,6 +40,7 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Dar # Mallocs set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds automatically disable TCMalloc)") set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.") + # Audio Engines option(FMODSTUDIO "Build with support for the FMOD Studio audio engine" ON) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0b66e7691..bcd5bdab0 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -398,9 +398,9 @@ class WindowsManifest(ViewerManifest): self.path("basic_plugin_filepicker.dll") self.end_prefix() - # Media plugins - QuickTime - if self.prefix(src='../plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_quicktime.dll") + # Media plugins - LibVLC + if self.prefix(src='../plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): + self.path("media_plugin_libvlc.dll") self.end_prefix() # Media plugins - CEF @@ -495,6 +495,12 @@ class WindowsManifest(ViewerManifest): self.path("zh-TW.pak") self.end_prefix() + if self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): + self.path("libvlc.dll") + self.path("libvlccore.dll") + self.path("plugins/") + self.end_prefix() + if not self.is_packaging_viewer(): self.package_file = "copied_deps" @@ -1095,6 +1101,7 @@ class LinuxManifest(ViewerManifest): self.path2basename("filepicker", "libbasic_plugin_filepicker.so") self.path("gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") self.path("cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so") + self.path2basename("libvlc", "libmedia_plugin_libvlc.so") self.end_prefix() # CEF files diff --git a/indra/plugins/CMakeLists.txt b/indra/plugins/CMakeLists.txt index b4a820c8f..441eb4dcf 100644 --- a/indra/plugins/CMakeLists.txt +++ b/indra/plugins/CMakeLists.txt @@ -8,13 +8,12 @@ if (LINUX) add_subdirectory(gstreamer010) endif (LINUX) +if (WINDOWS) + if (LIBVLCPLUGIN) + add_subdirectory(libvlc) + endif (LIBVLCPLUGIN) +endif (WINDOWS) + add_subdirectory(cef) - -if (WINDOWS OR DARWIN) - if(NOT WORD_SIZE EQUAL 64) - add_subdirectory(quicktime) - endif(NOT WORD_SIZE EQUAL 64) -endif (WINDOWS OR DARWIN) - add_subdirectory(example_basic) add_subdirectory(example_media) From 1244d5df030faa76807f272dc7408a4018058e6f Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 01:16:27 -0400 Subject: [PATCH 23/91] Fix the chat bug, save the world~ Also fix badly written parts of LL stuff synced. --- indra/llui/lltexteditor.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 73327d2e3..14f451d35 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4691,10 +4691,10 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) { auto seg_iter = getSegIterContaining(pos); + S32 end = pos + length; while(seg_iter != mSegments.end()) { LLTextSegmentPtr segmentp = *seg_iter; - S32 end = pos + length; if (segmentp->getStart() < pos) { // deleting from middle of segment @@ -4714,8 +4714,7 @@ S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) if (segmentp->getEnd() <= end) { // remove segment - auto seg_to_erase(seg_iter++); - mSegments.erase(seg_to_erase); + seg_iter = mSegments.erase(seg_iter); continue; } // deleting head of segment @@ -5026,7 +5025,7 @@ LLTextEditor::segment_list_t::iterator LLTextEditor::getSegIterContaining(S32 in if (mSegments.size() <= 1) { return mSegments.begin(); } LLPointer index_segment = new LLTextSegment(index); - auto it = std::upper_bound(mSegments.begin(), mSegments.end(), index_segment); + auto it = std::lower_bound(mSegments.begin(), mSegments.end(), index_segment, LLTextSegment::compare()); return it; } @@ -5040,7 +5039,7 @@ LLTextEditor::segment_list_t::const_iterator LLTextEditor::getSegIterContaining( if (mSegments.size() <= 1) { return mSegments.begin(); } LLPointer index_segment = new LLTextSegment(index); - auto it = std::upper_bound(mSegments.begin(), mSegments.end(), index_segment); + auto it = std::lower_bound(mSegments.begin(), mSegments.end(), index_segment, LLTextSegment::compare()); return it; } From 308a24989174dad16de9faadc5fc7b50ddacb4ce Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 02:29:12 -0400 Subject: [PATCH 24/91] Pie menus were looking up settings each frame... How am I the first to notice this. Also, while we're here, let's clean up duplicate code and make some constexprs --- indra/llui/llmenugl.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 4b38899d3..6f3006dd7 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -5264,30 +5264,30 @@ void LLPieMenu::draw() mHoverIndex = -1; } - F32 width = (F32) getRect().getWidth(); - F32 height = (F32) getRect().getHeight(); - mCurRadius = PIE_SCALE_FACTOR * llmax( width/2, height/2 ); + const auto& rect = getRect(); + // correct for non-square pixels + F32 center_x = ((F32) rect.getWidth())/2; + F32 center_y = ((F32) rect.getHeight())/2; + constexpr S32 steps = 100; + mCurRadius = PIE_SCALE_FACTOR * llmax(center_x, center_y); mOuterRingAlpha = mUseInfiniteRadius ? 0.f : 1.f; if (mShrinkBorderTimer.getStarted()) { - mOuterRingAlpha = clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 0.f, 1.f); - mCurRadius *= clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 1.f, 1.f / PIE_SCALE_FACTOR); + const auto& elapsed = mShrinkBorderTimer.getElapsedTimeF32(); + mOuterRingAlpha = clamp_rescale(elapsed, 0.f, PIE_SHRINK_TIME, 0.f, 1.f); + mCurRadius *= clamp_rescale(elapsed, 0.f, PIE_SHRINK_TIME, 1.f, 1.f / PIE_SCALE_FACTOR); } - // correct for non-square pixels - F32 center_x = width/2; - F32 center_y = height/2; - S32 steps = 100; gGL.pushUIMatrix(); { gGL.translateUI(center_x, center_y, 0.f); - F32 line_width = LLUI::sConfigGroup->getF32("PieMenuLineWidth"); - LLColor4 line_color = LLUI::sColorsGroup->getColor("PieMenuLineColor"); - LLColor4 bg_color = LLUI::sColorsGroup->getColor("PieMenuBgColor"); - LLColor4 selected_color = LLUI::sColorsGroup->getColor("PieMenuSelectedColor"); + static const LLUICachedControl line_width("PieMenuLineWidth"); + static const LLCachedControl line_color(*LLUI::sColorsGroup, "PieMenuLineColor"); + static const LLCachedControl bg_color(*LLUI::sColorsGroup, "PieMenuBgColor"); + static const LLCachedControl selected_color(*LLUI::sColorsGroup, "PieMenuSelectedColor"); // main body LLColor4 outer_color = bg_color; @@ -5297,7 +5297,7 @@ void LLPieMenu::draw() // selected wedge if (mHoverItem) { - F32 arc_size = F_PI * 0.25f; + constexpr F32 arc_size = F_PI * 0.25f; F32 start_radians = (mHoverIndex * arc_size) - (arc_size * 0.5f); F32 end_radians = start_radians + arc_size; @@ -5315,7 +5315,7 @@ void LLPieMenu::draw() gl_washer_spokes_2d( mCurRadius, (F32)PIE_CENTER_SIZE, 8, line_color, outer_color ); // inner circle - gGL.color4fv( line_color.mV ); + gGL.color4fv( line_color().mV ); gl_circle_2d( 0, 0, (F32)PIE_CENTER_SIZE, steps, FALSE ); // outer circle From 01b4339b6fa00c6d447f004fea165f8268452ca7 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 11:56:27 -0400 Subject: [PATCH 25/91] Fix configure Not sure how Prebuilt.cmake worked in my repo but not others, but okay. Of course I'd forget to stage the libvlc directory. And somehow vlc-bin wasn't added to autobuild.xml?! --- autobuild.xml | 64 ++ indra/cmake/Prebuilt.cmake | 4 +- indra/plugins/libvlc/CMakeLists.txt | 83 +++ indra/plugins/libvlc/media_plugin_libvlc.cpp | 713 +++++++++++++++++++ 4 files changed, 861 insertions(+), 3 deletions(-) create mode 100644 indra/plugins/libvlc/CMakeLists.txt create mode 100644 indra/plugins/libvlc/media_plugin_libvlc.cpp diff --git a/autobuild.xml b/autobuild.xml index fa8a1c307..1ddcabedf 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2675,6 +2675,70 @@ version 14.12.25810.0 + vlc-bin + + copyright + Copyright (C) 1998-2016 VLC authors and VideoLAN + license + GPL2 + license_file + LICENSES/vlc.txt + name + vlc-bin + platforms + + darwin64 + + archive + + hash + e5635e173c75dc0675b48ab5f5e4868b + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12143/71451/vlc_bin-2.2.8.511703-darwin64-511703.tar.bz2 + + name + darwin64 + + linux + + archive + + hash + 2f410640df3f9812d1abff02a414cfa8 + url + https://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-vlc-bin/rev/315283/arch/Linux/vlc_bin-2.2.3-linux-201606011750-r10.tar.bz2 + + name + linux + + windows + + archive + + hash + add560654a53cb1c554044a4fac3c718 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12144/71458/vlc_bin-2.2.8.511703-windows-511703.tar.bz2 + + name + windows + + windows64 + + archive + + hash + 94bf04b49acc1e1bf2c06e2232f8a083 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12145/71463/vlc_bin-2.2.8.511703-windows64-511703.tar.bz2 + + name + windows64 + + + version + 2.2.8.511703 + xmlrpc-epi copyright diff --git a/indra/cmake/Prebuilt.cmake b/indra/cmake/Prebuilt.cmake index 38e24f13d..33a6d7691 100644 --- a/indra/cmake/Prebuilt.cmake +++ b/indra/cmake/Prebuilt.cmake @@ -4,7 +4,6 @@ if(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED) set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES") include(FindAutobuild) -include(Variables) if(INSTALL_PROPRIETARY) include(FindSCP) endif(INSTALL_PROPRIETARY) @@ -40,14 +39,13 @@ macro (use_prebuilt_binary _binary) if(${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/${_binary}_installed OR NOT ${${_binary}_installed} EQUAL 0) if(DEBUG_PREBUILT) - message("cd ${CMAKE_SOURCE_DIR} && ${AUTOBUILD_EXECUTABLE} install + message(STATUS "cd ${CMAKE_SOURCE_DIR} && ${AUTOBUILD_EXECUTABLE} install --install-dir=${AUTOBUILD_INSTALL_DIR} ${_binary} ") endif(DEBUG_PREBUILT) execute_process(COMMAND "${AUTOBUILD_EXECUTABLE}" install --install-dir=${AUTOBUILD_INSTALL_DIR} - -p ${AUTOBUILD_PLATFORM_NAME} ${_binary} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE ${_binary}_installed diff --git a/indra/plugins/libvlc/CMakeLists.txt b/indra/plugins/libvlc/CMakeLists.txt new file mode 100644 index 000000000..619088cd0 --- /dev/null +++ b/indra/plugins/libvlc/CMakeLists.txt @@ -0,0 +1,83 @@ +# -*- cmake -*- + +project(media_plugin_libvlc) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(OpenGL) +include(Variables) + +include(LibVLCPlugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} + ${VLC_INCLUDE_DIR} +) +include_directories(SYSTEM + ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ) + + +### media_plugin_libvlc + +set(media_plugin_libvlc_SOURCE_FILES + media_plugin_libvlc.cpp + ) + +add_library(media_plugin_libvlc + SHARED + ${media_plugin_libvlc_SOURCE_FILES} +) + +target_link_libraries(media_plugin_libvlc + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${VLC_PLUGIN_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +set_target_properties(media_plugin_libvlc PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + +if (WINDOWS) + if (ADDRESS_SIZE EQUAL 32) + set_target_properties( + media_plugin_libvlc + PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO" + ) + else (ADDRESS_SIZE EQUAL 32) + set_target_properties( + media_plugin_libvlc + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) + endif (ADDRESS_SIZE EQUAL 32) +endif (WINDOWS) + +if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + media_plugin_libvlc + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + ) + +endif (DARWIN) diff --git a/indra/plugins/libvlc/media_plugin_libvlc.cpp b/indra/plugins/libvlc/media_plugin_libvlc.cpp new file mode 100644 index 000000000..d1dd5b9fe --- /dev/null +++ b/indra/plugins/libvlc/media_plugin_libvlc.cpp @@ -0,0 +1,713 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +/** +* @file media_plugin_libvlc.cpp +* @brief LibVLC plugin for LLMedia API plugin system +* +* @cond +* $LicenseInfo:firstyear=2008&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +* @endcond +*/ + +#include "linden_common.h" + +#include "llgl.h" +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#include "vlc/vlc.h" +#include "vlc/libvlc_version.h" + +#if LL_WINDOWS +// needed for waveOut call - see below for description +#include +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginLibVLC : + public MediaPluginBase +{ +public: + MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginLibVLC(); + + /*virtual*/ void receiveMessage(const char* message_string) override; + +private: + bool init(); + + void initVLC(); + void playMedia(); + void resetVLC(); + void setVolume(const F64 volume); + void setVolumeVLC(); + void updateTitle(const char* title); + void postLogMessage(const std::string& level, const std::string& msg); + + static void* lock(void* data, void** p_pixels); + static void unlock(void* data, void* id, void* const* raw_pixels); + static void display(void* data, void* id); + + /*virtual*/ void setDirty(int left, int top, int right, int bottom) override + /* override, but that is not supported in gcc 4.6 */; + + static void eventCallbacks(const libvlc_event_t* event, void* ptr); + + static void logCallback(void *data, int level, const libvlc_log_t *ctx, const char *fmt, va_list args); + + libvlc_instance_t* mLibVLC; + libvlc_media_t* mLibVLCMedia; + libvlc_media_player_t* mLibVLCMediaPlayer; + + struct mLibVLCContext + { + mLibVLCContext() : texture_pixels(nullptr), mp(nullptr), parent(nullptr) {}; + unsigned char* texture_pixels = nullptr; + libvlc_media_player_t* mp = nullptr; + MediaPluginLibVLC* parent = nullptr; + }; + struct mLibVLCContext mLibVLCCallbackContext; + + std::string mURL; + F64 mCurVolume; + + bool mIsLooping; + + float mCurTime; + float mDuration; + EStatus mVlcStatus; + + bool mEnableMediaPluginLogging; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginLibVLC::MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : +MediaPluginBase(host_send_func, host_user_data) +{ + mTextureWidth = 0; + mTextureHeight = 0; + mWidth = 0; + mHeight = 0; + mDepth = 4; + mPixels = nullptr; + + mLibVLC = nullptr; + mLibVLCMedia = nullptr; + mLibVLCMediaPlayer = nullptr; + + mCurVolume = 0.0; + + mIsLooping = false; + + mCurTime = 0.0; + mDuration = 0.0; + + mURL = std::string(); + + mEnableMediaPluginLogging = false; + + mVlcStatus = STATUS_NONE; + setStatus(STATUS_NONE); +} + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginLibVLC::~MediaPluginLibVLC() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::postLogMessage(const std::string& level, const std::string& msg) +{ + if (mEnableMediaPluginLogging) + { + std::stringstream str; + str << "VLC Msg> " << msg; + + LLPluginMessage debug_message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "debug_message"); + debug_message.setValue("message_text", str.str()); + debug_message.setValue("message_level", level); + sendMessage(debug_message); + } +} + +///////////////////////////////////////////////////////////////////////////////// +// +void* MediaPluginLibVLC::lock(void* data, void** p_pixels) +{ + struct mLibVLCContext* context = (mLibVLCContext*)data; + + *p_pixels = context->texture_pixels; + + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::unlock(void* data, void* id, void* const* raw_pixels) +{ + // nothing to do here for the moment + // we *could* modify pixels here to, for example, Y flip, but this is done with + // a VLC video filter transform. +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::display(void* data, void* id) +{ + struct mLibVLCContext* context = (mLibVLCContext*)data; + + context->parent->setDirty(0, 0, context->parent->mWidth, context->parent->mHeight); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::initVLC() +{ + char const* vlc_argv[] = + { + "--no-xlib", + "--video-filter=transform{type=vflip}", // MAINT-6578 Y flip textures in plugin vs client + }; + +#if LL_DARWIN + setenv("VLC_PLUGIN_PATH", ".", 1); +#endif + + int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv); + mLibVLC = libvlc_new(vlc_argc, vlc_argv); + + if (!mLibVLC) + { + postLogMessage("warn", "Failed to initialize VLC"); + setStatus(STATUS_ERROR); + return; + // for the moment, if this fails, the plugin will fail and + // the media sub-system will tell the viewer something went wrong. + } + libvlc_log_set(mLibVLC, logCallback, this); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::resetVLC() +{ + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + libvlc_release(mLibVLC); +} + +//////////////////////////////////////////////////////////////////////////////// +// *virtual* +void MediaPluginLibVLC::setDirty(int left, int top, int right, int bottom) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueS32("left", left); + message.setValueS32("top", top); + message.setValueS32("right", right); + message.setValueS32("bottom", bottom); + + message.setValueReal("current_time", mCurTime); + message.setValueReal("duration", mDuration); + message.setValueReal("current_rate", 1.0f); + + sendMessage(message); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) +{ + MediaPluginLibVLC* parent = (MediaPluginLibVLC*)ptr; + if (parent == nullptr) + { + return; + } + + switch (event->type) + { + case libvlc_MediaPlayerOpening: + parent->postLogMessage("info", "Media loading"); + parent->mVlcStatus = STATUS_LOADING; + break; + + case libvlc_MediaPlayerPlaying: + parent->postLogMessage("info", "Media playing"); + parent->mDuration = (float)(libvlc_media_get_duration(parent->mLibVLCMedia)) / 1000.0f; + parent->mVlcStatus = STATUS_PLAYING; + parent->setVolumeVLC(); + break; + + case libvlc_MediaPlayerPaused: + parent->mVlcStatus = STATUS_PAUSED; + break; + + case libvlc_MediaPlayerStopped: + parent->mVlcStatus = STATUS_DONE; + break; + + case libvlc_MediaPlayerEndReached: + parent->mVlcStatus = STATUS_DONE; + break; + + case libvlc_MediaPlayerEncounteredError: + parent->mVlcStatus = STATUS_ERROR; + break; + + case libvlc_MediaPlayerTimeChanged: + parent->postLogMessage("info", "Media time changed"); + parent->mCurTime = (float)libvlc_media_player_get_time(parent->mLibVLCMediaPlayer) / 1000.0f; + break; + + case libvlc_MediaPlayerPositionChanged: + break; + + case libvlc_MediaPlayerLengthChanged: + parent->postLogMessage("info", "Media length changed"); + parent->mDuration = (float)libvlc_media_get_duration(parent->mLibVLCMedia) / 1000.0f; + break; + + case libvlc_MediaPlayerTitleChanged: + { + parent->postLogMessage("info", "Media title getting metadata"); + char* title = libvlc_media_get_meta(parent->mLibVLCMedia, libvlc_meta_Title); + if (title) + { + parent->updateTitle(title); + } + } + break; + } +} + +void MediaPluginLibVLC::logCallback(void *data, int level, const libvlc_log_t *ctx, const char *fmt, va_list args) +{ + MediaPluginLibVLC* parent = (MediaPluginLibVLC*) data; + + char tstr[4096]; + std::vsnprintf(tstr, 4096, fmt, args); + std::stringstream out; + out << tstr; + parent->postLogMessage("info", out.str()); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::playMedia() +{ + if (mURL.length() == 0) + { + return; + } + + if (mLibVLCMediaPlayer) + { + // stop listening to events while we reset things + libvlc_event_manager_t* em = libvlc_media_player_event_manager(mLibVLCMediaPlayer); + if (em) + { + libvlc_event_detach(em, libvlc_MediaPlayerOpening, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerPlaying, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerPaused, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerStopped, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerEndReached, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerEncounteredError, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerTimeChanged, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerLengthChanged, eventCallbacks, nullptr); + libvlc_event_detach(em, libvlc_MediaPlayerTitleChanged, eventCallbacks, nullptr); + }; + + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + + mLibVLCMediaPlayer = nullptr; + } + + if (mLibVLCMedia) + { + libvlc_media_release(mLibVLCMedia); + + mLibVLCMedia = nullptr; + } + + mLibVLCMedia = libvlc_media_new_location(mLibVLC, mURL.c_str()); + if (!mLibVLCMedia) + { + mLibVLCMediaPlayer = nullptr; + setStatus(STATUS_ERROR); + return; + } + + mLibVLCMediaPlayer = libvlc_media_player_new_from_media(mLibVLCMedia); + if (!mLibVLCMediaPlayer) + { + setStatus(STATUS_ERROR); + return; + } + + // listen to events + libvlc_event_manager_t* em = libvlc_media_player_event_manager(mLibVLCMediaPlayer); + if (em) + { + libvlc_event_attach(em, libvlc_MediaPlayerOpening, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPlaying, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPaused, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerStopped, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerEndReached, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerEncounteredError, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerTimeChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerLengthChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerTitleChanged, eventCallbacks, this); + } + + mLibVLCCallbackContext.parent = this; + mLibVLCCallbackContext.texture_pixels = mPixels; + mLibVLCCallbackContext.mp = mLibVLCMediaPlayer; + + // Send a "navigate begin" event. + // This is really a browser message but the QuickTime plugin did it and + // the media system relies on this message to update internal state so we must send it too + // Note: see "navigate_complete" message below too + // https://jira.secondlife.com/browse/MAINT-6528 + LLPluginMessage message_begin(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); + message_begin.setValue("uri", mURL); + message_begin.setValueBoolean("history_back_available", false); + message_begin.setValueBoolean("history_forward_available", false); + sendMessage(message_begin); + + // volume level gets set before VLC is initialized (thanks media system) so we have to record + // it in mCurVolume and set it again here so that volume levels are correctly initialized + setVolume(mCurVolume); + + setStatus(STATUS_LOADED); + + libvlc_video_set_callbacks(mLibVLCMediaPlayer, lock, unlock, display, &mLibVLCCallbackContext); + libvlc_video_set_format(mLibVLCMediaPlayer, "RV32", mWidth, mHeight, mWidth * mDepth); + + // note this relies on the "set_loop" message arriving before the "start" (play) one + // but that appears to always be the case + if (mIsLooping) + { + libvlc_media_add_option(mLibVLCMedia, "input-repeat=-1"); + } + + libvlc_media_player_play(mLibVLCMediaPlayer); + + // send a "location_changed" message - this informs the media system + // that a new URL is the 'current' one and is used extensively. + // Again, this is really a browser message but we will use it here. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); + message.setValue("uri", mURL); + sendMessage(message); + + // Send a "navigate complete" event. + // This is really a browser message but the QuickTime plugin did it and + // the media system relies on this message to update internal state so we must send it too + // Note: see "navigate_begin" message above too + // https://jira.secondlife.com/browse/MAINT-6528 + LLPluginMessage message_complete(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); + message_complete.setValue("uri", mURL); + message_complete.setValueS32("result_code", 200); + message_complete.setValue("result_string", "OK"); + sendMessage(message_complete); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::updateTitle(const char* title) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", title); + sendMessage(message); +} + +void MediaPluginLibVLC::setVolumeVLC() +{ + if (mLibVLCMediaPlayer) + { + int vlc_vol = (int)(mCurVolume * 100); + + int result = libvlc_audio_set_volume(mLibVLCMediaPlayer, vlc_vol); + if (result == 0) + { + // volume change was accepted by LibVLC + } + else + { + // volume change was NOT accepted by LibVLC and not actioned + } + +#if LL_WINDOWS + // https ://jira.secondlife.com/browse/MAINT-8119 + // CEF media plugin uses code in media_plugins/cef/windows_volume_catcher.cpp to + // set the actual output volume of the plugin process since there is no API in + // CEF to otherwise do this. + // There are explicit calls to change the volume in LibVLC but sometimes they + // are ignored SLPlugin.exe process volume is set to 0 so you never heard audio + // from the VLC media stream. + // The right way to solve this is to move the volume catcher stuff out of + // the CEF plugin and into it's own folder under media_plugins and have it referenced + // by both CEF and VLC. That's for later. The code there boils down to this so for + // now, as we approach a release, the less risky option is to do it directly vs + // calls to volume catcher code. + DWORD left_channel = (DWORD)(mCurVolume * 65535.0f); + DWORD right_channel = (DWORD)(mCurVolume * 65535.0f); + DWORD hw_volume = left_channel << 16 | right_channel; + waveOutSetVolume(NULL, hw_volume); +#endif + } + else + { + // volume change was requested but VLC wasn't ready. + // that's okay though because we saved the value in mCurVolume and + // the next volume change after the VLC system is initilzied will set it + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::setVolume(const F64 volume) +{ + mCurVolume = volume; + + setVolumeVLC(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::receiveMessage(const char* message_string) +{ + LLPluginMessage message_in; + + if (message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if (message_name == "init") + { + initVLC(); + + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + std::ostringstream s; + s << "LibVLC plugin "; + s << LIBVLC_VERSION_MAJOR; + s << "."; + s << LIBVLC_VERSION_MINOR; + s << "."; + s << LIBVLC_VERSION_REVISION; + + message.setValue("plugin_version", s.str()); + sendMessage(message); + } + else if (message_name == "idle") + { + setStatus(mVlcStatus); + } + else if (message_name == "cleanup") + { + resetVLC(); + } + else if (message_name == "shm_added") + { + SharedSegmentInfo info; + info.mAddress = message_in.getValuePointer("address"); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if (message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if (iter != mSharedSegments.end()) + { + if (mPixels == iter->second.mAddress) + { + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + mLibVLCMediaPlayer = nullptr; + + mPixels = nullptr; + mTextureSegmentName.clear(); + } + mSharedSegments.erase(iter); + } + else + { + //std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { + //std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if (message_name == "init") + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 1024); + message.setValueS32("default_height", 1024); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGB); + message.setValueU32("format", GL_BGRA_EXT); + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", true); + sendMessage(message); + } + else if (message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + if (!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if (iter != mSharedSegments.end()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mWidth = width; + mHeight = height; + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + playMedia(); + }; + }; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + } + else if (message_name == "load_uri") + { + mURL = message_in.getValue("uri"); + playMedia(); + } + else if (message_name == "enable_media_plugin_debugging") + { + mEnableMediaPluginLogging = message_in.getValueBoolean("enable"); + } + } + else + if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if (message_name == "stop") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_stop(mLibVLCMediaPlayer); + } + } + else if (message_name == "start") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_play(mLibVLCMediaPlayer); + } + } + else if (message_name == "pause") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_set_pause(mLibVLCMediaPlayer, 1); + } + } + else if (message_name == "seek") + { + if (mDuration > 0) + { + F64 normalized_offset = message_in.getValueReal("time") / mDuration; + libvlc_media_player_set_position(mLibVLCMediaPlayer, normalized_offset); + } + } + else if (message_name == "set_loop") + { + mIsLooping = true; + } + else if (message_name == "set_volume") + { + // volume comes in 0 -> 1.0 + F64 volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool MediaPluginLibVLC::init() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", "LibVLC Plugin"); + sendMessage(message); + + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, + void* host_user_data, + LLPluginInstance::sendMessageFunction *plugin_send_func, + void **plugin_user_data) +{ + MediaPluginLibVLC* self = new MediaPluginLibVLC(host_send_func, host_user_data); + *plugin_send_func = MediaPluginLibVLC::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} From 1a88370566f7ddb3b372f4cfe9646e3c97939629 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 15:00:05 -0400 Subject: [PATCH 26/91] Finish hooking up VLC and removing quicktime --- indra/plugins/libvlc/media_plugin_libvlc.cpp | 17 +- indra/plugins/quicktime/CMakeLists.txt | 91 -- .../quicktime/media_plugin_quicktime.cpp | 1112 ----------------- 3 files changed, 12 insertions(+), 1208 deletions(-) delete mode 100644 indra/plugins/quicktime/CMakeLists.txt delete mode 100644 indra/plugins/quicktime/media_plugin_quicktime.cpp diff --git a/indra/plugins/libvlc/media_plugin_libvlc.cpp b/indra/plugins/libvlc/media_plugin_libvlc.cpp index d1dd5b9fe..7811bb626 100644 --- a/indra/plugins/libvlc/media_plugin_libvlc.cpp +++ b/indra/plugins/libvlc/media_plugin_libvlc.cpp @@ -50,7 +50,7 @@ class MediaPluginLibVLC : public MediaPluginBase { public: - MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, LLPluginInstance* host_user_data); ~MediaPluginLibVLC(); /*virtual*/ void receiveMessage(const char* message_string) override; @@ -104,7 +104,7 @@ private: //////////////////////////////////////////////////////////////////////////////// // -MediaPluginLibVLC::MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : +MediaPluginLibVLC::MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, LLPluginInstance*host_user_data) : MediaPluginBase(host_send_func, host_user_data) { mTextureWidth = 0; @@ -700,14 +700,21 @@ bool MediaPluginLibVLC::init() //////////////////////////////////////////////////////////////////////////////// // -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, - void* host_user_data, - LLPluginInstance::sendMessageFunction *plugin_send_func, +/*int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, + LLPluginInstance* host_user_data, + LLPluginInstance::receiveMessageFunction *plugin_send_func, void **plugin_user_data) { MediaPluginLibVLC* self = new MediaPluginLibVLC(host_send_func, host_user_data); *plugin_send_func = MediaPluginLibVLC::staticReceiveMessage; *plugin_user_data = (void*)self; + return 0; +}*/ +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance, + BasicPluginBase** plugin_object) +{ + *plugin_object = new MediaPluginLibVLC(send_message_function, plugin_instance); return 0; } diff --git a/indra/plugins/quicktime/CMakeLists.txt b/indra/plugins/quicktime/CMakeLists.txt deleted file mode 100644 index d1b17331c..000000000 --- a/indra/plugins/quicktime/CMakeLists.txt +++ /dev/null @@ -1,91 +0,0 @@ -# -*- cmake -*- - -project(media_plugin_quicktime) - -include(00-Common) -include(LLCommon) -include(LLImage) -include(LLPlugin) -include(LLMath) -include(LLRender) -include(LLWindow) -include(Linking) -include(PluginAPI) -include(MediaPluginBase) -include(FindOpenGL) -include(QuickTimePlugin) - -include_directories( - ${LLPLUGIN_INCLUDE_DIRS} - ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} - ${LLCOMMON_INCLUDE_DIRS} - ${LLMATH_INCLUDE_DIRS} - ${LLIMAGE_INCLUDE_DIRS} - ${LLRENDER_INCLUDE_DIRS} - ${LLWINDOW_INCLUDE_DIRS} -) - -if (DARWIN) - include(CMakeFindFrameworks) - find_library(CARBON_LIBRARY Carbon) -endif (DARWIN) - -### media_plugin_quicktime - -set(media_plugin_quicktime_SOURCE_FILES - media_plugin_quicktime.cpp - ) - -add_library(media_plugin_quicktime - SHARED - ${media_plugin_quicktime_SOURCE_FILES} -) - -target_link_libraries(media_plugin_quicktime - ${LLPLUGIN_LIBRARIES} - ${MEDIA_PLUGIN_BASE_LIBRARIES} - ${LLCOMMON_LIBRARIES} - ${QUICKTIME_LIBRARY} - ${PLUGIN_API_WINDOWS_LIBRARIES} -) - -add_dependencies(media_plugin_quicktime - ${LLPLUGIN_LIBRARIES} - ${MEDIA_PLUGIN_BASE_LIBRARIES} - ${LLCOMMON_LIBRARIES} -) - -if (WINDOWS) - set_target_properties( - media_plugin_quicktime - PROPERTIES - LINK_FLAGS "/MANIFEST:NO /NODEFAULTLIB:LIBCMT" - LINK_FLAGS_DEBUG "/MANIFEST:NO /NODEFAULTLIB:\"LIBCMT;LIBCMTD\"" - ) -endif (WINDOWS) - -if (QUICKTIME) - - add_definitions(-DLL_QUICKTIME_ENABLED=1) - - if (DARWIN) - # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name - set_target_properties( - media_plugin_quicktime - PROPERTIES - PREFIX "" - BUILD_WITH_INSTALL_RPATH 1 - INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list '${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp'" - ) - -# We use a bunch of deprecated system APIs. - set_source_files_properties( - media_plugin_quicktime.cpp PROPERTIES - COMPILE_FLAGS -Wno-deprecated-declarations - ) - find_library(CARBON_LIBRARY Carbon) - target_link_libraries(media_plugin_quicktime ${CARBON_LIBRARY}) - endif (DARWIN) -endif (QUICKTIME) - diff --git a/indra/plugins/quicktime/media_plugin_quicktime.cpp b/indra/plugins/quicktime/media_plugin_quicktime.cpp deleted file mode 100644 index 1c15b7483..000000000 --- a/indra/plugins/quicktime/media_plugin_quicktime.cpp +++ /dev/null @@ -1,1112 +0,0 @@ -/** - * @file media_plugin_quicktime.cpp - * @brief QuickTime plugin for LLMedia API plugin system - * - * @cond - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -#include "linden_common.h" - -#include "llgl.h" - -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "media_plugin_base.h" - -#if LL_QUICKTIME_ENABLED - -#if defined(LL_DARWIN) - #include -#elif defined(LL_WINDOWS) - #undef __STDC_CONSTANT_MACROS //Needed, as boost/unordered_map.hpp already defines INT32_C, etc. - #if defined(_MSC_VER) && _MSC_VER >= 1600 - #define _STDINT_H //Visual Studio 2010 includes its own stdint header already - #endif - #include "MacTypes.h" - #include "QTML.h" - #include "Movies.h" - #include "QDoffscreen.h" - #include "FixMath.h" - #include "QTLoadLibraryUtils.h" -#endif - -// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint -//////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginQuickTime : public MediaPluginBase -{ -public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); - ~MediaPluginQuickTime(); - - /* virtual */ void receiveMessage(const char *message_string); - -private: - - int mNaturalWidth; - int mNaturalHeight; - Movie mMovieHandle; - GWorldPtr mGWorldHandle; - ComponentInstance mMovieController; - int mCurVolume; - bool mMediaSizeChanging; - bool mIsLooping; - std::string mMovieTitle; - bool mReceivedTitle; - const int mMinWidth; - const int mMaxWidth; - const int mMinHeight; - const int mMaxHeight; - F64 mPlayRate; - std::string mNavigateURL; - - enum ECommand { - COMMAND_NONE, - COMMAND_STOP, - COMMAND_PLAY, - COMMAND_FAST_FORWARD, - COMMAND_FAST_REWIND, - COMMAND_PAUSE, - COMMAND_SEEK, - }; - ECommand mCommand; - - // Override this to add current time and duration to the message - /*virtual*/ void setDirty(int left, int top, int right, int bottom) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); - - message.setValueS32("left", left); - message.setValueS32("top", top); - message.setValueS32("right", right); - message.setValueS32("bottom", bottom); - - if(mMovieHandle) - { - message.setValueReal("current_time", getCurrentTime()); - message.setValueReal("duration", getDuration()); - message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle))); - } - - sendMessage(message); - } - - - static Rect rectFromSize(int width, int height) - { - Rect result; - - - result.left = 0; - result.top = 0; - result.right = width; - result.bottom = height; - - return result; - } - - Fixed getPlayRate(void) - { - Fixed result; - if(mPlayRate == 0.0f) - { - // Default to the movie's preferred rate - result = GetMoviePreferredRate(mMovieHandle); - if(result == 0) - { - // Don't return a 0 play rate, ever. - std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl; - result = X2Fix(1.0f); - } - } - else - { - result = X2Fix(mPlayRate); - } - - return result; - } - - void load( const std::string url ) - { - - if ( url.empty() ) - return; - - // Stop and unload any existing movie before starting another one. - unload(); - - setStatus(STATUS_LOADING); - - //In case std::string::c_str() makes a copy of the url data, - //make sure there is memory to hold it before allocating memory for handle. - //if fails, NewHandleClear(...) should return NULL. - const char* url_string = url.c_str() ; - Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) ); - - if ( NULL == handle || noErr != MemError() || NULL == *handle ) - { - setStatus(STATUS_ERROR); - return; - } - - BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) ); - - OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType ); - DisposeHandle( handle ); - if ( noErr != err ) - { - setStatus(STATUS_ERROR); - return; - }; - - mNavigateURL = url; - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); - message.setValue("uri", mNavigateURL); - sendMessage(message); - - // do pre-roll actions (typically fired for streaming movies but not always) - PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this ); - - Rect movie_rect = rectFromSize(mWidth, mHeight); - - // make a new movie controller - mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie ); - - // movie controller - MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this ); - - SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize ); - - // function that gets called when a frame is drawn - SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this ); - - setStatus(STATUS_LOADED); - - sizeChanged(); - }; - - bool unload() - { - // new movie and have to get title again - mReceivedTitle = false; - - if ( mMovieHandle ) - { - StopMovie( mMovieHandle ); - if ( mMovieController ) - { - MCMovieChanged( mMovieController, mMovieHandle ); - }; - }; - - if ( mMovieController ) - { - MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this ); - DisposeMovieController( mMovieController ); - mMovieController = NULL; - }; - - if ( mMovieHandle ) - { - SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this ); - DisposeMovie( mMovieHandle ); - mMovieHandle = NULL; - }; - - if ( mGWorldHandle ) - { - DisposeGWorld( mGWorldHandle ); - mGWorldHandle = NULL; - }; - - setStatus(STATUS_NONE); - - return true; - } - - bool navigateTo( const std::string url ) - { - unload(); - load( url ); - - return true; - }; - - bool sizeChanged() - { - if ( ! mMovieHandle ) - return false; - - // Check to see whether the movie's natural size has updated - { - int width, height; - getMovieNaturalSize(&width, &height); - if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight))) - { - mNaturalWidth = width; - mNaturalHeight = height; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); - message.setValue("name", mTextureSegmentName); - message.setValueS32("width", width); - message.setValueS32("height", height); - sendMessage(message); - //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl; - } - } - - // sanitize destination size - Rect dest_rect = rectFromSize(mWidth, mHeight); - - // media depth won't change - int depth_bits = mDepth * 8; - long rowbytes = mDepth * mTextureWidth; - - GWorldPtr old_gworld_handle = mGWorldHandle; - - if(mPixels != NULL) - { - // We have pixels. Set up a GWorld pointing at the texture. - OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes); - if ( noErr != result ) - { - // TODO: unrecoverable?? throw exception? return something? - return false; - } - } - else - { - // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally. - Rect tempRect = rectFromSize(1, 1); - OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0); - if ( noErr != result ) - { - // TODO: unrecoverable?? throw exception? return something? - return false; - } - } - - SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) ); - - // If the GWorld was already set up, delete it. - if(old_gworld_handle != NULL) - { - DisposeGWorld( old_gworld_handle ); - } - - // Set up the movie display matrix - { - // scale movie to fit rect and invert vertically to match opengl image format - MatrixRecord transform; - SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix - double scaleX = (double) mWidth / mNaturalWidth; - double scaleY = -1.0 * (double) mHeight / mNaturalHeight; - double centerX = mWidth / 2.0; - double centerY = mHeight / 2.0; - ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) ); - SetMovieMatrix( mMovieHandle, &transform ); - } - - // update movie controller - if ( mMovieController ) - { - MCSetControllerPort( mMovieController, mGWorldHandle ); - MCPositionController( mMovieController, &dest_rect, &dest_rect, - mcTopLeftMovie | mcPositionDontInvalidate ); - MCMovieChanged( mMovieController, mMovieHandle ); - } - - - // Emit event with size change so the calling app knows about it too - // TODO: - //LLMediaEvent event( this ); - //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event ); - - return true; - } - static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref ) - { - Boolean result = false; - - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - switch( action ) - { - // handle window resizing - case mcActionControllerSizeChanged: - // Ensure that the movie draws correctly at the new size - self->sizeChanged(); - break; - - // Block any movie controller actions that open URLs. - case mcActionLinkToURL: - case mcActionGetNextURL: - case mcActionLinkToURLExtended: - // Prevent the movie controller from handling the message - result = true; - break; - - default: - break; - }; - - return result; - }; - - static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref ) - { - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - // IMPORTANT: typically, a consumer who is observing this event will set a flag - // when this event is fired then render later. Be aware that the media stream - // can change during this period - dimensions, depth, format etc. - //LLMediaEvent event( self ); -// self->updateQuickTime(); - // TODO ^^^ - - - if ( self->mWidth > 0 && self->mHeight > 0 ) - self->setDirty( 0, 0, self->mWidth, self->mHeight ); - - return noErr; - }; - - static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref ) - { - MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; - - // TODO: - //LLMediaEvent event( self ); - //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event ); - - // Send a "navigate complete" event. - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); - message.setValue("uri", self->mNavigateURL); - message.setValueS32("result_code", 200); - message.setValue("result_string", "OK"); - self->sendMessage(message); - }; - - - void rewind() - { - GoToBeginningOfMovie( mMovieHandle ); - MCMovieChanged( mMovieController, mMovieHandle ); - }; - - bool processState() - { - if ( mCommand == COMMAND_PLAY ) - { - if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING || mStatus == STATUS_DONE ) - { - long state = GetMovieLoadState( mMovieHandle ); - - if ( state >= kMovieLoadStatePlaythroughOK ) - { - // if the movie is at the end (generally because it reached it naturally) - // and we play is requested, jump back to the start of the movie. - // note: this is different from having loop flag set. - if ( IsMovieDone( mMovieHandle ) ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - rewind(); - }; - - MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - setStatus(STATUS_PLAYING); - mCommand = COMMAND_NONE; - }; - }; - } - else - if ( mCommand == COMMAND_STOP ) - { - if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED || mStatus == STATUS_DONE ) - { - if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - rewind(); - - setStatus(STATUS_LOADED); - mCommand = COMMAND_NONE; - }; - }; - } - else - if ( mCommand == COMMAND_PAUSE ) - { - if ( mStatus == STATUS_PLAYING ) - { - if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) - { - Fixed rate = X2Fix( 0.0 ); - MCDoAction( mMovieController, mcActionPlay, (void*)rate ); - setStatus(STATUS_PAUSED); - mCommand = COMMAND_NONE; - }; - }; - }; - - return true; - }; - - void play(F64 rate) - { - mPlayRate = rate; - mCommand = COMMAND_PLAY; - }; - - void stop() - { - mCommand = COMMAND_STOP; - }; - - void pause() - { - mCommand = COMMAND_PAUSE; - }; - - void getMovieNaturalSize(int *movie_width, int *movie_height) - { - Rect rect; - - GetMovieNaturalBoundsRect( mMovieHandle, &rect ); - - int width = ( rect.right - rect.left ); - int height = ( rect.bottom - rect.top ); - - // make sure width and height fall in valid range - if ( width < mMinWidth ) - width = mMinWidth; - - if ( width > mMaxWidth ) - width = mMaxWidth; - - if ( height < mMinHeight ) - height = mMinHeight; - - if ( height > mMaxHeight ) - height = mMaxHeight; - - // return the new rect - *movie_width = width; - *movie_height = height; - } - - void updateQuickTime(int milliseconds) - { - if ( ! mMovieHandle ) - return; - - if ( ! mMovieController ) - return; - - // this wasn't required in 1.xx viewer but we have to manually - // work the Windows message pump now - #if defined( LL_WINDOWS ) - MSG msg; - while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) - { - GetMessage( &msg, NULL, 0, 0 ); - TranslateMessage( &msg ); - DispatchMessage( &msg ); - }; - #endif - - MCIdle( mMovieController ); - - if ( ! mGWorldHandle ) - return; - - if ( mMediaSizeChanging ) - return; - - // update state machine - processState(); - - // see if title arrived and if so, update member variable with contents - checkTitle(); - - // QT call to see if we are at the end - can't do with controller - if ( IsMovieDone( mMovieHandle ) ) - { - // special code for looping - need to rewind at the end of the movie - if ( mIsLooping ) - { - // go back to start - rewind(); - - if ( mMovieController ) - { - // kick off new play - MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); - - // set the volume - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - }; - } - else - { - if(mStatus == STATUS_PLAYING) - { - setStatus(STATUS_DONE); - } - } - } - - }; - - int getDataWidth() const - { - if ( mGWorldHandle ) - { - int depth = mDepth; - - if (depth < 1) - depth = 1; - - // ALWAYS use the row bytes from the PixMap if we have a GWorld because - // sometimes it's not the same as mMediaDepth * mMediaWidth ! - PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle ); - return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth; - } - else - { - // TODO : return LLMediaImplCommon::getaDataWidth(); - return 0; - } - }; - - void seek( F64 time ) - { - if ( mMovieController ) - { - TimeRecord when; - when.scale = GetMovieTimeScale( mMovieHandle ); - when.base = 0; - - // 'time' is in (floating point) seconds. The timebase time will be in 'units', where - // there are 'scale' units per second. - SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) ); - - when.value.hi = ( SInt32 )( raw_time >> 32 ); - when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) ); - - MCDoAction( mMovieController, mcActionGoToTime, &when ); - }; - }; - - F64 getLoadedDuration() - { - TimeValue duration; - if(GetMaxLoadedTimeInMovie( mMovieHandle, &duration ) != noErr) - { - // If GetMaxLoadedTimeInMovie returns an error, return the full duration of the movie. - duration = GetMovieDuration( mMovieHandle ); - } - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)duration / (F64)scale; - }; - - F64 getDuration() - { - TimeValue duration = GetMovieDuration( mMovieHandle ); - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)duration / (F64)scale; - }; - - F64 getCurrentTime() - { - TimeValue curr_time = GetMovieTime( mMovieHandle, 0 ); - TimeValue scale = GetMovieTimeScale( mMovieHandle ); - - return (F64)curr_time / (F64)scale; - }; - - void setVolume( F64 volume ) - { - mCurVolume = (short)(volume * ( double ) 0x100 ); - - if ( mMovieController ) - { - MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); - }; - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void update(int milliseconds = 0) - { - updateQuickTime(milliseconds); - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseDown( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseUp( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void mouseMove( int x, int y ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // - void keyPress( unsigned char key ) - { - }; - - //////////////////////////////////////////////////////////////////////////////// - // Grab movie title into mMovieTitle - should be called repeatedly - // until it returns true since movie title takes a while to become - // available. - const bool getMovieTitle() - { - // grab meta data from movie - QTMetaDataRef media_data_ref; - OSErr result = QTCopyMovieMetaData( mMovieHandle, &media_data_ref ); - if ( noErr != result ) - return false; - - // look up "Display Name" in meta data - OSType meta_data_key = kQTMetaDataCommonKeyDisplayName; - QTMetaDataItem item = kQTMetaDataItemUninitialized; - result = QTMetaDataGetNextItem( media_data_ref, kQTMetaDataStorageFormatWildcard, - 0, kQTMetaDataKeyFormatCommon, - (const UInt8 *)&meta_data_key, - sizeof( meta_data_key ), &item ); - if ( noErr != result ) - return false; - - // find the size of the title - ByteCount size; - result = QTMetaDataGetItemValue( media_data_ref, item, NULL, 0, &size ); - if ( noErr != result || size <= 0 /*|| size > 1024 FIXME: arbitrary limit */ ) - return false; - - // allocate some space and grab it - UInt8* item_data = new UInt8[ size + 1 ]; - memset( item_data, 0, ( size + 1 ) * sizeof( UInt8 ) ); - result = QTMetaDataGetItemValue( media_data_ref, item, item_data, size, NULL ); - if ( noErr != result ) - { - delete [] item_data; - return false; - }; - - // save it - if ( strlen( (char*)item_data ) ) - mMovieTitle = std::string( (char* )item_data ); - else - mMovieTitle = ""; - - // clean up - delete [] item_data; - - return true; - }; - - // called regularly to see if title changed - void checkTitle() - { - // we did already receive title so keep checking - if ( ! mReceivedTitle ) - { - // grab title from movie meta data - if ( getMovieTitle() ) - { - // pass back to host application - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); - message.setValue("name", mMovieTitle ); - sendMessage( message ); - - // stop looking once we find a title for this movie. - // TODO: this may to be reset if movie title changes - // during playback but this is okay for now - mReceivedTitle = true; - }; - }; - }; -}; - -MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction send_message_function, - LLPluginInstance* plugin_instance) : - MediaPluginBase(send_message_function, plugin_instance), - mMinWidth( 0 ), - mMaxWidth( 2048 ), - mMinHeight( 0 ), - mMaxHeight( 2048 ) -{ -// std::cerr << "MediaPluginQuickTime constructor" << std::endl; - - mNaturalWidth = -1; - mNaturalHeight = -1; - mMovieHandle = 0; - mGWorldHandle = 0; - mMovieController = 0; - mCurVolume = 0x99; - mMediaSizeChanging = false; - mIsLooping = false; - mMovieTitle = std::string(); - mReceivedTitle = false; - mCommand = COMMAND_NONE; - mPlayRate = 0.0f; - mStatus = STATUS_NONE; -} - -MediaPluginQuickTime::~MediaPluginQuickTime() -{ -// std::cerr << "MediaPluginQuickTime destructor" << std::endl; - - ExitMovies(); - -#ifdef LL_WINDOWS - TerminateQTML(); -// std::cerr << "QuickTime closing down" << std::endl; -#endif -} - - -void MediaPluginQuickTime::receiveMessage(const char *message_string) -{ -// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl; - LLPluginMessage message_in; - - if(message_in.parse(message_string) >= 0) - { - std::string message_class = message_in.getClass(); - std::string message_name = message_in.getName(); - if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) - { - if(message_name == "init") - { - LLPluginMessage message("base", "init_response"); - LLSD versions = LLSD::emptyMap(); - versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; - // Normally a plugin would only specify one of these two subclasses, but this is a demo... - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; - message.setValueLLSD("versions", versions); - - #ifdef LL_WINDOWS - - // QuickTime 7.6.4 has an issue (that was not present in 7.6.2) with initializing QuickTime - // according to this article: http://lists.apple.com/archives/QuickTime-API/2009/Sep/msg00097.html - // The solution presented there appears to work. - QTLoadLibrary("qtcf.dll"); - - // main initialization for QuickTime - only required on Windows - OSErr result = InitializeQTML( 0L ); - if ( result != noErr ) - { - //TODO: If no QT on Windows, this fails - respond accordingly. - } - else - { - //std::cerr << "QuickTime initialized" << std::endl; - }; - #endif - - // required for both Windows and Mac - EnterMovies(); - - std::string plugin_version = "QuickTime media plugin, QuickTime version "; - - long version = 0; - Gestalt( gestaltQuickTimeVersion, &version ); - std::ostringstream codec( "" ); - codec << std::hex << version << std::dec; - plugin_version += codec.str(); - message.setValue("plugin_version", plugin_version); - sendMessage(message); - } - else if(message_name == "idle") - { - // no response is necessary here. - F64 time = message_in.getValueReal("time"); - - // Convert time to milliseconds for update() - update((int)(time * 1000.0f)); - } - else if(message_name == "cleanup") - { - // TODO: clean up here - } - else if(message_name == "shm_added") - { - SharedSegmentInfo info; - info.mAddress = message_in.getValuePointer("address"); - info.mSize = (size_t)message_in.getValueS32("size"); - std::string name = message_in.getValue("name"); -// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name -// << ", size: " << info.mSize -// << ", address: " << info.mAddress -// << std::endl; - - mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); - - } - else if(message_name == "shm_remove") - { - std::string name = message_in.getValue("name"); - -// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl; - - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - if(mPixels == iter->second.mAddress) - { - // This is the currently active pixel buffer. Make sure we stop drawing to it. - mPixels = NULL; - mTextureSegmentName.clear(); - - // Make sure the movie GWorld is no longer pointed at the shared segment. - sizeChanged(); - } - mSharedSegments.erase(iter); - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl; - } - - // Send the response so it can be cleaned up. - LLPluginMessage message("base", "shm_remove_response"); - message.setValue("name", name); - sendMessage(message); - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl; - } - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) - { - if(message_name == "init") - { - // This is the media init message -- all necessary data for initialization should have been received. - - // Plugin gets to decide the texture parameters to use. - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); - #if defined(LL_WINDOWS) - // Values for Windows - mDepth = 3; - message.setValueU32("format", GL_RGB); - message.setValueU32("type", GL_UNSIGNED_BYTE); - - // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even. - // Padding to a multiple of 3*32 guarantees it'll divide out properly. - message.setValueU32("padding", 32 * 3); - #else - // Values for Mac - mDepth = 4; - message.setValueU32("format", GL_BGRA_EXT); - #ifdef __BIG_ENDIAN__ - message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV ); - #else - message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8); - #endif - - // Pad texture width to a multiple of 32 bytes, to line up with cache lines. - message.setValueU32("padding", 32); - #endif - message.setValueS32("depth", mDepth); - message.setValueU32("internalformat", GL_RGB); - message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. - message.setValueBoolean("allow_downsample", true); - sendMessage(message); - } - else if(message_name == "size_change") - { - std::string name = message_in.getValue("name"); - S32 width = message_in.getValueS32("width"); - S32 height = message_in.getValueS32("height"); - S32 texture_width = message_in.getValueS32("texture_width"); - S32 texture_height = message_in.getValueS32("texture_height"); - - //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); - message.setValue("name", name); - message.setValueS32("width", width); - message.setValueS32("height", height); - message.setValueS32("texture_width", texture_width); - message.setValueS32("texture_height", texture_height); - sendMessage(message); - - if(!name.empty()) - { - // Find the shared memory region with this name - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { -// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl; -// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl; - - mPixels = (unsigned char*)iter->second.mAddress; - mTextureSegmentName = name; - mWidth = width; - mHeight = height; - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - - mMediaSizeChanging = false; - - sizeChanged(); - - update(); - }; - }; - } - else if(message_name == "load_uri") - { - std::string uri = message_in.getValue("uri"); - load( uri ); - sendStatus(); - } - else if(message_name == "mouse_event") - { - std::string event = message_in.getValue("event"); - S32 x = message_in.getValueS32("x"); - S32 y = message_in.getValueS32("y"); - - if(event == "down") - { - mouseDown(x, y); - } - else if(event == "up") - { - mouseUp(x, y); - } - else if(event == "move") - { - mouseMove(x, y); - }; - }; - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) - { - if(message_name == "stop") - { - stop(); - } - else if(message_name == "start") - { - F64 rate = 0.0; - if(message_in.hasValue("rate")) - { - rate = message_in.getValueReal("rate"); - } - play(rate); - } - else if(message_name == "pause") - { - pause(); - } - else if(message_name == "seek") - { - F64 time = message_in.getValueReal("time"); - seek(time); - } - else if(message_name == "set_loop") - { - bool loop = message_in.getValueBoolean("loop"); - mIsLooping = loop; - } - else if(message_name == "set_volume") - { - F64 volume = message_in.getValueReal("volume"); - setVolume(volume); - } - } - else - { -// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl; - }; - }; -} - -int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) -{ - *plugin_object = new MediaPluginQuickTime(send_message_function, plugin_instance); - return 0; -} - -#else // LL_QUICKTIME_ENABLED - -// Stubbed-out class with constructor/destructor (necessary or windows linker -// will just think its dead code and optimize it all out) -class MediaPluginQuickTime : public MediaPluginBase -{ -public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); - ~MediaPluginQuickTime(); - /* virtual */ void receiveMessage(const char *message_string); -}; - -MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction send_message_function, - LLPluginInstance* plugin_instance) : - MediaPluginBase(send_message_function, plugin_instance) -{ - // no-op -} - -MediaPluginQuickTime::~MediaPluginQuickTime() -{ - // no-op -} - -void MediaPluginQuickTime::receiveMessage(const char *message_string) -{ - // no-op -} - -// We're building without quicktime enabled. Just refuse to initialize. -int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) -{ - return -1; -} - -#endif // LL_QUICKTIME_ENABLED From f2beb4443abeb75a77cbe09548d13fbab3c5c7b9 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 16:52:40 -0400 Subject: [PATCH 27/91] This should fix Linux, thanks Drake. --- indra/cmake/BuildPackagesInfo.cmake | 2 + indra/cmake/CMakeLists.txt | 1 - indra/cmake/LLAddBuildTest.cmake | 11 - indra/cmake/LLSharedLibs.cmake | 56 +++-- indra/cmake/RunBuildTest.cmake | 20 -- indra/cmake/run_build_test.py | 340 ++++++++++++++++++++++++++++ 6 files changed, 374 insertions(+), 56 deletions(-) delete mode 100644 indra/cmake/RunBuildTest.cmake create mode 100644 indra/cmake/run_build_test.py diff --git a/indra/cmake/BuildPackagesInfo.cmake b/indra/cmake/BuildPackagesInfo.cmake index 6b2410ea5..64a353ba2 100644 --- a/indra/cmake/BuildPackagesInfo.cmake +++ b/indra/cmake/BuildPackagesInfo.cmake @@ -14,5 +14,7 @@ add_custom_command(OUTPUT packages-info.txt DEPENDS ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py ${CMAKE_SOURCE_DIR}/../autobuild.xml COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/cmake/run_build_test.py -DAUTOBUILD_ADDRSIZE=${ADDRESS_SIZE} + ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py "${VIEWER_CHANNEL}" "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}" > packages-info.txt ) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 215f7a300..932730441 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -85,7 +85,6 @@ set(cmake_SOURCE_FILES Prebuilt.cmake PulseAudio.cmake Python.cmake - RunBuildTest.cmake StateMachine.cmake TemplateCheck.cmake Tut.cmake diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 290db9f22..7875e5bfc 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -105,17 +105,6 @@ MACRO(ADD_BUILD_TEST_INTERNAL name parent libraries source_files) IF (NOT "$ENV{LD_LIBRARY_PATH}" STREQUAL "") SET(LD_LIBRARY_PATH "$ENV{LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}") ENDIF (NOT "$ENV{LD_LIBRARY_PATH}" STREQUAL "") - ADD_CUSTOM_COMMAND( - OUTPUT ${TEST_OUTPUT} - COMMAND - ${CMAKE_COMMAND} - "-DLD_LIBRARY_PATH=${LD_LIBRARY_PATH}" - "-DTEST_CMD:STRING=${TEST_CMD}" - -P ${CMAKE_SOURCE_DIR}/cmake/RunBuildTest.cmake - DEPENDS ${name}_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - VERBATIM - ) ADD_CUSTOM_TARGET(${name}_test_ok ALL DEPENDS ${TEST_OUTPUT}) IF (${parent}) diff --git a/indra/cmake/LLSharedLibs.cmake b/indra/cmake/LLSharedLibs.cmake index 4ba1d4c02..f69b45cd9 100644 --- a/indra/cmake/LLSharedLibs.cmake +++ b/indra/cmake/LLSharedLibs.cmake @@ -1,32 +1,40 @@ # ll_deploy_sharedlibs_command # target_exe: the cmake target of the executable for which the shared libs will be deployed. macro(ll_deploy_sharedlibs_command target_exe) - set(OUTPUT_PATH $) + set(TARGET_LOCATION $) + get_filename_component(OUTPUT_PATH ${TARGET_LOCATION} PATH) - if(DARWIN) - SET_TEST_PATH(SEARCH_DIRS) - get_target_property(IS_BUNDLE ${target_exe} MACOSX_BUNDLE) - if(IS_BUNDLE) - set(OUTPUT_PATH ${OUTPUT_PATH}/../Resources) - endif(IS_BUNDLE) - elseif(WINDOWS) - SET_TEST_PATH(SEARCH_DIRS) - LIST(APPEND SEARCH_DIRS "$ENV{SystemRoot}/system32") - elseif(LINUX) - SET_TEST_PATH(SEARCH_DIRS) - set(OUTPUT_PATH ${OUTPUT_PATH}/lib) - endif(DARWIN) + # It's not clear that this does anything useful for us on Darwin. It has + # been broken for some time now; the BIN_NAME was being constructed as a + # ridiculous nonexistent path with duplicated segments. Fixing that only + # produces ominous spammy warnings: at the time the command below is run, we + # have not yet populated the nested mac-crash-logger.app/Contents/Resources + # with the .dylibs with which it was linked. Moreover, the form of the + # embedded @executable_path/../Resources/mumble.dylib pathname confuses the + # GetPrerequisites.cmake tool invoked by DeploySharedLibs.cmake. It seems + # clear that we have long since accomplished by other means what this was + # originally supposed to do. Skipping it only eliminates an annoying + # non-fatal error. + if(NOT DARWIN) + if(WINDOWS) + SET_TEST_PATH(SEARCH_DIRS) + LIST(APPEND SEARCH_DIRS "$ENV{SystemRoot}/system32") + elseif(LINUX) + SET_TEST_PATH(SEARCH_DIRS) + set(OUTPUT_PATH ${OUTPUT_PATH}/lib) + endif(WINDOWS) - add_custom_command( - TARGET ${target_exe} POST_BUILD - COMMAND ${CMAKE_COMMAND} - ARGS - "-DBIN_NAME=\"$\"" - "-DSEARCH_DIRS=\"${SEARCH_DIRS}\"" - "-DDST_PATH=\"${OUTPUT_PATH}\"" - "-P" - "${CMAKE_SOURCE_DIR}/cmake/DeploySharedLibs.cmake" - ) + add_custom_command( + TARGET ${target_exe} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + "-DBIN_NAME=\"${TARGET_LOCATION}\"" + "-DSEARCH_DIRS=\"${SEARCH_DIRS}\"" + "-DDST_PATH=\"${OUTPUT_PATH}\"" + "-P" + "${CMAKE_SOURCE_DIR}/cmake/DeploySharedLibs.cmake" + ) + endif(NOT DARWIN) endmacro(ll_deploy_sharedlibs_command) diff --git a/indra/cmake/RunBuildTest.cmake b/indra/cmake/RunBuildTest.cmake deleted file mode 100644 index dc6c942b7..000000000 --- a/indra/cmake/RunBuildTest.cmake +++ /dev/null @@ -1,20 +0,0 @@ -#This cmake script is meant to be run as a build time custom command. -#The script is run using cmake w/ the -P option. -# parameters are passed to this scripts execution with the -D option. -# A full command line would look like this: -# cmake -D LD_LIBRARY_PATH=~/checkout/libraries -D TEST_CMD=./llunit_test -D ARGS=--touch=llunit_test_ok.txt -P RunBuildTest.cmake - -# Parameters: -# LD_LIBRARY_PATH: string, What to set the LD_LIBRARY_PATH env var. -# TEST_CMD: string list, command to run the unit test with, followed by its args. -set(ENV{LD_LIBRARY_PATH} ${LD_LIBRARY_PATH}) -#message("Running: ${TEST_CMD}") -execute_process( - COMMAND ${TEST_CMD} - RESULT_VARIABLE RES - ) - -if(NOT ${RES} STREQUAL 0) - message(STATUS "Failure running: ${TEST_CMD}") - message(FATAL_ERROR "Error: ${RES}") -endif(NOT ${RES} STREQUAL 0) \ No newline at end of file diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py new file mode 100644 index 000000000..210e43b23 --- /dev/null +++ b/indra/cmake/run_build_test.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +"""\ +@file run_build_test.py +@author Nat Goodspeed +@date 2009-09-03 +@brief Helper script to allow CMake to run some command after setting + environment variables. + +CMake has commands to run an external program. But remember that each CMake +command must be backed by multiple build-system implementations. Unfortunately +it seems CMake can't promise that every target build system can set specified +environment variables before running the external program of interest. + +This helper script is a workaround. It simply sets the requested environment +variables and then executes the program specified on the rest of its command +line. + +Example: + +python run_build_test.py -DFOO=bar myprog somearg otherarg + +sets environment variable FOO=bar, then runs: +myprog somearg otherarg + +$LicenseInfo:firstyear=2009&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2009-2010, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +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 os +import sys +import errno +import HTMLParser +import re +import signal +import subprocess +import logging + +def main(command, arguments=[], libpath=[], vars={}): + """Pass: + command is the command to be executed + + argument is a sequence (e.g. a list) of strings to be passed to command + + libpath is a sequence of directory pathnames. These will be appended to + the platform-specific dynamic library search path environment variable. + + vars is a dict of arbitrary (var, value) pairs to be added to the + environment before running 'command'. + + This function runs the specified command, waits for it to terminate and + returns its return code. This will be negative if the command terminated + with a signal, else it will be the process's specified exit code. + """ + # Handle platform-dependent libpath first. + if sys.platform == "win32": + lpvars = ["PATH"] + elif sys.platform == "darwin": + lpvars = ["LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"] + elif sys.platform.startswith("linux"): + lpvars = ["LD_LIBRARY_PATH"] + else: + # No idea what the right pathname might be! But only crump if this + # feature is requested. + if libpath: + raise RuntimeError("run_build_test: unknown platform %s" % sys.platform) + lpvars = [] + for var in lpvars: + # Split the existing path. Bear in mind that the variable in question + # might not exist; instead of KeyError, just use an empty string. + dirs = os.environ.get(var, "").split(os.pathsep) + # Append the sequence in libpath + log.info("%s += %r" % (var, libpath)) + for dir in libpath: + # append system paths at the end + if dir in ('/lib', '/usr/lib'): + dirs.append(dir) + # prepend non-system paths + else: + dirs.insert(0, dir) + + # Filter out some useless pieces + clean_dirs = [] + for dir in dirs: + if dir and dir not in ('', '.'): + clean_dirs.append(dir) + + # Now rebuild the path string. This way we use a minimum of separators + # -- and we avoid adding a pointless separator when libpath is empty. + os.environ[var] = os.pathsep.join(clean_dirs) + log.info("%s = %r" % (var, os.environ[var])) + # Now handle arbitrary environment variables. The tricky part is ensuring + # that all the keys and values we try to pass are actually strings. + if vars: + log.info("Setting: %s" % ("\n".join(["%s=%s" % (key, value) for key, value in vars.iteritems()]))) + os.environ.update(dict([(str(key), str(value)) for key, value in vars.iteritems()])) + # Run the child process. + command_list = [command] + command_list.extend(arguments) + log.info("Running: %s" % " ".join(command_list)) + # Make sure we see all relevant output *before* child-process output. + sys.stdout.flush() + try: + return subprocess.call(command_list) + except OSError as err: + # If the caller is trying to execute a test program that doesn't + # exist, we want to produce a reasonable error message rather than a + # traceback. This happens when the build is halted by errors, but + # CMake tries to proceed with testing anyway . However, do + # NOT attempt to handle any error but "doesn't exist." + if err.errno != errno.ENOENT: + raise + # In practice, the pathnames into CMake's build tree are so long as to + # obscure the name of the test program. Just log its basename. + log.warn("No such program %s; check for preceding build errors" % \ + os.path.basename(command[0])) + # What rc should we simulate for missing executable? Windows produces + # 9009. + return 9009 + +# swiped from vita, sigh, seems like a Bad Idea to introduce dependency +def translate_rc(rc): + """ + Accept an rc encoded as for subprocess.Popen.returncode: + None means still running + int >= 0 means terminated voluntarily with specified rc + int < 0 means terminated by signal (-rc) + + Return a string explaining the outcome. In case of a signal, try to + name the corresponding symbol from the 'signal' module. + """ + if rc is None: + return "still running" + + if rc >= 0: + return "terminated with rc %s" % rc + + if sys.platform.startswith("win"): + # From http://stackoverflow.com/questions/20629027/process-finished-with-exit-code-1073741571 + # [-1073741571] is the signed integer representation of Microsoft's + # "stack overflow/stack exhaustion" error code 0xC00000FD. + # Anytime you see strange, large negative exit codes in windows, convert + # them to hex and then look them up in the ntstatus error codes + # http://msdn.microsoft.com/en-us/library/cc704588.aspx + + # Python bends over backwards to give you all the integer precision + # you need, avoiding truncation. But only with 32-bit signed ints is + # -1073741571 equivalent to 0xC00000FD! Explicitly truncate before + # converting. + hexrc = "0x%X" % (rc & 0xFFFFFFFF) + # At this point, we're only trying to format the rc to make it easier + # for a human being to understand. Any exception here -- file doesn't + # exist, HTML parsing error, unrecognized table structure, unknown key + # -- should NOT kill the script! It should only cause us to shrug and + # present our caller with the best information available. + try: + table = get_windows_table() + symbol, desc = table[hexrc] + except Exception, err: + log.error("(%s -- carrying on)" % err) + log.error("terminated with rc %s (%s)" % (rc, hexrc)) + else: + log.info("terminated with rc %s: %s: %s" % (hexrc, symbol, desc)) + + else: + # On Posix, negative rc means the child was terminated by signal -rc. + rc = -rc + for attr in dir(signal): + if attr.startswith('SIG') and getattr(signal, attr) == rc: + strc = attr + break + else: + strc = str(rc) + return "terminated by signal %s" % strc + +class TableParser(HTMLParser.HTMLParser): + """ + This HTMLParser subclass is designed to parse the table we know exists + in windows-rcs.html, hopefully without building in too much knowledge of + the specific way that table is currently formatted. + """ + # regular expression matching any string containing only whitespace + whitespace = re.compile(r'\s*$') + + def __init__(self): + # Because Python 2.x's HTMLParser is an old-style class, we must use + # old-style syntax to forward the __init__() call -- not super(). + HTMLParser.HTMLParser.__init__(self) + # this will collect all the data, eventually + self.table = [] + # Stack whose top (last item) indicates where to append current + # element data. When empty, don't collect data at all. + self.dest = [] + + def handle_starttag(self, tag, attrs): + if tag == "table": + # This is the outermost tag we recognize. Collect nested elements + # within self.table. + self.dest.append(self.table) + elif tag in ("tr", "td"): + # Nested elements whose contents we want to capture as sublists. + # To the list currently designated by the top of the dest stack, + # append a new empty sublist. + self.dest[-1].append([]) + # Now push THAT new, empty list as the new top of the dest stack. + self.dest.append(self.dest[-1][-1]) + elif tag == "p": + # We could handle

...

just like or , but that + # introduces an unnecessary extra level of nesting. Just skip. + pass + else: + # For any tag we don't recognize (notably ), push a new, empty + # list to the top of the dest stack. This new list is NOT + # referenced by anything in self.table; thus, when we pop it, any + # data we've collected inside that list will be discarded. + self.dest.append([]) + + def handle_endtag(self, tag): + # Because we avoid pushing self.dest for

in handle_starttag(), we + # must refrain from popping it for

here. + if tag != "p": + # For everything else, including unrecognized tags, pop the dest + # stack, reverting to outer collection. + self.dest.pop() + + def handle_startendtag(self, tag, attrs): + # The table of interest contains entries of the form: + #

0x00000000
STATUS_SUCCESS

+ # The
is very useful -- we definitely want two different data + # items for "0x00000000" and "STATUS_SUCCESS" -- but we don't need or + # want it to push, then discard, an empty list as it would if we let + # the default HTMLParser.handle_startendtag() call handle_starttag() + # followed by handle_endtag(). Just ignore
or any other + # singleton tag. + pass + + def handle_data(self, data): + # Outside the of interest, self.dest is empty. Do not bother + # collecting data when self.dest is empty. + # HTMLParser calls handle_data() with every chunk of whitespace + # between tags. That would be lovely if our eventual goal was to + # reconstitute the original input stream with its existing formatting, + # but for us, whitespace only clutters the table. Ignore it. + if self.dest and not self.whitespace.match(data): + # Here we're within our
and we have non-whitespace data. + # Append it to the list designated by the top of the dest stack. + self.dest[-1].append(data) + +# cache for get_windows_table() +_windows_table = None + +def get_windows_table(): + global _windows_table + # If we already loaded _windows_table, no need to load it all over again. + if _windows_table: + return _windows_table + + # windows-rcs.html was fetched on 2015-03-24 with the following command: + # curl -o windows-rcs.html \ + # https://msdn.microsoft.com/en-us/library/cc704588.aspx + parser = TableParser() + with open(os.path.join(os.path.dirname(__file__), "windows-rcs.html")) as hf: + # We tried feeding the file data to TableParser in chunks, to avoid + # buffering the entire file as a single string. Unfortunately its + # handle_data() cannot tell the difference between distinct calls + # separated by HTML tags, and distinct calls necessitated by a chunk + # boundary. Sigh! Read in the whole file. At the time this was + # written, it was only 500KB anyway. + parser.feed(hf.read()) + parser.close() + table = parser.table + + # With our parser, any row leaves a table entry + # consisting only of an empty list. Remove any such. + while table and not table[0]: + table.pop(0) + + # We expect rows of the form: + # [['0x00000000', 'STATUS_SUCCESS'], + # ['The operation completed successfully.']] + # The latter list will have multiple entries if Microsoft embedded
+ # or

...

in the text, in which case joining with '\n' is + # appropriate. + # Turn that into a dict whose key is the hex string, and whose value is + # the pair (symbol, desc). + _windows_table = dict((key, (symbol, '\n'.join(desc))) + for (key, symbol), desc in table) + + return _windows_table + +log=logging.getLogger(__name__) +logging.basicConfig() + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("-d", "--debug", dest="loglevel", action="store_const", + const=logging.DEBUG, default=logging.INFO) + parser.add_argument("-D", "--define", dest="vars", default=[], action="append", + metavar="VAR=value", + help="Add VAR=value to the env variables defined") + parser.add_argument("-l", "--libpath", dest="libpath", default=[], action="append", + metavar="DIR", + help="Add DIR to the platform-dependent DLL search path") + parser.add_argument("command") + parser.add_argument('args', nargs=argparse.REMAINDER) + args = parser.parse_args() + + log.setLevel(args.loglevel) + + # What we have in opts.vars is a list of strings of the form "VAR=value" + # or possibly just "VAR". What we want is a dict. We can build that dict by + # constructing a list of ["VAR", "value"] pairs -- so split each + # "VAR=value" string on the '=' sign (but only once, in case we have + # "VAR=some=user=string"). To handle the case of just "VAR", append "" to + # the list returned by split(), then slice off anything after the pair we + # want. + rc = main(command=args.command, arguments=args.args, libpath=args.libpath, + vars=dict([(pair.split('=', 1) + [""])[:2] for pair in args.vars])) + if rc not in (None, 0): + log.error("Failure running: %s" % " ".join([args.command] + args.args)) + log.error("Error %s: %s" % (rc, translate_rc(rc))) + sys.exit((rc < 0) and 255 or rc) From 564410ac7e6f9939c68b427f1968588101913ab2 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 17:29:55 -0400 Subject: [PATCH 28/91] Fix some warnings and errors --- indra/cmake/Copy3rdPartyLibs.cmake | 34 +++++++++++++++-------------- indra/llcommon/llstring.cpp | 2 +- indra/llinventory/llsettingssky.cpp | 10 ++++----- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 4c9a830f0..f541578be 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -6,6 +6,8 @@ include(CMakeCopyIfDifferent) include(Linking) +include(Variables) +include(LLCommon) ################################################################### # set up platform specific lists of files that need to be copied @@ -46,7 +48,7 @@ if(WINDOWS) libhunspell.dll ) - if(WORD_SIZE STREQUAL 64) + if(ADDRESS_SIZE EQUAL 64) list(APPEND debug_files libcrypto-1_1-x64.dll libssl-1_1-x64.dll @@ -55,7 +57,7 @@ if(WINDOWS) libcrypto-1_1-x64.dll libssl-1_1-x64.dll ) - else(WORD_SIZE STREQUAL 64) + else(ADDRESS_SIZE EQUAL 64) list(APPEND debug_files libcrypto-1_1.dll libssl-1_1.dll @@ -64,7 +66,7 @@ if(WINDOWS) libcrypto-1_1.dll libssl-1_1.dll ) - endif(WORD_SIZE STREQUAL 64) + endif(ADDRESS_SIZE EQUAL 64) if(NOT DISABLE_TCMALLOC) set(debug_files ${debug_files} libtcmalloc_minimal-debug.dll) @@ -72,14 +74,15 @@ if(WINDOWS) endif(NOT DISABLE_TCMALLOC) if (FMODSTUDIO) - if(WORD_SIZE STREQUAL 64) + if(ADDRESS_SIZE STREQUAL 64) set(debug_files ${debug_files} fmodL64.dll) set(release_files ${release_files} fmod64.dll) - else(WORD_SIZE STREQUAL 64) + else(ADDRESS_SIZE STREQUAL 64) set(debug_files ${debug_files} fmodL.dll) set(release_files ${release_files} fmod.dll) - endif(WORD_SIZE STREQUAL 64) + endif(ADDRESS_SIZE STREQUAL 64) endif (FMODSTUDIO) + elseif(DARWIN) set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug/Resources") set(SHARED_LIB_STAGING_DIR_RELWITHDEBINFO "${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/Resources") @@ -113,8 +116,8 @@ elseif(DARWIN) ) if (FMODSTUDIO) - set(debug_files ${debug_files} libfmodL.dylib) - set(release_files ${release_files} libfmod.dylib) + list(APPEND debug_files libfmodL.dylib) + list(APPEND release_files libfmod.dylib) endif (FMODSTUDIO) elseif(LINUX) @@ -132,7 +135,6 @@ elseif(LINUX) libvivoxplatform.so libvivoxsdk.so SLVoice - # ca-bundle.crt #No cert for linux. It is actually still 3.2SDK. ) # *TODO - update this to use LIBS_PREBUILT_DIR and LL_ARCH_DIR variables # or ARCH_PREBUILT_DIRS @@ -221,13 +223,13 @@ set(third_party_targets ${third_party_targets} ${out_targets}) -copy_if_different( - ${debug_src_dir} - "${SHARED_LIB_STAGING_DIR_DEBUG}" - out_targets - ${debug_files} - ) -set(third_party_targets ${third_party_targets} ${out_targets}) +#copy_if_different( +# ${debug_src_dir} +# "${SHARED_LIB_STAGING_DIR_DEBUG}" +# out_targets +# ${debug_files} +# ) +#set(third_party_targets ${third_party_targets} ${out_targets}) copy_if_different( ${release_src_dir} diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 4e8649503..f01615cc2 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -809,7 +809,7 @@ wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page w_out[real_output_str_len] = 0; return w_out; - return {&w_out[0]}; +// return {&w_out[0]}; } S32 wchartchars_to_llwchar(const std::wstring::value_type* inchars, llwchar* outchar) diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 0eac6294e..8b93ebc00 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -719,9 +719,9 @@ LLSD LLSettingsSky::defaults(const LLSettingsBase::TrackPosition& position) moonquat = convert_azimuth_and_altitude_to_quat(altitude + (F_PI * 0.125f), azimuth + (F_PI * 0.125f)); // Magic constants copied form dfltsetting.xml - dfltsetting[SETTING_CLOUD_COLOR] = LLColor4(0.4099, 0.4099, 0.4099, 0.0).getValue(); - dfltsetting[SETTING_CLOUD_POS_DENSITY1] = LLColor4(1.0000, 0.5260, 1.0000, 0.0).getValue(); - dfltsetting[SETTING_CLOUD_POS_DENSITY2] = LLColor4(1.0000, 0.5260, 1.0000, 0.0).getValue(); + dfltsetting[SETTING_CLOUD_COLOR] = LLColor4(0.4099f, 0.4099f, 0.4099f, 0.0f).getValue(); + dfltsetting[SETTING_CLOUD_POS_DENSITY1] = LLColor4(1.0000f, 0.5260f, 1.0000f, 0.0f).getValue(); + dfltsetting[SETTING_CLOUD_POS_DENSITY2] = LLColor4(1.0000f, 0.5260f, 1.0000f, 0.0f).getValue(); dfltsetting[SETTING_CLOUD_SCALE] = LLSD::Real(0.4199); dfltsetting[SETTING_CLOUD_SCROLL_RATE] = LLSDArray(0.0f)(0.0f); dfltsetting[SETTING_CLOUD_SHADOW] = LLSD::Real(0.2699); @@ -730,14 +730,14 @@ LLSD LLSettingsSky::defaults(const LLSettingsBase::TrackPosition& position) dfltsetting[SETTING_DOME_OFFSET] = LLSD::Real(0.96f); dfltsetting[SETTING_DOME_RADIUS] = LLSD::Real(15000.f); dfltsetting[SETTING_GAMMA] = LLSD::Real(1.0); - dfltsetting[SETTING_GLOW] = LLColor4(5.000, 0.0010, -0.4799, 1.0).getValue(); + dfltsetting[SETTING_GLOW] = LLColor4(5.000f, 0.0010f, -0.4799f, 1.0f).getValue(); dfltsetting[SETTING_MAX_Y] = LLSD::Real(1605); dfltsetting[SETTING_MOON_ROTATION] = moonquat.getValue(); dfltsetting[SETTING_MOON_BRIGHTNESS] = LLSD::Real(0.5f); dfltsetting[SETTING_STAR_BRIGHTNESS] = LLSD::Real(256.0000); - dfltsetting[SETTING_SUNLIGHT_COLOR] = LLColor4(0.7342, 0.7815, 0.8999, 0.0).getValue(); + dfltsetting[SETTING_SUNLIGHT_COLOR] = LLColor4(0.7342f, 0.7815f, 0.8999f, 0.0f).getValue(); dfltsetting[SETTING_SUN_ROTATION] = sunquat.getValue(); dfltsetting[SETTING_BLOOM_TEXTUREID] = GetDefaultBloomTextureId(); From 8ef3e923e8a8b0a313885b47897ba0c152676af0 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 20:07:46 -0400 Subject: [PATCH 29/91] Allow bare minimum urls in [url text] syntax --- indra/llui/llurlentry.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 8bf38caeb..9c26f23ab 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -41,6 +41,7 @@ //#include "lluicolortable.h" #include "message.h" +#include #include // #define APP_HEADER_REGEX "((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/app)|(secondlife:///app))" @@ -120,11 +121,11 @@ std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const size_t start = 0; while (! isspace(text[start])) { - start++; + ++start; } while (text[start] == ' ' || text[start] == '\t') { - start++; + ++start; } return unescapeUrl(url.substr(start, url.size()-start-1)); } @@ -136,7 +137,7 @@ std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const size_t end = 0; while (! isspace(text[end])) { - end++; + ++end; } return escapeUrl(string.substr(1, end-1)); } @@ -273,7 +274,7 @@ std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const // LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel() { - mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]", + mPattern = boost::regex("\\[(https?://\\S+|\\S+\\.([^\\s<]*)?)[ \t]+[^\\]]+\\]", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -292,7 +293,10 @@ std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const { - return getUrlFromWikiLink(string); + auto url = getUrlFromWikiLink(string); + if (!boost::istarts_with(url, "http")) + return "http://" + url; + return url; } // From d194dfbc09c5bbe95912c71741217ade7609c565 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 19 Apr 2019 23:25:47 -0400 Subject: [PATCH 30/91] Sync with alchemyng for VCRedist update --- autobuild.xml | 46 ------- indra/cmake/CMakeLists.txt | 5 +- indra/cmake/FindVCRedist.cmake | 34 +++++ indra/newview/CMakeLists.txt | 29 ++++- .../installers/windows/installer_template.nsi | 122 ++++++++---------- indra/newview/viewer_manifest.py | 8 ++ 6 files changed, 120 insertions(+), 124 deletions(-) create mode 100644 indra/cmake/FindVCRedist.cmake diff --git a/autobuild.xml b/autobuild.xml index 1ddcabedf..0fb46724d 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2629,52 +2629,6 @@ version 0.8.4 - vcredist - - copyright - Copyright (c) Microsoft Corporation. All rights reserved. - description - The Visual Studio C and C++ runtime redistributable - license - Proprietary - license_file - LICENSES/vcredist.txt - name - vcredist - platforms - - windows - - archive - - hash - f193ec9e594d857d22c067c6e52622a9 - hash_algorithm - md5 - url - https://depot.alchemyviewer.org/pub/windows/lib-vc141/vcredist-14.10.25017.0-windows-201705270440.tar.bz2 - - name - windows - - windows64 - - archive - - hash - e895f4b5d24b372a360c3a8fe3bc0398 - hash_algorithm - md5 - url - https://depot.alchemyviewer.org/pub/windows64/lib-vc142/vcredist-14.12.25810.0-windows64-201712251445.tar.bz2 - - name - windows64 - - - version - 14.12.25810.0 - vlc-bin copyright diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 932730441..0889c38d0 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -18,16 +18,15 @@ set(cmake_SOURCE_FILES CARes.cmake CEFPlugin.cmake CMakeCopyIfDifferent.cmake - CURL.cmake Colladadom.cmake ConfigurePkgConfig.cmake + CURL.cmake Copy3rdPartyLibs.cmake Cwdebug.cmake DBusGlib.cmake DeploySharedLibs.cmake EXPAT.cmake ExamplePlugin.cmake - FMODSTUDIO.cmake FindAPR.cmake FindAutobuild.cmake FindCARes.cmake @@ -39,8 +38,10 @@ set(cmake_SOURCE_FILES FindNDOF.cmake FindOpenJPEG.cmake FindTut.cmake + FindVCRedist.cmake FindURIPARSER.cmake FindXmlRpcEpi.cmake + FMODSTUDIO.cmake FreeType.cmake GLOD.cmake GStreamer010Plugin.cmake diff --git a/indra/cmake/FindVCRedist.cmake b/indra/cmake/FindVCRedist.cmake new file mode 100644 index 000000000..c2cda59cd --- /dev/null +++ b/indra/cmake/FindVCRedist.cmake @@ -0,0 +1,34 @@ +include(Variables) + +if(WINDOWS) + if(MSVC_VERSION GREATER_EQUAL 1919 AND MSVC_VERSION LESS 1920) + set(VISUAL_STUDIO_VERSION 15.0) + set(VISUAL_STUDIO_VERSION_UPPER 16.0) + elseif(MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) + set(VISUAL_STUDIO_VERSION 16.0) + set(VISUAL_STUDIO_VERSION_UPPER 17.0) + endif () + + set(vswhere "$ENV{PROGRAMFILES\(X86\)}\\Microsoft Visual Studio\\Installer\\vswhere.exe") + if(EXISTS ${vswhere}) + execute_process(COMMAND ${vswhere} -version "[${VISUAL_STUDIO_VERSION},${VISUAL_STUDIO_VERSION_UPPER})" -property "installationPath" + OUTPUT_VARIABLE installationPath + OUTPUT_STRIP_TRAILING_WHITESPACE) + file(TO_CMAKE_PATH ${installationPath} installationPath) + set(redistPath "${installationPath}/VC/Redist/MSVC") + file(GLOB redistPath "${installationPath}/VC/Redist/MSVC/*") + list(LENGTH redistPath length) + if(length EQUAL 1) + if(ADDRESS_SIZE EQUAL 64) + set(VISUAL_STUDIO_REDISTRIBUTABLE_NAME "vc_redist.x64.exe" CACHE FILEPATH "Name of Microsoft Visual Studio Redistributable") + set(redistPath "${redistPath}/${VISUAL_STUDIO_REDISTRIBUTABLE_NAME}") + else() + set(VISUAL_STUDIO_REDISTRIBUTABLE_NAME "vc_redist.x86.exe" CACHE FILEPATH "Name of Microsoft Visual Studio Redistributable") + set(redistPath "${redistPath}/${VISUAL_STUDIO_REDISTRIBUTABLE_NAME}") + endif() + if(EXISTS ${redistPath}) + set(VISUAL_STUDIO_REDISTRIBUTABLE_PATH ${redistPath} CACHE FILEPATH "Path to the appropriate Microsoft Visual Studio Redistributable") + endif() + endif() + endif() +endif(WINDOWS) \ No newline at end of file diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 341d75cbd..423c1e686 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -9,6 +9,7 @@ include(BuildVersion) include(BuildBranding) include(CMakeCopyIfDifferent) include(DBusGlib) +include(FindVCRedist) include(FMODSTUDIO) include(GLOD) include(Hunspell) @@ -45,10 +46,6 @@ include(WinManifest) include(ZLIB) include(URIPARSER) -if (MSVC) - use_prebuilt_binary(vcredist) -endif (MSVC) - include_directories( ${STATEMACHINE_INCLUDE_DIRS} ${DBUSGLIB_INCLUDE_DIRS} @@ -1577,7 +1574,29 @@ if (WINDOWS) add_custom_target(copy_w_viewer_manifest ALL DEPENDS ${CMAKE_CFG_INTDIR}/copy_touched.bat) - add_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon copy_w_viewer_manifest) + + if(EXISTS ${VISUAL_STUDIO_REDISTRIBUTABLE_PATH}) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/redist + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/redist" + DEPENDS copy_w_viewer_manifest + COMMENT "Creating redist dir" + ) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/redist/${VISUAL_STUDIO_REDISTRIBUTABLE_NAME} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ARGS + ${VISUAL_STUDIO_REDISTRIBUTABLE_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/redist/${VISUAL_STUDIO_REDISTRIBUTABLE_NAME} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/redist + COMMENT "Copying ${VISUAL_STUDIO_REDISTRIBUTABLE_PATH} to redist dir" + ) + + add_custom_target(copy_w_redist ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/redist/${VISUAL_STUDIO_REDISTRIBUTABLE_NAME}) + endif() + + + add_dependencies(${VIEWER_BINARY_NAME} stage_third_party_libs llcommon copy_w_viewer_manifest copy_w_redist) if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts) diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 8ff723f76..b915fba4c 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -1,7 +1,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; secondlife setup.nsi -;; Copyright 2004-2011, Linden Research, Inc. -;; Copyright 2013-2015 Alchemy Viewer Project +;; Second Life setup.nsi +;; Copyright 2004-2015, Linden Research, Inc. ;; ;; This library is free software; you can redistribute it and/or ;; modify it under the terms of the GNU Lesser General Public @@ -19,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, Don Kjer, Callum Prentice, Drake Arconis ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -34,6 +32,8 @@ !include "LogicLib.nsh" !include "StdUtils.nsh" !include "FileFunc.nsh" + !insertmacro GetParameters + !insertmacro GetOptions !include "x64.nsh" !include "WinVer.nsh" !include "MUI2.nsh" @@ -44,8 +44,8 @@ %%INST_VARS%% %%WIN64_BIN_BUILD%% - Var INSTPROG Var INSTEXE + Var INSTPROG Var INSTSHORTCUT Var AUTOSTART Var COMMANDLINE ; command line passed to this installer, set in .onInit @@ -55,6 +55,14 @@ Var SKIP_AUTORUN ; skip automatic launch of viewer after install Var STARTMENUFOLDER +;-------------------------------- +;Registry Keys + !define ALCHEMY_KEY "SOFTWARE\${VENDORSTR}" + !define INSTNAME_KEY "${ALCHEMY_KEY}\${APPNAMEONEWORD}" + !define MSCURRVER_KEY "SOFTWARE\Microsoft\Windows\CurrentVersion" + !define MSNTCURRVER_KEY "SOFTWARE\Microsoft\Windows NT\CurrentVersion" + !define MSUNINSTALL_KEY "${MSCURRVER_KEY}\Uninstall\${APPNAMEONEWORD}" + ;-------------------------------- ;General @@ -73,7 +81,7 @@ ;Get installation folder from registry if available and 32bit otherwise do it in init !ifndef WIN64_BIN_BUILD - InstallDirRegKey HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" "" + InstallDirRegKey HKLM "${INSTNAME_KEY}" "" !endif ;Request application privileges for Windows UAC @@ -90,12 +98,13 @@ ;Version Information VIProductVersion "${VERSION_LONG}" - VIAddVersionKey "ProductName" "Singularity Viewer" + VIAddVersionKey "ProductName" "Singularity Viewer Installer" VIAddVersionKey "Comments" "A viewer for the meta-verse!" VIAddVersionKey "CompanyName" "${VENDORSTR}" - VIAddVersionKey "LegalCopyright" "Copyright © 2010-2016, ${VENDORSTR}" + VIAddVersionKey "LegalCopyright" "Copyright © 2010-2019, ${VENDORSTR}" VIAddVersionKey "FileDescription" "${APPNAME} Installer" VIAddVersionKey "ProductVersion" "${VERSION_LONG}" + VIAddVersionKey "FileVersion" "${VERSION_LONG}" ;-------------------------------- ;Interface Settings @@ -118,7 +127,7 @@ ;Remember the installer language !define MUI_LANGDLL_REGISTRY_ROOT "HKLM" - !define MUI_LANGDLL_REGISTRY_KEY "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" + !define MUI_LANGDLL_REGISTRY_KEY "${INSTNAME_KEY}" !define MUI_LANGDLL_REGISTRY_VALUENAME "InstallerLanguage" ;Always show the dialog @@ -140,7 +149,7 @@ ;Start Menu Folder Page !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" - !define MUI_STARTMENUPAGE_REGISTRY_KEY "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "${INSTNAME_KEY}" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip !ifdef WIN64_BIN_BUILD @@ -177,10 +186,9 @@ ;Languages !include "%%SOURCE%%\installers\windows\lang_en-us.nsi" - !include "%%SOURCE%%\installers\windows\lang_fr.nsi" !include "%%SOURCE%%\installers\windows\lang_de.nsi" !include "%%SOURCE%%\installers\windows\lang_es.nsi" - !include "%%SOURCE%%\installers\windows\lang_zh.nsi" + !include "%%SOURCE%%\installers\windows\lang_fr.nsi" !include "%%SOURCE%%\installers\windows\lang_ja.nsi" !include "%%SOURCE%%\installers\windows\lang_pl.nsi" !include "%%SOURCE%%\installers\windows\lang_it.nsi" @@ -188,6 +196,7 @@ !include "%%SOURCE%%\installers\windows\lang_da.nsi" !include "%%SOURCE%%\installers\windows\lang_ru.nsi" !include "%%SOURCE%%\installers\windows\lang_tr.nsi" + !include "%%SOURCE%%\installers\windows\lang_zh.nsi" ;-------------------------------- ;Reserve Files @@ -197,7 +206,7 @@ ;because this will make your installer start faster. !insertmacro MUI_RESERVEFILE_LANGDLL - ReserveFile "${NSISDIR}\Plugins\x86-unicode\NSISdl.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\nsisdl.dll" ReserveFile "${NSISDIR}\Plugins\x86-unicode\nsDialogs.dll" ReserveFile "${NSISDIR}\Plugins\x86-unicode\StartMenu.dll" ReserveFile "${NSISDIR}\Plugins\x86-unicode\StdUtils.dll" @@ -374,39 +383,7 @@ Function un.CloseSecondLife Return FunctionEnd -; Test our connection to secondlife.com -; Also allows us to count attempted installs by examining web logs. -; *TODO: Return current SL version info and have installer check -; if it is up to date. -Function CheckNetworkConnection - ; Uneeded - LD - Return - Push $0 - Push $1 - Push $2 # Option value for GetOptions - DetailPrint $(CheckNetworkConnectionDP) - ; Look for a tag value from the stub installer, used for statistics - ; to correlate installs. Default to "" if not found on command line. - StrCpy $2 "" - ${GetOptions} $COMMANDLINE "/STUBTAG=" $2 - GetTempFileName $0 - !define HTTP_TIMEOUT 5000 ; milliseconds - ; Don't show secondary progress bar, this will be quick. - NSISdl::download_quiet \ - /TIMEOUT=${HTTP_TIMEOUT} \ - "http://install.secondlife.com/check/?stubtag=$2&version=${VERSION_LONG}" \ - $0 - Pop $1 ; Return value, either "success", "cancel" or an error message - ; MessageBox MB_OK "Download result: $1" - ; Result ignored for now - ; StrCmp $1 "success" +2 - ; DetailPrint "Connection failed: $1" - Delete $0 ; temporary file - Pop $2 - Pop $1 - Pop $0 - Return -FunctionEnd + ;-------------------------------- ;Installer Sections @@ -424,7 +401,6 @@ Section "Viewer" Call CheckIfAlreadyCurrent Call CloseSecondLife ; Make sure we're not running - Call CheckNetworkConnection ; ping secondlife.com SetOutPath "$INSTDIR" ;Remove old shader files first so fallbacks will work. @@ -455,7 +431,6 @@ Section "Viewer" WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Create Account.url" "InternetShortcut" "URL" "http://join.secondlife.com/" WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Your Account.url" "InternetShortcut" "URL" "http://www.secondlife.com/account/" WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Scripting Language Help.url" "InternetShortcut" "URL" "http://wiki.secondlife.com/wiki/LSL_Portal" - !insertmacro MUI_STARTMENU_WRITE_END ;Other shortcuts @@ -473,28 +448,28 @@ Section "Viewer" !endif ;Write registry - WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "" "$INSTDIR" - WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "Version" "${VERSION_LONG}" - WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "Shortcut" "$INSTSHORTCUT" - WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "Exe" "$INSTEXE" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "Comments" "A viewer for the meta-verse!" + WriteRegStr HKLM "${INSTNAME_KEY}" "" "$INSTDIR" + WriteRegStr HKLM "${INSTNAME_KEY}" "Version" "${VERSION_LONG}" + WriteRegStr HKLM "${INSTNAME_KEY}" "Shortcut" "$INSTSHORTCUT" + WriteRegStr HKLM "${INSTNAME_KEY}" "Exe" "$INSTEXE" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "Comments" "A viewer for the meta-verse!" !ifdef WIN64_BIN_BUILD - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayName" "$INSTSHORTCUT (64 bit) Viewer" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayName" "$INSTSHORTCUT (64 bit) Viewer" !else - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayName" "$INSTSHORTCUT Viewer" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayName" "$INSTSHORTCUT Viewer" !endif - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayIcon" "$INSTDIR\$INSTEXE" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayVersion" "${VERSION_LONG}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "InstallLocation" "$INSTDIR" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "InstallSource" "$EXEDIR\" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "HelpLink" "http://www.singularityviewer.org" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "NoModify" 1 - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "NoRepair" 1 - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "Publisher" "${VENDORSTR}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "URLInfoAbout" "http://www.singularityviewer.org" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "URLUpdateInfo" "http://www.singularityviewer.org/downloads" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "UninstallString" "$\"$INSTDIR\uninst.exe$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "QuietUninstallString" "$\"$INSTDIR\uninst.exe$\" /S" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayIcon" "$INSTDIR\$INSTEXE" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayVersion" "${VERSION_LONG}" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "InstallSource" "$EXEDIR\" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "HelpLink" "http://www.singularityviewer.org" + WriteRegDWORD HKLM "${MSUNINSTALL_KEY}" "NoModify" 1 + WriteRegDWORD HKLM "${MSUNINSTALL_KEY}" "NoRepair" 1 + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "Publisher" "${VENDORSTR}" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "URLInfoAbout" "http://www.singularityviewer.org" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "URLUpdateInfo" "http://www.singularityviewer.org/downloads" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "UninstallString" "$\"$INSTDIR\uninst.exe$\"" + WriteRegStr HKLM "${MSUNINSTALL_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninst.exe$\" /S" ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 IntFmt $0 "0x%08X" $0 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "EstimatedSize" "$0" @@ -533,6 +508,7 @@ Section "Viewer" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "" "URL:Hypergrid legacy" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" "$\"$INSTDIR\$INSTEXE$\"" + ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell" "" "open" @@ -552,6 +528,9 @@ SectionEnd ;-------------------------------- ;Installer Functions Function .onInit +!ifdef WIN64_BIN_BUILD + SetRegView 64 +!endif ;Don't install on unsupported operating systems Call CheckWindowsVersion ;Don't install if not administator @@ -563,7 +542,6 @@ Function .onInit ;Get installation folder from registry if available for 64bit !ifdef WIN64_BIN_BUILD - SetRegView 64 ReadRegStr $0 HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" "" IfErrors +2 0 ; If error jump past setting SKIP_AUTORUN StrCpy $INSTDIR $0 @@ -583,6 +561,7 @@ Function .onInit IfErrors +2 0 ; If error jump past setting AUTOSTART StrCpy $AUTOSTART "true" + ${GetOptions} $COMMANDLINE "/LANGID=" $0 ; /LANGID=1033 implies US English ; If no language (error), then proceed IfErrors lbl_configure_default_lang @@ -613,7 +592,6 @@ Section "Uninstall" !endif StrCpy $INSTPROG "${APPNAMEONEWORD}" - StrCpy $INSTEXE "${INSTEXE}" StrCpy $INSTSHORTCUT "${APPNAME}" Call un.CloseSecondLife @@ -660,7 +638,9 @@ SectionEnd ;Uninstaller Functions Function un.onInit - +!ifdef WIN64_BIN_BUILD + SetRegView 64 +!endif Call un.CheckIfAdministrator !insertmacro MUI_UNGETLANGUAGE diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index bcd5bdab0..743e02236 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -315,6 +315,14 @@ class WindowsManifest(ViewerManifest): # Find singularity-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/%s-bin.exe' % (self.args['configuration'],self.viewer_branding_id()), dst=self.final_exe()) + if self.prefix(src=os.path.join(pkgdir, "redist")): + # include the compiled launcher scripts so that it gets included in the file_list + if(self.is_win64()): + self.path('vc_redist.x64.exe') + else: + self.path('vc_redist.x86.exe') + self.end_prefix() + # Plugin host application self.path2basename(os.path.join(os.pardir, 'llplugin', 'slplugin', self.args['configuration']), From ed09681479a49bd82f57c65c7165b96e22b5b033 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 20 Apr 2019 01:35:20 -0400 Subject: [PATCH 31/91] There's no need to warn or even mention this, thanks Deltek! --- indra/cmake/BuildBranding.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cmake/BuildBranding.cmake b/indra/cmake/BuildBranding.cmake index 4cb59e246..4ab86234d 100644 --- a/indra/cmake/BuildBranding.cmake +++ b/indra/cmake/BuildBranding.cmake @@ -1,6 +1,6 @@ # -*- cmake -*- if (WINDOWS) - message(WARNING, ${CMAKE_CURRENT_BINARY_DIR}/newview/viewerRes.rc.in) + #message(WARNING, ${CMAKE_CURRENT_BINARY_DIR}/newview/viewerRes.rc.in) configure_file( ${CMAKE_SOURCE_DIR}/newview/res/viewerRes.rc.in ${CMAKE_CURRENT_BINARY_DIR}/viewerRes.rc From 726141d6dae00337649754167e7613d1110b9fad Mon Sep 17 00:00:00 2001 From: Router Gray Date: Wed, 17 Apr 2019 20:29:21 -0500 Subject: [PATCH 32/91] Fix LLViewerObject processTaskInvFile iterator double .begin() and no .end() --- indra/newview/llviewerobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 816455a52..ba36aa529 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3276,7 +3276,7 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS LLViewerInventoryItem* item = dynamic_cast(it->get()); if(item && item->getType() != LLAssetType::AT_CATEGORY) { - std::list::iterator id_it = std::find(pending_lst.begin(), pending_lst.begin(), item->getAssetUUID()); + std::list::iterator id_it = std::find(pending_lst.begin(), pending_lst.end(), item->getAssetUUID()); if (id_it != pending_lst.end()) { pending_lst.erase(id_it); From 5f38bb4bf295b3e5089cd7457ebaa50ed81ff7d7 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Wed, 17 Apr 2019 20:30:30 -0500 Subject: [PATCH 33/91] Make dullahan autoplay autoplay. Thanks Liru. --- indra/plugins/cef/media_plugin_cef.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/plugins/cef/media_plugin_cef.cpp b/indra/plugins/cef/media_plugin_cef.cpp index 66a3036b9..6b5efc63f 100644 --- a/indra/plugins/cef/media_plugin_cef.cpp +++ b/indra/plugins/cef/media_plugin_cef.cpp @@ -571,6 +571,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string) settings.proxy_type = mProxyType; settings.proxy_host = mProxyHost; settings.proxy_port = mProxyPort; + settings.autoplay_without_gesture = true; std::vector custom_schemes; custom_schemes.emplace_back("secondlife"); From 002cbce499294081c056212287ce61238edc94b1 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Wed, 17 Apr 2019 20:31:50 -0500 Subject: [PATCH 34/91] Regextra. Fix URLs with '?' params and probly other stuff too. Thanks Liru. --- indra/llui/llurlentry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 9c26f23ab..31d9377d5 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -338,7 +338,7 @@ std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() : LLUrlEntryBase() { - mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?\\S*)?)?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -425,7 +425,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const LLUrlEntrySLURL::LLUrlEntrySLURL() { // see http://slurl.com/about.php for details on the SLURL format - mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?\\S*)?)?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slurl.xml"; mTooltip = LLTrans::getString("TooltipSLURL"); From f7386e8e70cc984ed0bd60d0ec536c9d260fd774 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 20 Apr 2019 16:25:51 -0400 Subject: [PATCH 35/91] Update python stuff to latest Probably definitely break linux --- indra/lib/python/indra/base/__init__.py | 27 - indra/lib/python/indra/base/cllsd_test.py | 73 -- indra/lib/python/indra/base/config.py | 266 ----- indra/lib/python/indra/base/llsd.py | 1052 ----------------- indra/lib/python/indra/base/lluuid.py | 319 ----- indra/lib/python/indra/base/metrics.py | 121 -- indra/lib/python/indra/ipc/httputil.py | 30 - indra/lib/python/indra/ipc/llsdhttp.py | 100 -- indra/lib/python/indra/ipc/mysql_pool.py | 81 -- indra/lib/python/indra/ipc/russ.py | 165 --- indra/lib/python/indra/ipc/servicebuilder.py | 134 --- indra/lib/python/indra/ipc/siesta.py | 468 -------- indra/lib/python/indra/ipc/siesta_test.py | 235 ---- indra/lib/python/indra/ipc/webdav.py | 597 ---------- indra/lib/python/indra/ipc/xml_rpc.py | 273 ----- .../python/indra/util/fastest_elementtree.py | 64 - indra/lib/python/indra/util/helpformatter.py | 52 - indra/lib/python/indra/util/iterators.py | 63 - indra/lib/python/indra/util/iterators_test.py | 72 -- indra/lib/python/indra/util/llmanifest.py | 381 +++--- indra/lib/python/indra/util/llperformance.py | 182 --- indra/lib/python/indra/util/llsubprocess.py | 117 -- indra/lib/python/indra/util/named_query.py | 592 ---------- indra/lib/python/indra/util/shutil2.py | 84 -- .../indra/util/simperf_host_xml_parser.py | 338 ------ .../indra/util/simperf_oprof_interface.py | 167 --- .../indra/util/simperf_proc_interface.py | 191 --- indra/lib/python/indra/util/term.py | 222 ---- indra/lib/python/uuid.py | 508 -------- indra/newview/viewer_manifest.py | 653 +++++----- 30 files changed, 545 insertions(+), 7082 deletions(-) delete mode 100644 indra/lib/python/indra/base/__init__.py delete mode 100644 indra/lib/python/indra/base/cllsd_test.py delete mode 100644 indra/lib/python/indra/base/config.py delete mode 100644 indra/lib/python/indra/base/llsd.py delete mode 100644 indra/lib/python/indra/base/lluuid.py delete mode 100644 indra/lib/python/indra/base/metrics.py delete mode 100644 indra/lib/python/indra/ipc/httputil.py delete mode 100644 indra/lib/python/indra/ipc/llsdhttp.py delete mode 100644 indra/lib/python/indra/ipc/mysql_pool.py delete mode 100644 indra/lib/python/indra/ipc/russ.py delete mode 100644 indra/lib/python/indra/ipc/servicebuilder.py delete mode 100644 indra/lib/python/indra/ipc/siesta.py delete mode 100644 indra/lib/python/indra/ipc/siesta_test.py delete mode 100644 indra/lib/python/indra/ipc/webdav.py delete mode 100644 indra/lib/python/indra/ipc/xml_rpc.py delete mode 100644 indra/lib/python/indra/util/fastest_elementtree.py delete mode 100644 indra/lib/python/indra/util/helpformatter.py delete mode 100644 indra/lib/python/indra/util/iterators.py delete mode 100755 indra/lib/python/indra/util/iterators_test.py delete mode 100755 indra/lib/python/indra/util/llperformance.py delete mode 100644 indra/lib/python/indra/util/llsubprocess.py delete mode 100644 indra/lib/python/indra/util/named_query.py delete mode 100644 indra/lib/python/indra/util/shutil2.py delete mode 100755 indra/lib/python/indra/util/simperf_host_xml_parser.py delete mode 100755 indra/lib/python/indra/util/simperf_oprof_interface.py delete mode 100755 indra/lib/python/indra/util/simperf_proc_interface.py delete mode 100644 indra/lib/python/indra/util/term.py delete mode 100644 indra/lib/python/uuid.py diff --git a/indra/lib/python/indra/base/__init__.py b/indra/lib/python/indra/base/__init__.py deleted file mode 100644 index 2904fd338..000000000 --- a/indra/lib/python/indra/base/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -"""\ -@file __init__.py -@brief Initialization file for the indra.base module. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" diff --git a/indra/lib/python/indra/base/cllsd_test.py b/indra/lib/python/indra/base/cllsd_test.py deleted file mode 100644 index 1f06898ff..000000000 --- a/indra/lib/python/indra/base/cllsd_test.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python -## -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ -from indra.base import llsd, lluuid -from datetime import datetime -import cllsd -import time, sys - -class myint(int): - pass - -values = ( - '&<>', - u'\u81acj', - llsd.uri('http://foo<'), - lluuid.UUID(), - llsd.LLSD(['thing']), - 1, - myint(31337), - sys.maxint + 10, - llsd.binary('foo'), - [], - {}, - {u'f&\u1212': 3}, - 3.1, - True, - None, - datetime.fromtimestamp(time.time()), - ) - -def valuator(values): - for v in values: - yield v - -longvalues = () # (values, list(values), iter(values), valuator(values)) - -for v in values + longvalues: - print '%r => %r' % (v, cllsd.llsd_to_xml(v)) - -a = [[{'a':3}]] * 1000000 - -s = time.time() -print hash(cllsd.llsd_to_xml(a)) -e = time.time() -t1 = e - s -print t1 - -s = time.time() -print hash(llsd.LLSDXMLFormatter()._format(a)) -e = time.time() -t2 = e - s -print t2 - -print 'Speedup:', t2 / t1 diff --git a/indra/lib/python/indra/base/config.py b/indra/lib/python/indra/base/config.py deleted file mode 100644 index adafa29b5..000000000 --- a/indra/lib/python/indra/base/config.py +++ /dev/null @@ -1,266 +0,0 @@ -"""\ -@file config.py -@brief Utility module for parsing and accessing the indra.xml config file. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import copy -import errno -import os -import traceback -import time -import types - -from os.path import dirname, getmtime, join, realpath -from indra.base import llsd - -_g_config = None - -class IndraConfig(object): - """ - IndraConfig loads a 'indra' xml configuration file - and loads into memory. This representation in memory - can get updated to overwrite values or add new values. - - The xml configuration file is considered a live file and changes - to the file are checked and reloaded periodically. If a value had - been overwritten via the update or set method, the loaded values - from the file are ignored (the values from the update/set methods - override) - """ - def __init__(self, indra_config_file): - self._indra_config_file = indra_config_file - self._reload_check_interval = 30 # seconds - self._last_check_time = 0 - self._last_mod_time = 0 - - self._config_overrides = {} - self._config_file_dict = {} - self._combined_dict = {} - - self._load() - - def _load(self): - # if you initialize the IndraConfig with None, no attempt - # is made to load any files - if self._indra_config_file is None: - return - - config_file = open(self._indra_config_file) - self._config_file_dict = llsd.parse(config_file.read()) - self._combine_dictionaries() - config_file.close() - - self._last_mod_time = self._get_last_modified_time() - self._last_check_time = time.time() # now - - def _get_last_modified_time(self): - """ - Returns the mtime (last modified time) of the config file, - if such exists. - """ - if self._indra_config_file is not None: - return os.path.getmtime(self._indra_config_file) - - return 0 - - def _combine_dictionaries(self): - self._combined_dict = {} - self._combined_dict.update(self._config_file_dict) - self._combined_dict.update(self._config_overrides) - - def _reload_if_necessary(self): - now = time.time() - - if (now - self._last_check_time) > self._reload_check_interval: - self._last_check_time = now - try: - modtime = self._get_last_modified_time() - if modtime > self._last_mod_time: - self._load() - except OSError, e: - if e.errno == errno.ENOENT: # file not found - # someone messed with our internal state - # or removed the file - - print 'WARNING: Configuration file has been removed ' + (self._indra_config_file) - print 'Disabling reloading of configuration file.' - - traceback.print_exc() - - self._indra_config_file = None - self._last_check_time = 0 - self._last_mod_time = 0 - else: - raise # pass the exception along to the caller - - def __getitem__(self, key): - self._reload_if_necessary() - - return self._combined_dict[key] - - def get(self, key, default = None): - try: - return self.__getitem__(key) - except KeyError: - return default - - def __setitem__(self, key, value): - """ - Sets the value of the config setting of key to be newval - - Once any key/value pair is changed via the set method, - that key/value pair will remain set with that value until - change via the update or set method - """ - self._config_overrides[key] = value - self._combine_dictionaries() - - def set(self, key, newval): - return self.__setitem__(key, newval) - - def update(self, new_conf): - """ - Load an XML file and apply its map as overrides or additions - to the existing config. Update can be a file or a dict. - - Once any key/value pair is changed via the update method, - that key/value pair will remain set with that value until - change via the update or set method - """ - if isinstance(new_conf, dict): - overrides = new_conf - else: - # assuming that it is a filename - config_file = open(new_conf) - overrides = llsd.parse(config_file.read()) - config_file.close() - - self._config_overrides.update(overrides) - self._combine_dictionaries() - - def as_dict(self): - """ - Returns immutable copy of the IndraConfig as a dictionary - """ - return copy.deepcopy(self._combined_dict) - -def load(config_xml_file = None): - global _g_config - - load_default_files = config_xml_file is None - if load_default_files: - ## going from: - ## "/opt/linden/indra/lib/python/indra/base/config.py" - ## to: - ## "/opt/linden/etc/indra.xml" - config_xml_file = realpath( - dirname(realpath(__file__)) + "../../../../../../etc/indra.xml") - - try: - _g_config = IndraConfig(config_xml_file) - except IOError: - # Failure to load passed in file - # or indra.xml default file - if load_default_files: - try: - config_xml_file = realpath( - dirname(realpath(__file__)) + "../../../../../../etc/globals.xml") - _g_config = IndraConfig(config_xml_file) - return - except IOError: - # Failure to load globals.xml - # fall to code below - pass - - # Either failed to load passed in file - # or failed to load all default files - _g_config = IndraConfig(None) - -def dump(indra_xml_file, indra_cfg = None, update_in_mem=False): - ''' - Dump config contents into a file - Kindof reverse of load. - Optionally takes a new config to dump. - Does NOT update global config unless requested. - ''' - global _g_config - - if not indra_cfg: - if _g_config is None: - return - - indra_cfg = _g_config.as_dict() - - if not indra_cfg: - return - - config_file = open(indra_xml_file, 'w') - _config_xml = llsd.format_xml(indra_cfg) - config_file.write(_config_xml) - config_file.close() - - if update_in_mem: - update(indra_cfg) - -def update(new_conf): - global _g_config - - if _g_config is None: - # To keep with how this function behaved - # previously, a call to update - # before the global is defined - # make a new global config which does not - # load data from a file. - _g_config = IndraConfig(None) - - return _g_config.update(new_conf) - -def get(key, default = None): - global _g_config - - if _g_config is None: - load() - - return _g_config.get(key, default) - -def set(key, newval): - """ - Sets the value of the config setting of key to be newval - - Once any key/value pair is changed via the set method, - that key/value pair will remain set with that value until - change via the update or set method or program termination - """ - global _g_config - - if _g_config is None: - _g_config = IndraConfig(None) - - _g_config.set(key, newval) - -def get_config(): - global _g_config - return _g_config diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py deleted file mode 100644 index 4527b115f..000000000 --- a/indra/lib/python/indra/base/llsd.py +++ /dev/null @@ -1,1052 +0,0 @@ -"""\ -@file llsd.py -@brief Types as well as parsing and formatting functions for handling LLSD. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import datetime -import base64 -import string -import struct -import time -import types -import re - -from indra.util.fastest_elementtree import ElementTreeError, fromstring -from indra.base import lluuid - -# cllsd.c in server/server-1.25 has memory leaks, -# so disabling cllsd for now -#try: -# import cllsd -#except ImportError: -# cllsd = None -cllsd = None - -int_regex = re.compile(r"[-+]?\d+") -real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") -alpha_regex = re.compile(r"[a-zA-Z]+") -date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" - r"(?P\d{2}):(?P\d{2}):(?P\d{2})" - r"(?P(\.\d+)?)Z") -#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" - -class LLSDParseError(Exception): - pass - -class LLSDSerializationError(TypeError): - pass - - -class binary(str): - pass - -class uri(str): - pass - - -BOOL_TRUE = ('1', '1.0', 'true') -BOOL_FALSE = ('0', '0.0', 'false', '') - - -def format_datestr(v): - """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" - if hasattr(v, 'microsecond'): - return v.isoformat() + 'Z' - else: - return v.strftime('%Y-%m-%dT%H:%M:%SZ') - -def parse_datestr(datestr): - """Parses a datetime object from the string format shared by xml and notation serializations.""" - if datestr == "": - return datetime.datetime(1970, 1, 1) - - match = re.match(date_regex, datestr) - if not match: - raise LLSDParseError("invalid date string '%s'." % datestr) - - year = int(match.group('year')) - month = int(match.group('month')) - day = int(match.group('day')) - hour = int(match.group('hour')) - minute = int(match.group('minute')) - second = int(match.group('second')) - seconds_float = match.group('second_float') - microsecond = 0 - if seconds_float: - microsecond = int(float('0' + seconds_float) * 1e6) - return datetime.datetime(year, month, day, hour, minute, second, microsecond) - - -def bool_to_python(node): - val = node.text or '' - if val in BOOL_TRUE: - return True - else: - return False - -def int_to_python(node): - val = node.text or '' - if not val.strip(): - return 0 - return int(val) - -def real_to_python(node): - val = node.text or '' - if not val.strip(): - return 0.0 - return float(val) - -def uuid_to_python(node): - return lluuid.UUID(node.text) - -def str_to_python(node): - return node.text or '' - -def bin_to_python(node): - return binary(base64.decodestring(node.text or '')) - -def date_to_python(node): - val = node.text or '' - if not val: - val = "1970-01-01T00:00:00Z" - return parse_datestr(val) - - -def uri_to_python(node): - val = node.text or '' - if not val: - return None - return uri(val) - -def map_to_python(node): - result = {} - for index in range(len(node))[::2]: - result[node[index].text] = to_python(node[index+1]) - return result - -def array_to_python(node): - return [to_python(child) for child in node] - - -NODE_HANDLERS = dict( - undef=lambda x: None, - boolean=bool_to_python, - integer=int_to_python, - real=real_to_python, - uuid=uuid_to_python, - string=str_to_python, - binary=bin_to_python, - date=date_to_python, - uri=uri_to_python, - map=map_to_python, - array=array_to_python, - ) - -def to_python(node): - return NODE_HANDLERS[node.tag](node) - -class Nothing(object): - pass - - -class LLSDXMLFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def elt(self, name, contents=None): - if(contents is None or contents is ''): - return "<%s />" % (name,) - else: - if type(contents) is unicode: - contents = contents.encode('utf-8') - return "<%s>%s" % (name, contents, name) - - def xml_esc(self, v): - if type(v) is unicode: - v = v.encode('utf-8') - return v.replace('&', '&').replace('<', '<').replace('>', '>') - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return self.elt('undef') - def BOOLEAN(self, v): - if v: - return self.elt('boolean', 'true') - else: - return self.elt('boolean', 'false') - def INTEGER(self, v): - return self.elt('integer', v) - def REAL(self, v): - return self.elt('real', v) - def UUID(self, v): - if(v.isNull()): - return self.elt('uuid') - else: - return self.elt('uuid', v) - def BINARY(self, v): - return self.elt('binary', base64.encodestring(v)) - def STRING(self, v): - return self.elt('string', self.xml_esc(v)) - def URI(self, v): - return self.elt('uri', self.xml_esc(str(v))) - def DATE(self, v): - return self.elt('date', format_datestr(v)) - def ARRAY(self, v): - return self.elt('array', ''.join([self.generate(item) for item in v])) - def MAP(self, v): - return self.elt( - 'map', - ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) - for key, value in v.items()])) - - typeof = type - def generate(self, something): - t = self.typeof(something) - if self.type_map.has_key(t): - return self.type_map[t](something) - else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - t, something)) - - def _format(self, something): - return '' + self.elt("llsd", self.generate(something)) - - def format(self, something): - if cllsd: - return cllsd.llsd_to_xml(something) - return self._format(something) - -_g_xml_formatter = None -def format_xml(something): - global _g_xml_formatter - if _g_xml_formatter is None: - _g_xml_formatter = LLSDXMLFormatter() - return _g_xml_formatter.format(something) - -class LLSDXMLPrettyFormatter(LLSDXMLFormatter): - def __init__(self, indent_atom = None): - # Call the super class constructor so that we have the type map - super(LLSDXMLPrettyFormatter, self).__init__() - - # Override the type map to use our specialized formatters to - # emit the pretty output. - self.type_map[list] = self.PRETTY_ARRAY - self.type_map[tuple] = self.PRETTY_ARRAY - self.type_map[types.GeneratorType] = self.PRETTY_ARRAY, - self.type_map[dict] = self.PRETTY_MAP - - # Private data used for indentation. - self._indent_level = 1 - if indent_atom is None: - self._indent_atom = ' ' - else: - self._indent_atom = indent_atom - - def _indent(self): - "Return an indentation based on the atom and indentation level." - return self._indent_atom * self._indent_level - - def PRETTY_ARRAY(self, v): - rv = [] - rv.append('\n') - self._indent_level = self._indent_level + 1 - rv.extend(["%s%s\n" % - (self._indent(), - self.generate(item)) - for item in v]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('') - return ''.join(rv) - - def PRETTY_MAP(self, v): - rv = [] - rv.append('\n') - self._indent_level = self._indent_level + 1 - keys = v.keys() - keys.sort() - rv.extend(["%s%s\n%s%s\n" % - (self._indent(), - self.elt('key', key), - self._indent(), - self.generate(v[key])) - for key in keys]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('') - return ''.join(rv) - - def format(self, something): - data = [] - data.append('\n') - data.append(self.generate(something)) - data.append('\n') - return '\n'.join(data) - -def format_pretty_xml(something): - """@brief Serialize a python object as 'pretty' llsd xml. - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - This function is not necessarily suited for serializing very large - objects. It is not optimized by the cllsd module, and sorts on - dict (llsd map) keys alphabetically to ease human reading. - """ - return LLSDXMLPrettyFormatter().format(something) - -class LLSDNotationFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return '!' - def BOOLEAN(self, v): - if v: - return 'true' - else: - return 'false' - def INTEGER(self, v): - return "i%s" % v - def REAL(self, v): - return "r%s" % v - def UUID(self, v): - return "u%s" % v - def BINARY(self, v): - return 'b64"' + base64.encodestring(v) + '"' - def STRING(self, v): - if isinstance(v, unicode): - v = v.encode('utf-8') - return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") - def URI(self, v): - return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') - def DATE(self, v): - return 'd"%s"' % format_datestr(v) - def ARRAY(self, v): - return "[%s]" % ','.join([self.generate(item) for item in v]) - def MAP(self, v): - def fix(key): - if isinstance(key, unicode): - return key.encode('utf-8') - return key - return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) - for key, value in v.items()]) - - def generate(self, something): - t = type(something) - handler = self.type_map.get(t) - if handler: - return handler(something) - else: - try: - return self.ARRAY(iter(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % (t, something)) - - def format(self, something): - return self.generate(something) - -def format_notation(something): - return LLSDNotationFormatter().format(something) - -def _hex_as_nybble(hex): - if (hex >= '0') and (hex <= '9'): - return ord(hex) - ord('0') - elif (hex >= 'a') and (hex <='f'): - return 10 + ord(hex) - ord('a') - elif (hex >= 'A') and (hex <='F'): - return 10 + ord(hex) - ord('A'); - -class LLSDBinaryParser(object): - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the binary data to parse in an indexable sequence. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - self._buffer = buffer - self._index = 0 - self._keep_binary = not ignore_binary - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc == 'i': - # 'i' = integer - idx = self._index - self._index += 4 - return struct.unpack("!i", self._buffer[idx:idx+4])[0] - elif cc == ('r'): - # 'r' = real number - idx = self._index - self._index += 8 - return struct.unpack("!d", self._buffer[idx:idx+8])[0] - elif cc == 'u': - # 'u' = uuid - idx = self._index - self._index += 16 - return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16]) - elif cc == 's': - # 's' = string - return self._parse_string() - elif cc in ("'", '"'): - # delimited/escaped string - return self._parse_string_delim(cc) - elif cc == 'l': - # 'l' = uri - return uri(self._parse_string()) - elif cc == ('d'): - # 'd' = date in seconds since epoch - idx = self._index - self._index += 8 - seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0] - return datetime.datetime.fromtimestamp(seconds) - elif cc == 'b': - binary = self._parse_string() - if self._keep_binary: - return binary - # *NOTE: maybe have a binary placeholder which has the - # length. - return None - else: - raise LLSDParseError("invalid binary token at byte %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_map(self): - rv = {} - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - self._index += 1 - key = '' - while (cc != '}') and (count < size): - if cc == 'k': - key = self._parse_string() - elif cc in ("'", '"'): - key = self._parse_string_delim(cc) - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - value = self._parse() - rv[key] = value - count += 1 - cc = self._buffer[self._index] - self._index += 1 - if cc != '}': - raise LLSDParseError("invalid map close token at byte %d." % ( - self._index,)) - return rv - - def _parse_array(self): - rv = [] - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - while (cc != ']') and (count < size): - rv.append(self._parse()) - count += 1 - cc = self._buffer[self._index] - if cc != ']': - raise LLSDParseError("invalid array close token at byte %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_string(self): - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - rv = self._buffer[self._index:self._index+size] - self._index += size - return rv - - def _parse_string_delim(self, delim): - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - -class LLSDNotationParser(object): - """ Parse LLSD notation: - map: { string:object, string:object } - array: [ object, object, object ] - undef: ! - boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE - integer: i#### - real: r#### - uuid: u#### - string: "g\'day" | 'have a "nice" day' | s(size)"raw data" - uri: l"escaped" - date: d"YYYY-MM-DDTHH:MM:SS.FFZ" - binary: b##"ff3120ab1" | b(size)"raw data" - """ - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the notation string to parse. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - if buffer == "": - return False - - self._buffer = buffer - self._index = 0 - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc in ('F', 'f'): - self._skip_alpha() - return False - elif cc in ('T', 't'): - self._skip_alpha() - return True - elif cc == 'i': - # 'i' = integer - return self._parse_integer() - elif cc == ('r'): - # 'r' = real number - return self._parse_real() - elif cc == 'u': - # 'u' = uuid - return self._parse_uuid() - elif cc in ("'", '"', 's'): - return self._parse_string(cc) - elif cc == 'l': - # 'l' = uri - delim = self._buffer[self._index] - self._index += 1 - val = uri(self._parse_string(delim)) - if len(val) == 0: - return None - return val - elif cc == ('d'): - # 'd' = date in seconds since epoch - return self._parse_date() - elif cc == 'b': - return self._parse_binary() - else: - raise LLSDParseError("invalid token at index %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_binary(self): - i = self._index - if self._buffer[i:i+2] == '64': - q = self._buffer[i+2] - e = self._buffer.find(q, i+3) - try: - return base64.decodestring(self._buffer[i+3:e]) - finally: - self._index = e + 1 - else: - raise LLSDParseError('random horrible binary format not supported') - - def _parse_map(self): - """ map: { string:object, string:object } """ - rv = {} - cc = self._buffer[self._index] - self._index += 1 - key = '' - found_key = False - while (cc != '}'): - if not found_key: - if cc in ("'", '"', 's'): - key = self._parse_string(cc) - found_key = True - elif cc.isspace() or cc == ',': - cc = self._buffer[self._index] - self._index += 1 - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - elif cc.isspace() or cc == ':': - cc = self._buffer[self._index] - self._index += 1 - continue - else: - self._index += 1 - value = self._parse() - rv[key] = value - found_key = False - cc = self._buffer[self._index] - self._index += 1 - - return rv - - def _parse_array(self): - """ array: [ object, object, object ] """ - rv = [] - cc = self._buffer[self._index] - while (cc != ']'): - if cc.isspace() or cc == ',': - self._index += 1 - cc = self._buffer[self._index] - continue - rv.append(self._parse()) - cc = self._buffer[self._index] - - if cc != ']': - raise LLSDParseError("invalid array close token at index %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_uuid(self): - match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid uuid token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return lluuid.UUID(self._buffer[start:end]) - - def _skip_alpha(self): - match = re.match(alpha_regex, self._buffer[self._index:]) - if match: - self._index += match.end() - - def _parse_date(self): - delim = self._buffer[self._index] - self._index += 1 - datestr = self._parse_string(delim) - return parse_datestr(datestr) - - def _parse_real(self): - match = re.match(real_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid real token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return float( self._buffer[start:end] ) - - def _parse_integer(self): - match = re.match(int_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid integer token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return int( self._buffer[start:end] ) - - def _parse_string(self, delim): - """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """ - rv = "" - - if delim in ("'", '"'): - rv = self._parse_string_delim(delim) - elif delim == 's': - rv = self._parse_string_raw() - else: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - - - def _parse_string_delim(self, delim): - """ string: "g'day 'un" | 'have a "nice" day' """ - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - - def _parse_string_raw(self): - """ string: s(size)"raw data" """ - # Read the (size) portion. - cc = self._buffer[self._index] - self._index += 1 - if cc != '(': - raise LLSDParseError("invalid string token at index %d." % self._index) - - rparen = self._buffer.find(')', self._index) - if rparen == -1: - raise LLSDParseError("invalid string token at index %d." % self._index) - - size = int(self._buffer[self._index:rparen]) - - self._index = rparen + 1 - delim = self._buffer[self._index] - self._index += 1 - if delim not in ("'", '"'): - raise LLSDParseError("invalid string token at index %d." % self._index) - - rv = self._buffer[self._index:(self._index + size)] - self._index += size - cc = self._buffer[self._index] - self._index += 1 - if cc != delim: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - -def format_binary(something): - return '\n' + _format_binary_recurse(something) - -def _format_binary_recurse(something): - def _format_list(something): - array_builder = [] - array_builder.append('[' + struct.pack('!i', len(something))) - for item in something: - array_builder.append(_format_binary_recurse(item)) - array_builder.append(']') - return ''.join(array_builder) - - if something is None: - return '!' - elif isinstance(something, LLSD): - return _format_binary_recurse(something.thing) - elif isinstance(something, bool): - if something: - return '1' - else: - return '0' - elif isinstance(something, (int, long)): - return 'i' + struct.pack('!i', something) - elif isinstance(something, float): - return 'r' + struct.pack('!d', something) - elif isinstance(something, lluuid.UUID): - return 'u' + something._bits - elif isinstance(something, binary): - return 'b' + struct.pack('!i', len(something)) + something - elif isinstance(something, str): - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, unicode): - something = something.encode('utf-8') - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, uri): - return 'l' + struct.pack('!i', len(something)) + something - elif isinstance(something, datetime.datetime): - seconds_since_epoch = time.mktime(something.timetuple()) - return 'd' + struct.pack('!d', seconds_since_epoch) - elif isinstance(something, (list, tuple)): - return _format_list(something) - elif isinstance(something, dict): - map_builder = [] - map_builder.append('{' + struct.pack('!i', len(something))) - for key, value in something.items(): - if isinstance(key, unicode): - key = key.encode('utf-8') - map_builder.append('k' + struct.pack('!i', len(key)) + key) - map_builder.append(_format_binary_recurse(value)) - map_builder.append('}') - return ''.join(map_builder) - else: - try: - return _format_list(list(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % - (type(something), something)) - - -def parse_binary(binary): - if binary.startswith(''): - just_binary = binary.split('\n', 1)[1] - else: - just_binary = binary - return LLSDBinaryParser().parse(just_binary) - -def parse_xml(something): - try: - return to_python(fromstring(something)[0]) - except ElementTreeError, err: - raise LLSDParseError(*err.args) - -def parse_notation(something): - return LLSDNotationParser().parse(something) - -def parse(something): - try: - something = string.lstrip(something) #remove any pre-trailing whitespace - if something.startswith(''): - return parse_binary(something) - # This should be better. - elif something.startswith('<'): - return parse_xml(something) - else: - return parse_notation(something) - except KeyError, e: - raise Exception('LLSD could not be parsed: %s' % (e,)) - -class LLSD(object): - def __init__(self, thing=None): - self.thing = thing - - def __str__(self): - return self.toXML(self.thing) - - parse = staticmethod(parse) - toXML = staticmethod(format_xml) - toPrettyXML = staticmethod(format_pretty_xml) - toBinary = staticmethod(format_binary) - toNotation = staticmethod(format_notation) - - -undef = LLSD(None) - -XML_MIME_TYPE = 'application/llsd+xml' -BINARY_MIME_TYPE = 'application/llsd+binary' - -# register converters for llsd in mulib, if it is available -try: - from mulib import stacked, mu - stacked.NoProducer() # just to exercise stacked - mu.safe_load(None) # just to exercise mu -except: - # mulib not available, don't print an error message since this is normal - pass -else: - mu.add_parser(parse, XML_MIME_TYPE) - mu.add_parser(parse, 'application/llsd+binary') - - def llsd_convert_xml(llsd_stuff, request): - request.write(format_xml(llsd_stuff)) - - def llsd_convert_binary(llsd_stuff, request): - request.write(format_binary(llsd_stuff)) - - for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]: - stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE) - stacked.add_producer(typ, llsd_convert_xml, 'application/xml') - stacked.add_producer(typ, llsd_convert_xml, 'text/xml') - - stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') - - stacked.add_producer(LLSD, llsd_convert_xml, '*/*') - - # in case someone is using the legacy mu.xml wrapper, we need to - # tell mu to produce application/xml or application/llsd+xml - # (based on the accept header) from raw xml. Phoenix 2008-07-21 - stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE) - stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml') - - - -# mulib wsgi stuff -# try: -# from mulib import mu, adapters -# -# # try some known attributes from mulib to be ultra-sure we've imported it -# mu.get_current -# adapters.handlers -# except: -# # mulib not available, don't print an error message since this is normal -# pass -# else: -# def llsd_xml_handler(content_type): -# def handle_llsd_xml(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_xml(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_xml -# -# def llsd_binary_handler(content_type): -# def handle_llsd_binary(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_binary(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_binary -# -# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse - -# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: -# for content_type in (XML_MIME_TYPE, 'application/xml'): -# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type) -# -# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE) -# -# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*') diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py deleted file mode 100644 index 7413ffe10..000000000 --- a/indra/lib/python/indra/base/lluuid.py +++ /dev/null @@ -1,319 +0,0 @@ -"""\ -@file lluuid.py -@brief UUID parser/generator. - -$LicenseInfo:firstyear=2004&license=mit$ - -Copyright (c) 2004-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import random, socket, string, time, re -import uuid -try: - # Python 2.6 - from hashlib import md5 -except ImportError: - # Python 2.5 and earlier - from md5 import new as md5 - -def _int2binstr(i,l): - s='' - for a in range(l): - s=chr(i&0xFF)+s - i>>=8 - return s - -def _binstr2int(s): - i = long(0) - for c in s: - i = (i<<8) + ord(c) - return i - -class UUID(object): - """ - A class which represents a 16 byte integer. Stored as a 16 byte 8 - bit character string. - - The string version is to be of the form: - AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - - NULL_STR = "00000000-0000-0000-0000-000000000000" - - # the UUIDREGEX_STRING is helpful for parsing UUID's in text - hex_wildcard = r"[0-9a-fA-F]" - word = hex_wildcard + r"{4,4}-" - long_word = hex_wildcard + r"{8,8}-" - very_long_word = hex_wildcard + r"{12,12}" - UUID_REGEX_STRING = long_word + word + word + word + very_long_word - uuid_regex = re.compile(UUID_REGEX_STRING) - - rand = random.Random() - ip = '' - try: - ip = socket.gethostbyname(socket.gethostname()) - except(socket.gaierror, socket.error): - # no ip address, so just default to somewhere in 10.x.x.x - ip = '10' - for i in range(3): - ip += '.' + str(rand.randrange(1,254)) - hexip = ''.join(["%04x" % long(i) for i in ip.split('.')]) - lastid = '' - - def __init__(self, possible_uuid=None): - """ - Initialize to first valid UUID in argument (if a string), - or to null UUID if none found or argument is not supplied. - - If the argument is a UUID, the constructed object will be a copy of it. - """ - self._bits = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - if possible_uuid is None: - return - - if isinstance(possible_uuid, type(self)): - self.set(possible_uuid) - return - - uuid_match = UUID.uuid_regex.search(possible_uuid) - if uuid_match: - uuid_string = uuid_match.group() - s = string.replace(uuid_string, '-', '') - self._bits = _int2binstr(string.atol(s[:8],16),4) + \ - _int2binstr(string.atol(s[8:16],16),4) + \ - _int2binstr(string.atol(s[16:24],16),4) + \ - _int2binstr(string.atol(s[24:],16),4) - - def __len__(self): - """ - Used by the len() builtin. - """ - return 36 - - def __nonzero__(self): - return self._bits != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - - def __str__(self): - uuid_string = self.toString() - return uuid_string - - __repr__ = __str__ - - def __getitem__(self, index): - return str(self)[index] - - def __eq__(self, other): - if isinstance(other, (str, unicode)): - return other == str(self) - return self._bits == getattr(other, '_bits', '') - - def __ne__(self, other): - return not self.__eq__(other) - - def __le__(self, other): - return self._bits <= other._bits - - def __ge__(self, other): - return self._bits >= other._bits - - def __lt__(self, other): - return self._bits < other._bits - - def __gt__(self, other): - return self._bits > other._bits - - def __hash__(self): - return hash(self._bits) - - def set(self, uuid): - self._bits = uuid._bits - - def setFromString(self, uuid_string): - """ - Given a string version of a uuid, set self bits - appropriately. Returns self. - """ - s = string.replace(uuid_string, '-', '') - self._bits = _int2binstr(string.atol(s[:8],16),4) + \ - _int2binstr(string.atol(s[8:16],16),4) + \ - _int2binstr(string.atol(s[16:24],16),4) + \ - _int2binstr(string.atol(s[24:],16),4) - return self - - def setFromMemoryDump(self, gdb_string): - """ - We expect to get gdb_string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 - Which will be translated to: - db547d14-1b3f4bc3-9b984f71-d22f890a - Returns self. - """ - s = string.replace(gdb_string, '0x', '') - s = string.replace(s, ' ', '') - t = '' - for i in range(8,40,8): - for j in range(0,8,2): - t = t + s[i-j-2:i-j] - self.setFromString(t) - - def toString(self): - """ - Return as a string matching the LL standard - AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - return uuid_bits_to_string(self._bits) - - def getAsString(self): - """ - Return a different string representation of the form - AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - i1 = _binstr2int(self._bits[0:4]) - i2 = _binstr2int(self._bits[4:8]) - i3 = _binstr2int(self._bits[8:12]) - i4 = _binstr2int(self._bits[12:16]) - return '%08lx-%08lx-%08lx-%08lx' % (i1,i2,i3,i4) - - def generate(self): - """ - Generate a new uuid. This algorithm is slightly different - from c++ implementation for portability reasons. - Returns self. - """ - m = md5() - m.update(uuid.uuid1().bytes) - self._bits = m.digest() - return self - - def isNull(self): - """ - Returns 1 if the uuid is null - ie, equal to default uuid. - """ - return (self._bits == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0") - - def xor(self, rhs): - """ - xors self with rhs. - """ - v1 = _binstr2int(self._bits[0:4]) ^ _binstr2int(rhs._bits[0:4]) - v2 = _binstr2int(self._bits[4:8]) ^ _binstr2int(rhs._bits[4:8]) - v3 = _binstr2int(self._bits[8:12]) ^ _binstr2int(rhs._bits[8:12]) - v4 = _binstr2int(self._bits[12:16]) ^ _binstr2int(rhs._bits[12:16]) - self._bits = _int2binstr(v1,4) + \ - _int2binstr(v2,4) + \ - _int2binstr(v3,4) + \ - _int2binstr(v4,4) - - -# module-level null constant -NULL = UUID() - -def printTranslatedMemory(four_hex_uints): - """ - We expect to get the string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 - Which will be translated to: - db547d14-1b3f4bc3-9b984f71-d22f890a - """ - uuid = UUID() - uuid.setFromMemoryDump(four_hex_uints) - print uuid.toString() - -def isUUID(id_str): - """ - This function returns: - - 1 if the string passed is a UUID - - 0 is the string passed is not a UUID - - None if it neither of the if's below is satisfied - """ - if not id_str or len(id_str) < 5 or len(id_str) > 36: - return 0 - - if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str): - return 1 - - return None - -def isPossiblyID(id_str): - """ - This function returns 1 if the string passed has some uuid-like - characteristics. Otherwise returns 0. - """ - - is_uuid = isUUID(id_str) - if is_uuid is not None: - return is_uuid - - # build a string which matches every character. - hex_wildcard = r"[0-9a-fA-F]" - chars = len(id_str) - next = min(chars, 8) - matcher = hex_wildcard+"{"+str(next)+","+str(next)+"}" - chars = chars - next - if chars > 0: - matcher = matcher + "-" - chars = chars - 1 - for block in range(3): - next = max(min(chars, 4), 0) - if next: - matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" - chars = chars - next - if chars > 0: - matcher = matcher + "-" - chars = chars - 1 - if chars > 0: - next = min(chars, 12) - matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" - #print matcher - uuid_matcher = re.compile(matcher) - if uuid_matcher.match(id_str): - return 1 - return 0 - -def uuid_bits_to_string(bits): - i1 = _binstr2int(bits[0:4]) - i2 = _binstr2int(bits[4:6]) - i3 = _binstr2int(bits[6:8]) - i4 = _binstr2int(bits[8:10]) - i5 = _binstr2int(bits[10:12]) - i6 = _binstr2int(bits[12:16]) - return '%08lx-%04lx-%04lx-%04lx-%04lx%08lx' % (i1,i2,i3,i4,i5,i6) - -def uuid_bits_to_uuid(bits): - return UUID(uuid_bits_to_string(bits)) - - -try: - from mulib import stacked - stacked.NoProducer() # just to exercise stacked -except: - #print "Couldn't import mulib.stacked, not registering UUID converter" - pass -else: - def convertUUID(uuid, req): - req.write(str(uuid)) - - stacked.add_producer(UUID, convertUUID, "*/*") - stacked.add_producer(UUID, convertUUID, "text/html") diff --git a/indra/lib/python/indra/base/metrics.py b/indra/lib/python/indra/base/metrics.py deleted file mode 100644 index ff8380265..000000000 --- a/indra/lib/python/indra/base/metrics.py +++ /dev/null @@ -1,121 +0,0 @@ -"""\ -@file metrics.py -@author Phoenix -@date 2007-11-27 -@brief simple interface for logging metrics - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys -try: - import syslog -except ImportError: - # Windows - import sys - class syslog(object): - # wrap to a lame syslog for windows - _logfp = sys.stderr - def syslog(msg): - _logfp.write(msg) - if not msg.endswith('\n'): - _logfp.write('\n') - syslog = staticmethod(syslog) - -from indra.base.llsd import format_notation - -def record_metrics(table, stats): - "Write a standard metrics log" - _log("LLMETRICS", table, stats) - -def record_event(table, data): - "Write a standard logmessage log" - _log("LLLOGMESSAGE", table, data) - -def set_destination(dest): - """Set the destination of metrics logs for this process. - - If you do not call this function prior to calling a logging - method, that function will open sys.stdout as a destination. - Attempts to set dest to None will throw a RuntimeError. - @param dest a file-like object which will be the destination for logs.""" - if dest is None: - raise RuntimeError("Attempt to unset metrics destination.") - global _destination - _destination = dest - -def destination(): - """Get the destination of the metrics logs for this process. - Returns None if no destination is set""" - global _destination - return _destination - -class SysLogger(object): - "A file-like object which writes to syslog." - def __init__(self, ident='indra', logopt = None, facility = None): - try: - if logopt is None: - logopt = syslog.LOG_CONS | syslog.LOG_PID - if facility is None: - facility = syslog.LOG_LOCAL0 - syslog.openlog(ident, logopt, facility) - import atexit - atexit.register(syslog.closelog) - except AttributeError: - # No syslog module on Windows - pass - - def write(str): - syslog.syslog(str) - write = staticmethod(write) - - def flush(): - pass - flush = staticmethod(flush) - -# -# internal API -# -_sequence_id = 0 -_destination = None - -def _next_id(): - global _sequence_id - next = _sequence_id - _sequence_id += 1 - return next - -def _dest(): - global _destination - if _destination is None: - # this default behavior is documented in the metrics functions above. - _destination = sys.stdout - return _destination - -def _log(header, table, data): - log_line = "%s (%d) %s %s" \ - % (header, _next_id(), table, format_notation(data)) - dest = _dest() - dest.write(log_line) - dest.flush() diff --git a/indra/lib/python/indra/ipc/httputil.py b/indra/lib/python/indra/ipc/httputil.py deleted file mode 100644 index d53f34a77..000000000 --- a/indra/lib/python/indra/ipc/httputil.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## 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 warnings - -warnings.warn("indra.ipc.httputil has been deprecated; use eventlet.httpc instead", DeprecationWarning, 2) - -from eventlet.httpc import * - - -makeConnection = make_connection diff --git a/indra/lib/python/indra/ipc/llsdhttp.py b/indra/lib/python/indra/ipc/llsdhttp.py deleted file mode 100644 index cbe8ee1ec..000000000 --- a/indra/lib/python/indra/ipc/llsdhttp.py +++ /dev/null @@ -1,100 +0,0 @@ -"""\ -@file llsdhttp.py -@brief Functions to ease moving llsd over http - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import os.path -import os -import urlparse - -from indra.base import llsd - -from eventlet import httpc - -suite = httpc.HttpSuite(llsd.format_xml, llsd.parse, 'application/llsd+xml') -delete = suite.delete -delete_ = suite.delete_ -get = suite.get -get_ = suite.get_ -head = suite.head -head_ = suite.head_ -post = suite.post -post_ = suite.post_ -put = suite.put -put_ = suite.put_ -request = suite.request -request_ = suite.request_ - -# import every httpc error exception into our namespace for convenience -for x in httpc.status_to_error_map.itervalues(): - globals()[x.__name__] = x -ConnectionError = httpc.ConnectionError -Retriable = httpc.Retriable - -for x in (httpc.ConnectionError,): - globals()[x.__name__] = x - - -def postFile(url, filename): - f = open(filename) - body = f.read() - f.close() - llsd_body = llsd.parse(body) - return post_(url, llsd_body) - - -# deprecated in favor of get_ -def getStatus(url, use_proxy=False): - status, _headers, _body = get_(url, use_proxy=use_proxy) - return status - -# deprecated in favor of put_ -def putStatus(url, data): - status, _headers, _body = put_(url, data) - return status - -# deprecated in favor of delete_ -def deleteStatus(url): - status, _headers, _body = delete_(url) - return status - -# deprecated in favor of post_ -def postStatus(url, data): - status, _headers, _body = post_(url, data) - return status - - -def postFileStatus(url, filename): - status, _headers, body = postFile(url, filename) - return status, body - - -def getFromSimulator(path, use_proxy=False): - return get('http://' + simulatorHostAndPort + path, use_proxy=use_proxy) - - -def postToSimulator(path, data=None): - return post('http://' + simulatorHostAndPort + path, data) diff --git a/indra/lib/python/indra/ipc/mysql_pool.py b/indra/lib/python/indra/ipc/mysql_pool.py deleted file mode 100644 index e5855a309..000000000 --- a/indra/lib/python/indra/ipc/mysql_pool.py +++ /dev/null @@ -1,81 +0,0 @@ -"""\ -@file mysql_pool.py -@brief Thin wrapper around eventlet.db_pool that chooses MySQLdb and Tpool. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import MySQLdb -from eventlet import db_pool - -class DatabaseConnector(db_pool.DatabaseConnector): - def __init__(self, credentials, *args, **kwargs): - super(DatabaseConnector, self).__init__(MySQLdb, credentials, - conn_pool=db_pool.ConnectionPool, - *args, **kwargs) - - # get is extended relative to eventlet.db_pool to accept a port argument - def get(self, host, dbname, port=3306): - key = (host, dbname, port) - if key not in self._databases: - new_kwargs = self._kwargs.copy() - new_kwargs['db'] = dbname - new_kwargs['host'] = host - new_kwargs['port'] = port - new_kwargs.update(self.credentials_for(host)) - dbpool = ConnectionPool(*self._args, **new_kwargs) - self._databases[key] = dbpool - - return self._databases[key] - -class ConnectionPool(db_pool.TpooledConnectionPool): - """A pool which gives out saranwrapped MySQLdb connections from a pool - """ - - def __init__(self, *args, **kwargs): - super(ConnectionPool, self).__init__(MySQLdb, *args, **kwargs) - - def get(self): - conn = super(ConnectionPool, self).get() - # annotate the connection object with the details on the - # connection; this is used elsewhere to check that you haven't - # suddenly changed databases in midstream while making a - # series of queries on a connection. - arg_names = ['host','user','passwd','db','port','unix_socket','conv','connect_timeout', - 'compress', 'named_pipe', 'init_command', 'read_default_file', 'read_default_group', - 'cursorclass', 'use_unicode', 'charset', 'sql_mode', 'client_flag', 'ssl', - 'local_infile'] - # you could have constructed this connectionpool with a mix of - # keyword and non-keyword arguments, but we want to annotate - # the connection object with a dict so it's easy to check - # against so here we are converting the list of non-keyword - # arguments (in self._args) into a dict of keyword arguments, - # and merging that with the actual keyword arguments - # (self._kwargs). The arg_names variable lists the - # constructor arguments for MySQLdb Connection objects. - converted_kwargs = dict([ (arg_names[i], arg) for i, arg in enumerate(self._args) ]) - converted_kwargs.update(self._kwargs) - conn.connection_parameters = converted_kwargs - return conn - diff --git a/indra/lib/python/indra/ipc/russ.py b/indra/lib/python/indra/ipc/russ.py deleted file mode 100644 index 35d8afb15..000000000 --- a/indra/lib/python/indra/ipc/russ.py +++ /dev/null @@ -1,165 +0,0 @@ -"""\ -@file russ.py -@brief Recursive URL Substitution Syntax helpers -@author Phoenix - -Many details on how this should work is available on the wiki: -https://wiki.secondlife.com/wiki/Recursive_URL_Substitution_Syntax - -Adding features to this should be reflected in that page in the -implementations section. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import urllib -from indra.ipc import llsdhttp - -class UnbalancedBraces(Exception): - pass - -class UnknownDirective(Exception): - pass - -class BadDirective(Exception): - pass - -def format_value_for_path(value): - if type(value) in [list, tuple]: - # *NOTE: treat lists as unquoted path components so that the quoting - # doesn't get out-of-hand. This is a workaround for the fact that - # russ always quotes, even if the data it's given is already quoted, - # and it's not safe to simply unquote a path directly, so if we want - # russ to substitute urls parts inside other url parts we always - # have to do so via lists of unquoted path components. - return '/'.join([urllib.quote(str(item)) for item in value]) - else: - return urllib.quote(str(value)) - -def format(format_str, context): - """@brief Format format string according to rules for RUSS. -@see https://osiris.lindenlab.com/mediawiki/index.php/Recursive_URL_Substitution_Syntax -@param format_str The input string to format. -@param context A map used for string substitutions. -@return Returns the formatted string. If no match, the braces remain intact. -""" - while True: - #print "format_str:", format_str - all_matches = _find_sub_matches(format_str) - if not all_matches: - break - substitutions = 0 - while True: - matches = all_matches.pop() - # we work from right to left to make sure we do not - # invalidate positions earlier in format_str - matches.reverse() - for pos in matches: - # Use index since _find_sub_matches should have raised - # an exception, and failure to find now is an exception. - end = format_str.index('}', pos) - #print "directive:", format_str[pos+1:pos+5] - if format_str[pos + 1] == '$': - value = context[format_str[pos + 2:end]] - if value is not None: - value = format_value_for_path(value) - elif format_str[pos + 1] == '%': - value = _build_query_string( - context.get(format_str[pos + 2:end])) - elif format_str[pos+1:pos+5] == 'http' or format_str[pos+1:pos+5] == 'file': - value = _fetch_url_directive(format_str[pos + 1:end]) - else: - raise UnknownDirective, format_str[pos:end + 1] - if value is not None: - format_str = format_str[:pos]+str(value)+format_str[end+1:] - substitutions += 1 - - # If there were any substitutions at this depth, re-parse - # since this may have revealed new things to substitute - if substitutions: - break - if not all_matches: - break - - # If there were no substitutions at all, and we have exhausted - # the possible matches, bail. - if not substitutions: - break - return format_str - -def _find_sub_matches(format_str): - """@brief Find all of the substitution matches. -@param format_str the RUSS conformant format string. -@return Returns an array of depths of arrays of positional matches in input. -""" - depth = 0 - matches = [] - for pos in range(len(format_str)): - if format_str[pos] == '{': - depth += 1 - if not len(matches) == depth: - matches.append([]) - matches[depth - 1].append(pos) - continue - if format_str[pos] == '}': - depth -= 1 - continue - if not depth == 0: - raise UnbalancedBraces, format_str - return matches - -def _build_query_string(query_dict): - """\ - @breif given a dict, return a query string. utility wrapper for urllib. - @param query_dict input query dict - @returns Returns an urlencoded query string including leading '?'. - """ - if query_dict: - keys = query_dict.keys() - keys.sort() - def stringize(value): - if type(value) in (str,unicode): - return value - else: - return str(value) - query_list = [urllib.quote(str(key)) + '=' + urllib.quote(stringize(query_dict[key])) for key in keys] - return '?' + '&'.join(query_list) - else: - return '' - -def _fetch_url_directive(directive): - "*FIX: This only supports GET" - commands = directive.split('|') - resource = llsdhttp.get(commands[0]) - if len(commands) == 3: - resource = _walk_resource(resource, commands[2]) - return resource - -def _walk_resource(resource, path): - path = path.split('/') - for child in path: - if not child: - continue - resource = resource[child] - return resource diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py deleted file mode 100644 index 0a0ce2b4e..000000000 --- a/indra/lib/python/indra/ipc/servicebuilder.py +++ /dev/null @@ -1,134 +0,0 @@ -"""\ -@file servicebuilder.py -@author Phoenix -@brief Class which will generate service urls. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from indra.base import config -from indra.ipc import llsdhttp -from indra.ipc import russ - -# *NOTE: agent presence relies on this variable existing and being current, it is a huge hack -services_config = {} -try: - services_config = llsdhttp.get(config.get('services-config')) -except: - pass - -_g_builder = None -def _builder(): - global _g_builder - if _g_builder is None: - _g_builder = ServiceBuilder() - return _g_builder - -def build(name, context={}, **kwargs): - """ Convenience method for using a global, singleton, service builder. Pass arguments either via a dict or via python keyword arguments, or both! - - Example use: - > context = {'channel':'Second Life Release', 'version':'1.18.2.0'} - > servicebuilder.build('version-manager-version', context) - 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0' - > servicebuilder.build('version-manager-version', channel='Second Life Release', version='1.18.2.0') - 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0' - > servicebuilder.build('version-manager-version', context, version='1.18.1.2') - 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.1.2' - """ - global _g_builder - if _g_builder is None: - _g_builder = ServiceBuilder() - return _g_builder.buildServiceURL(name, context, **kwargs) - -def build_path(name, context={}, **kwargs): - context = context.copy() # shouldn't modify the caller's dictionary - context.update(kwargs) - return _builder().buildPath(name, context) - -class ServiceBuilder(object): - def __init__(self, services_definition = services_config): - """\ - @brief - @brief Create a ServiceBuilder. - @param services_definition Complete services definition, services.xml. - """ - # no need to keep a copy of the services section of the - # complete services definition, but it doesn't hurt much. - self.services = services_definition['services'] - self.builders = {} - for service in self.services: - service_builder = service.get('service-builder') - if not service_builder: - continue - if isinstance(service_builder, dict): - # We will be constructing several builders - for name, builder in service_builder.iteritems(): - full_builder_name = service['name'] + '-' + name - self.builders[full_builder_name] = builder - else: - self.builders[service['name']] = service_builder - - def buildPath(self, name, context): - """\ - @brief given the environment on construction, return a service path. - @param name The name of the service. - @param context A dict of name value lookups for the service. - @returns Returns the - """ - return russ.format(self.builders[name], context) - - def buildServiceURL(self, name, context={}, **kwargs): - """\ - @brief given the environment on construction, return a service URL. - @param name The name of the service. - @param context A dict of name value lookups for the service. - @param kwargs Any keyword arguments are treated as members of the - context, this allows you to be all 31337 by writing shit like: - servicebuilder.build('name', param=value) - @returns Returns the - """ - context = context.copy() # shouldn't modify the caller's dictionary - context.update(kwargs) - base_url = config.get('services-base-url') - svc_path = russ.format(self.builders[name], context) - return base_url + svc_path - - -def on_in(query_name, host_key, schema_key): - """\ - @brief Constructs an on/in snippet (for running named queries) - from a schema name and two keys referencing values stored in - indra.xml. - - @param query_name Name of the query. - @param host_key Logical name of destination host. Will be - looked up in indra.xml. - @param schema_key Logical name of destination schema. Will - be looked up in indra.xml. - """ - return "on/config:%s/in/config:%s/%s" % (host_key.strip('/'), - schema_key.strip('/'), - query_name.lstrip('/')) - diff --git a/indra/lib/python/indra/ipc/siesta.py b/indra/lib/python/indra/ipc/siesta.py deleted file mode 100644 index d867e7153..000000000 --- a/indra/lib/python/indra/ipc/siesta.py +++ /dev/null @@ -1,468 +0,0 @@ -"""\ -@file siesta.py -@brief A tiny llsd based RESTful web services framework - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from indra.base import config -from indra.base import llsd -from webob import exc -import webob -import re, socket - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO - -try: - import cjson - json_decode = cjson.decode - json_encode = cjson.encode - JsonDecodeError = cjson.DecodeError - JsonEncodeError = cjson.EncodeError -except ImportError: - import simplejson - json_decode = simplejson.loads - json_encode = simplejson.dumps - JsonDecodeError = ValueError - JsonEncodeError = TypeError - - -llsd_parsers = { - 'application/json': json_decode, - llsd.BINARY_MIME_TYPE: llsd.parse_binary, - 'application/llsd+notation': llsd.parse_notation, - llsd.XML_MIME_TYPE: llsd.parse_xml, - 'application/xml': llsd.parse_xml, - } - - -def mime_type(content_type): - '''Given a Content-Type header, return only the MIME type.''' - - return content_type.split(';', 1)[0].strip().lower() - -class BodyLLSD(object): - '''Give a webob Request or Response an llsd based "content" property. - - Getting the content property parses the body, and caches the result. - - Setting the content property formats a payload, and the body property - is set.''' - - def _llsd__get(self): - '''Get, set, or delete the LLSD value stored in this object.''' - - try: - return self._llsd - except AttributeError: - if not self.body: - raise AttributeError('No llsd attribute has been set') - else: - mtype = mime_type(self.content_type) - try: - parser = llsd_parsers[mtype] - except KeyError: - raise exc.HTTPUnsupportedMediaType( - 'Content type %s not supported' % mtype).exception - try: - self._llsd = parser(self.body) - except (llsd.LLSDParseError, JsonDecodeError, TypeError), err: - raise exc.HTTPBadRequest( - 'Could not parse body: %r' % err.args).exception - return self._llsd - - def _llsd__set(self, val): - req = getattr(self, 'request', None) - if req is not None: - formatter, ctype = formatter_for_request(req) - self.content_type = ctype - else: - formatter, ctype = formatter_for_mime_type( - mime_type(self.content_type)) - self.body = formatter(val) - - def _llsd__del(self): - if hasattr(self, '_llsd'): - del self._llsd - - content = property(_llsd__get, _llsd__set, _llsd__del) - - -class Response(webob.Response, BodyLLSD): - '''Response class with LLSD support. - - A sensible default content type is used. - - Setting the llsd property also sets the body. Getting the llsd - property parses the body if necessary. - - If you set the body property directly, the llsd property will be - deleted.''' - - default_content_type = 'application/llsd+xml' - - def _body__set(self, body): - if hasattr(self, '_llsd'): - del self._llsd - super(Response, self)._body__set(body) - - def cache_forever(self): - self.cache_expires(86400 * 365) - - body = property(webob.Response._body__get, _body__set, - webob.Response._body__del, - webob.Response._body__get.__doc__) - - -class Request(webob.Request, BodyLLSD): - '''Request class with LLSD support. - - Sensible content type and accept headers are used by default. - - Setting the content property also sets the body. Getting the content - property parses the body if necessary. - - If you set the body property directly, the content property will be - deleted.''' - - default_content_type = 'application/llsd+xml' - default_accept = ('application/llsd+xml; q=0.5, ' - 'application/llsd+notation; q=0.3, ' - 'application/llsd+binary; q=0.2, ' - 'application/xml; q=0.1, ' - 'application/json; q=0.0') - - def __init__(self, environ=None, *args, **kwargs): - if environ is None: - environ = {} - else: - environ = environ.copy() - if 'CONTENT_TYPE' not in environ: - environ['CONTENT_TYPE'] = self.default_content_type - if 'HTTP_ACCEPT' not in environ: - environ['HTTP_ACCEPT'] = self.default_accept - super(Request, self).__init__(environ, *args, **kwargs) - - def _body__set(self, body): - if hasattr(self, '_llsd'): - del self._llsd - super(Request, self)._body__set(body) - - def path_urljoin(self, *parts): - return '/'.join([path_url.rstrip('/')] + list(parts)) - - body = property(webob.Request._body__get, _body__set, - webob.Request._body__del, webob.Request._body__get.__doc__) - - def create_response(self, content=None, status='200 OK', - conditional_response=webob.NoDefault): - resp = self.ResponseClass(status=status, request=self, - conditional_response=conditional_response) - resp.content = content - return resp - - def curl(self): - '''Create and fill out a pycurl easy object from this request.''' - - import pycurl - c = pycurl.Curl() - c.setopt(pycurl.URL, self.url()) - if self.headers: - c.setopt(pycurl.HTTPHEADER, - ['%s: %s' % (k, self.headers[k]) for k in self.headers]) - c.setopt(pycurl.FOLLOWLOCATION, True) - c.setopt(pycurl.AUTOREFERER, True) - c.setopt(pycurl.MAXREDIRS, 16) - c.setopt(pycurl.NOSIGNAL, True) - c.setopt(pycurl.READFUNCTION, self.body_file.read) - c.setopt(pycurl.SSL_VERIFYHOST, 2) - - if self.method == 'POST': - c.setopt(pycurl.POST, True) - post301 = getattr(pycurl, 'POST301', None) - if post301 is not None: - # Added in libcurl 7.17.1. - c.setopt(post301, True) - elif self.method == 'PUT': - c.setopt(pycurl.PUT, True) - elif self.method != 'GET': - c.setopt(pycurl.CUSTOMREQUEST, self.method) - return c - -Request.ResponseClass = Response -Response.RequestClass = Request - - -llsd_formatters = { - 'application/json': json_encode, - 'application/llsd+binary': llsd.format_binary, - 'application/llsd+notation': llsd.format_notation, - 'application/llsd+xml': llsd.format_xml, - 'application/xml': llsd.format_xml, - } - -formatter_qualities = ( - ('application/llsd+xml', 1.0), - ('application/llsd+notation', 0.5), - ('application/llsd+binary', 0.4), - ('application/xml', 0.3), - ('application/json', 0.2), - ) - -def formatter_for_mime_type(mime_type): - '''Return a formatter that encodes to the given MIME type. - - The result is a pair of function and MIME type.''' - try: - return llsd_formatters[mime_type], mime_type - except KeyError: - raise exc.HTTPInternalServerError( - 'Could not use MIME type %r to format response' % - mime_type).exception - - -def formatter_for_request(req): - '''Return a formatter that encodes to the preferred type of the client. - - The result is a pair of function and actual MIME type.''' - ctype = req.accept.best_match(formatter_qualities) - try: - return llsd_formatters[ctype], ctype - except KeyError: - raise exc.HTTPNotAcceptable().exception - - -def wsgi_adapter(func, environ, start_response): - '''Adapt a Siesta callable to act as a WSGI application.''' - # Process the request as appropriate. - try: - req = Request(environ) - #print req.urlvars - resp = func(req, **req.urlvars) - if not isinstance(resp, webob.Response): - try: - formatter, ctype = formatter_for_request(req) - resp = req.ResponseClass(formatter(resp), content_type=ctype) - resp._llsd = resp - except (JsonEncodeError, TypeError), err: - resp = exc.HTTPInternalServerError( - detail='Could not format response') - except exc.HTTPException, e: - resp = e - except socket.error, e: - resp = exc.HTTPInternalServerError(detail=e.args[1]) - return resp(environ, start_response) - - -def llsd_callable(func): - '''Turn a callable into a Siesta application.''' - - def replacement(environ, start_response): - return wsgi_adapter(func, environ, start_response) - - return replacement - - -def llsd_method(http_method, func): - def replacement(environ, start_response): - if environ['REQUEST_METHOD'] == http_method: - return wsgi_adapter(func, environ, start_response) - return exc.HTTPMethodNotAllowed()(environ, start_response) - - return replacement - - -http11_methods = 'OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT'.split() -http11_methods.sort() - -def llsd_class(cls): - '''Turn a class into a Siesta application. - - A new instance is created for each request. A HTTP method FOO is - turned into a call to the handle_foo method of the instance.''' - - def foo(req, **kwargs): - instance = cls() - method = req.method.lower() - try: - handler = getattr(instance, 'handle_' + method) - except AttributeError: - allowed = [m for m in http11_methods - if hasattr(instance, 'handle_' + m.lower())] - raise exc.HTTPMethodNotAllowed( - headers={'Allow': ', '.join(allowed)}).exception - #print "kwargs: ", kwargs - return handler(req, **kwargs) - - def replacement(environ, start_response): - return wsgi_adapter(foo, environ, start_response) - - return replacement - - -def curl(reqs): - import pycurl - - m = pycurl.CurlMulti() - curls = [r.curl() for r in reqs] - io = {} - for c in curls: - fp = StringIO() - hdr = StringIO() - c.setopt(pycurl.WRITEFUNCTION, fp.write) - c.setopt(pycurl.HEADERFUNCTION, hdr.write) - io[id(c)] = fp, hdr - m.handles = curls - try: - while True: - ret, num_handles = m.perform() - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - finally: - m.close() - - for req, c in zip(reqs, curls): - fp, hdr = io[id(c)] - hdr.seek(0) - status = hdr.readline().rstrip() - headers = [] - name, values = None, None - - # XXX We don't currently handle bogus header data. - - for line in hdr.readlines(): - if not line[0].isspace(): - if name: - headers.append((name, ' '.join(values))) - name, value = line.strip().split(':', 1) - value = [value] - else: - values.append(line.strip()) - if name: - headers.append((name, ' '.join(values))) - - resp = c.ResponseClass(fp.getvalue(), status, headers, request=req) - - -route_re = re.compile(r''' - \{ # exact character "{" - (\w*) # "config" or variable (restricted to a-z, 0-9, _) - (?:([:~])([^}]+))? # optional :type or ~regex part - \} # exact character "}" - ''', re.VERBOSE) - -predefined_regexps = { - 'uuid': r'[a-f0-9][a-f0-9-]{31,35}', - 'int': r'\d+', - 'host': r'[a-z0-9][a-z0-9\-\.]*', - } - -def compile_route(route): - fp = StringIO() - last_pos = 0 - for match in route_re.finditer(route): - #print "matches: ", match.groups() - fp.write(re.escape(route[last_pos:match.start()])) - var_name = match.group(1) - sep = match.group(2) - expr = match.group(3) - if var_name == 'config': - expr = re.escape(str(config.get(var_name))) - else: - if expr: - if sep == ':': - expr = predefined_regexps[expr] - # otherwise, treat what follows '~' as a regexp - else: - expr = '[^/]+' - if var_name != '': - expr = '(?P<%s>%s)' % (var_name, expr) - else: - expr = '(%s)' % (expr,) - fp.write(expr) - last_pos = match.end() - fp.write(re.escape(route[last_pos:])) - compiled_route = '^%s$' % fp.getvalue() - #print route, "->", compiled_route - return compiled_route - -class Router(object): - '''WSGI routing class. Parses a URL and hands off a request to - some other WSGI application. If no suitable application is found, - responds with a 404.''' - - def __init__(self): - self._new_routes = [] - self._routes = [] - self._paths = [] - - def add(self, route, app, methods=None): - self._new_routes.append((route, app, methods)) - - def _create_routes(self): - for route, app, methods in self._new_routes: - self._paths.append(route) - self._routes.append( - (re.compile(compile_route(route)), - app, - methods and dict.fromkeys(methods))) - self._new_routes = [] - - def __call__(self, environ, start_response): - # load up the config from the config file. Only needs to be - # done once per interpreter. This is the entry point of all - # siesta applications, so this is where we trap it. - _conf = config.get_config() - if _conf is None: - import os.path - fname = os.path.join( - environ.get('ll.config_dir', '/local/linden/etc'), - 'indra.xml') - config.load(fname) - - # proceed with handling the request - self._create_routes() - path_info = environ['PATH_INFO'] - request_method = environ['REQUEST_METHOD'] - allowed = [] - for regex, app, methods in self._routes: - m = regex.match(path_info) - if m: - #print "groupdict:",m.groupdict() - if not methods or request_method in methods: - environ['paste.urlvars'] = m.groupdict() - return app(environ, start_response) - else: - allowed += methods - if allowed: - allowed = dict.fromkeys(allows).keys() - allowed.sort() - resp = exc.HTTPMethodNotAllowed( - headers={'Allow': ', '.join(allowed)}) - else: - resp = exc.HTTPNotFound() - return resp(environ, start_response) diff --git a/indra/lib/python/indra/ipc/siesta_test.py b/indra/lib/python/indra/ipc/siesta_test.py deleted file mode 100644 index a35eed246..000000000 --- a/indra/lib/python/indra/ipc/siesta_test.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/python -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ -from indra.base import llsd, lluuid -from indra.ipc import siesta -import datetime, math, unittest -from webob import exc - - -class ClassApp(object): - def handle_get(self, req): - pass - - def handle_post(self, req): - return req.llsd - - -def callable_app(req): - if req.method == 'UNDERPANTS': - raise exc.HTTPMethodNotAllowed() - elif req.method == 'GET': - return None - return req.llsd - - -class TestBase: - def test_basic_get(self): - req = siesta.Request.blank('/') - self.assertEquals(req.get_response(self.server).body, - llsd.format_xml(None)) - - def test_bad_method(self): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'UNDERPANTS' - self.assertEquals(req.get_response(self.server).status_int, - exc.HTTPMethodNotAllowed.code) - - json_safe = { - 'none': None, - 'bool_true': True, - 'bool_false': False, - 'int_zero': 0, - 'int_max': 2147483647, - 'int_min': -2147483648, - 'long_zero': 0, - 'long_max': 2147483647L, - 'long_min': -2147483648L, - 'float_zero': 0, - 'float': math.pi, - 'float_huge': 3.14159265358979323846e299, - 'str_empty': '', - 'str': 'foo', - u'unic\u1e51de_empty': u'', - u'unic\u1e51de': u'\u1e4exx\u10480', - } - json_safe['array'] = json_safe.values() - json_safe['tuple'] = tuple(json_safe.values()) - json_safe['dict'] = json_safe.copy() - - json_unsafe = { - 'uuid_empty': lluuid.UUID(), - 'uuid_full': lluuid.UUID('dc61ab0530200d7554d23510559102c1a98aab1b'), - 'binary_empty': llsd.binary(), - 'binary': llsd.binary('f\0\xff'), - 'uri_empty': llsd.uri(), - 'uri': llsd.uri('http://www.secondlife.com/'), - 'datetime_empty': datetime.datetime(1970,1,1), - 'datetime': datetime.datetime(1999,9,9,9,9,9), - } - json_unsafe.update(json_safe) - json_unsafe['array'] = json_unsafe.values() - json_unsafe['tuple'] = tuple(json_unsafe.values()) - json_unsafe['dict'] = json_unsafe.copy() - json_unsafe['iter'] = iter(json_unsafe.values()) - - def _test_client_content_type_good(self, content_type, ll): - def run(ll): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'POST' - req.content_type = content_type - req.llsd = ll - req.accept = content_type - resp = req.get_response(self.server) - self.assertEquals(resp.status_int, 200) - return req, resp - - if False and isinstance(ll, dict): - def fixup(v): - if isinstance(v, float): - return '%.5f' % v - if isinstance(v, long): - return int(v) - if isinstance(v, (llsd.binary, llsd.uri)): - return v - if isinstance(v, (tuple, list)): - return [fixup(i) for i in v] - if isinstance(v, dict): - return dict([(k, fixup(i)) for k, i in v.iteritems()]) - return v - for k, v in ll.iteritems(): - l = [k, v] - req, resp = run(l) - self.assertEquals(fixup(resp.llsd), fixup(l)) - - run(ll) - - def test_client_content_type_json_good(self): - self._test_client_content_type_good('application/json', self.json_safe) - - def test_client_content_type_llsd_xml_good(self): - self._test_client_content_type_good('application/llsd+xml', - self.json_unsafe) - - def test_client_content_type_llsd_notation_good(self): - self._test_client_content_type_good('application/llsd+notation', - self.json_unsafe) - - def test_client_content_type_llsd_binary_good(self): - self._test_client_content_type_good('application/llsd+binary', - self.json_unsafe) - - def test_client_content_type_xml_good(self): - self._test_client_content_type_good('application/xml', - self.json_unsafe) - - def _test_client_content_type_bad(self, content_type): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'POST' - req.body = '\0invalid nonsense under all encodings' - req.content_type = content_type - self.assertEquals(req.get_response(self.server).status_int, - exc.HTTPBadRequest.code) - - def test_client_content_type_json_bad(self): - self._test_client_content_type_bad('application/json') - - def test_client_content_type_llsd_xml_bad(self): - self._test_client_content_type_bad('application/llsd+xml') - - def test_client_content_type_llsd_notation_bad(self): - self._test_client_content_type_bad('application/llsd+notation') - - def test_client_content_type_llsd_binary_bad(self): - self._test_client_content_type_bad('application/llsd+binary') - - def test_client_content_type_xml_bad(self): - self._test_client_content_type_bad('application/xml') - - def test_client_content_type_bad(self): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'POST' - req.body = 'XXX' - req.content_type = 'application/nonsense' - self.assertEquals(req.get_response(self.server).status_int, - exc.HTTPUnsupportedMediaType.code) - - def test_request_default_content_type(self): - req = siesta.Request.blank('/') - self.assertEquals(req.content_type, req.default_content_type) - - def test_request_default_accept(self): - req = siesta.Request.blank('/') - from webob import acceptparse - self.assertEquals(str(req.accept).replace(' ', ''), - req.default_accept.replace(' ', '')) - - def test_request_llsd_auto_body(self): - req = siesta.Request.blank('/') - req.llsd = {'a': 2} - self.assertEquals(req.body, '' - 'a2') - - def test_request_llsd_mod_body_changes_llsd(self): - req = siesta.Request.blank('/') - req.llsd = {'a': 2} - req.body = '1337' - self.assertEquals(req.llsd, 1337) - - def test_request_bad_llsd_fails(self): - def crashme(ctype): - def boom(): - class foo(object): pass - req = siesta.Request.blank('/') - req.content_type = ctype - req.llsd = foo() - for mime_type in siesta.llsd_parsers: - self.assertRaises(TypeError, crashme(mime_type)) - - -class ClassServer(TestBase, unittest.TestCase): - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - self.server = siesta.llsd_class(ClassApp) - - -class CallableServer(TestBase, unittest.TestCase): - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - self.server = siesta.llsd_callable(callable_app) - - -class RouterServer(unittest.TestCase): - def test_router(self): - def foo(req, quux): - print quux - - r = siesta.Router() - r.add('/foo/{quux:int}', siesta.llsd_callable(foo), methods=['GET']) - req = siesta.Request.blank('/foo/33') - req.get_response(r) - - req = siesta.Request.blank('/foo/bar') - self.assertEquals(req.get_response(r).status_int, - exc.HTTPNotFound.code) - -if __name__ == '__main__': - unittest.main() diff --git a/indra/lib/python/indra/ipc/webdav.py b/indra/lib/python/indra/ipc/webdav.py deleted file mode 100644 index 98b8499b6..000000000 --- a/indra/lib/python/indra/ipc/webdav.py +++ /dev/null @@ -1,597 +0,0 @@ -""" -@file webdav.py -@brief Classes to make manipulation of a webdav store easier. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys, os, httplib, urlparse -import socket, time -import xml.dom.minidom -import syslog -# import signal - -__revision__ = '0' - -dav_debug = False - - -# def urlsafe_b64decode (enc): -# return base64.decodestring (enc.replace ('_', '/').replace ('-', '+')) - -# def urlsafe_b64encode (str): -# return base64.encodestring (str).replace ('+', '-').replace ('/', '_') - - -class DAVError (Exception): - """ Base class for exceptions in this module. """ - def __init__ (self, status=0, message='', body='', details=''): - self.status = status - self.message = message - self.body = body - self.details = details - Exception.__init__ (self, '%d:%s:%s%s' % (self.status, self.message, - self.body, self.details)) - - def print_to_stderr (self): - """ print_to_stderr docstring """ - print >> sys.stderr, str (self.status) + ' ' + self.message - print >> sys.stderr, str (self.details) - - -class Timeout (Exception): - """ Timeout docstring """ - def __init__ (self, arg=''): - Exception.__init__ (self, arg) - - -def alarm_handler (signum, frame): - """ alarm_handler docstring """ - raise Timeout ('caught alarm') - - -class WebDAV: - """ WebDAV docstring """ - def __init__ (self, url, proxy=None, retries_before_fail=6): - self.init_url = url - self.init_proxy = proxy - self.retries_before_fail = retries_before_fail - url_parsed = urlparse.urlsplit (url) - - self.top_path = url_parsed[ 2 ] - # make sure top_path has a trailing / - if self.top_path == None or self.top_path == '': - self.top_path = '/' - elif len (self.top_path) > 1 and self.top_path[-1:] != '/': - self.top_path += '/' - - if dav_debug: - syslog.syslog ('new WebDAV %s : %s' % (str (url), str (proxy))) - - if proxy: - proxy_parsed = urlparse.urlsplit (proxy) - self.host_header = url_parsed[ 1 ] - host_and_port = proxy_parsed[ 1 ].split (':') - self.host = host_and_port[ 0 ] - if len (host_and_port) > 1: - self.port = int(host_and_port[ 1 ]) - else: - self.port = 80 - else: # no proxy - host_and_port = url_parsed[ 1 ].split (':') - self.host_header = None - self.host = host_and_port[ 0 ] - if len (host_and_port) > 1: - self.port = int(host_and_port[ 1 ]) - else: - self.port = 80 - - self.connection = False - self.connect () - - - def log (self, msg, depth=0): - """ log docstring """ - if dav_debug and depth == 0: - host = str (self.init_url) - if host == 'http://int.tuco.lindenlab.com:80/asset/': - host = 'tuco' - if host == 'http://harriet.lindenlab.com/asset-keep/': - host = 'harriet/asset-keep' - if host == 'http://harriet.lindenlab.com/asset-flag/': - host = 'harriet/asset-flag' - if host == 'http://harriet.lindenlab.com/asset/': - host = 'harriet/asset' - if host == 'http://ozzy.lindenlab.com/asset/': - host = 'ozzy/asset' - if host == 'http://station11.lindenlab.com:12041/:': - host = 'station11:12041' - proxy = str (self.init_proxy) - if proxy == 'None': - proxy = '' - if proxy == 'http://int.tuco.lindenlab.com:3128/': - proxy = 'tuco' - syslog.syslog ('WebDAV (%s:%s) %s' % (host, proxy, str (msg))) - - - def connect (self): - """ connect docstring """ - self.log ('connect') - self.connection = httplib.HTTPConnection (self.host, self.port) - - def __err (self, response, details): - """ __err docstring """ - raise DAVError (response.status, response.reason, response.read (), - str (self.init_url) + ':' + \ - str (self.init_proxy) + ':' + str (details)) - - def request (self, method, path, body=None, headers=None, - read_all=True, body_hook = None, recurse=0, allow_cache=True): - """ request docstring """ - # self.log ('request %s %s' % (method, path)) - if headers == None: - headers = {} - if not allow_cache: - headers['Pragma'] = 'no-cache' - headers['cache-control'] = 'no-cache' - try: - if method.lower () != 'purge': - if path.startswith ('/'): - path = path[1:] - if self.host_header: # use proxy - headers[ 'host' ] = self.host_header - fullpath = 'http://%s%s%s' % (self.host_header, - self.top_path, path) - else: # no proxy - fullpath = self.top_path + path - else: - fullpath = path - - self.connection.request (method, fullpath, body, headers) - if body_hook: - body_hook () - - # signal.signal (signal.SIGALRM, alarm_handler) - # try: - # signal.alarm (120) - # signal.alarm (0) - # except Timeout, e: - # if recurse < 6: - # return self.retry_request (method, path, body, headers, - # read_all, body_hook, recurse) - # else: - # raise DAVError (0, 'timeout', self.host, - # (method, path, body, headers, recurse)) - - response = self.connection.getresponse () - - if read_all: - while len (response.read (1024)) > 0: - pass - if (response.status == 500 or \ - response.status == 503 or \ - response.status == 403) and \ - recurse < self.retries_before_fail: - return self.retry_request (method, path, body, headers, - read_all, body_hook, recurse) - return response - except (httplib.ResponseNotReady, - httplib.BadStatusLine, - socket.error): - # if the server hangs up on us (keepalive off, broken pipe), - # we need to reconnect and try again. - if recurse < self.retries_before_fail: - return self.retry_request (method, path, body, headers, - read_all, body_hook, recurse) - raise DAVError (0, 'reconnect failed', self.host, - (method, path, body, headers, recurse)) - - - def retry_request (self, method, path, body, headers, - read_all, body_hook, recurse): - """ retry_request docstring """ - time.sleep (10.0 * recurse) - self.connect () - return self.request (method, path, body, headers, - read_all, body_hook, recurse+1) - - - - def propfind (self, path, body=None, depth=1): - """ propfind docstring """ - # self.log ('propfind %s' % path) - headers = {'Content-Type':'text/xml; charset="utf-8"', - 'Depth':str(depth)} - response = self.request ('PROPFIND', path, body, headers, False) - if response.status == 207: - return response # Multi-Status - self.__err (response, ('PROPFIND', path, body, headers, 0)) - - - def purge (self, path): - """ issue a squid purge command """ - headers = {'Accept':'*/*'} - response = self.request ('PURGE', path, None, headers) - if response.status == 200 or response.status == 404: - # 200 if it was purge, 404 if it wasn't there. - return response - self.__err (response, ('PURGE', path, None, headers)) - - - def get_file_size (self, path): - """ - Use propfind to ask a webdav server what the size of - a file is. If used on a directory (collection) return 0 - """ - self.log ('get_file_size %s' % path) - # "getcontentlength" property - # 8.1.1 Example - Retrieving Named Properties - # http://docs.python.org/lib/module-xml.dom.html - nsurl = 'http://apache.org/dav/props/' - doc = xml.dom.minidom.Document () - propfind_element = doc.createElementNS (nsurl, "D:propfind") - propfind_element.setAttributeNS (nsurl, 'xmlns:D', 'DAV:') - doc.appendChild (propfind_element) - prop_element = doc.createElementNS (nsurl, "D:prop") - propfind_element.appendChild (prop_element) - con_len_element = doc.createElementNS (nsurl, "D:getcontentlength") - prop_element.appendChild (con_len_element) - - response = self.propfind (path, doc.toxml ()) - doc.unlink () - - resp_doc = xml.dom.minidom.parseString (response.read ()) - cln = resp_doc.getElementsByTagNameNS ('DAV:','getcontentlength')[ 0 ] - try: - content_length = int (cln.childNodes[ 0 ].nodeValue) - except IndexError: - return 0 - resp_doc.unlink () - return content_length - - - def file_exists (self, path): - """ - do an http head on the given file. return True if it succeeds - """ - self.log ('file_exists %s' % path) - expect_gzip = path.endswith ('.gz') - response = self.request ('HEAD', path) - got_gzip = response.getheader ('Content-Encoding', '').strip () - if got_gzip.lower () == 'x-gzip' and expect_gzip == False: - # the asset server fakes us out if we ask for the non-gzipped - # version of an asset, but the server has the gzipped version. - return False - return response.status == 200 - - - def mkdir (self, path): - """ mkdir docstring """ - self.log ('mkdir %s' % path) - headers = {} - response = self.request ('MKCOL', path, None, headers) - if response.status == 201: - return # success - if response.status == 405: - return # directory already existed? - self.__err (response, ('MKCOL', path, None, headers, 0)) - - - def delete (self, path): - """ delete docstring """ - self.log ('delete %s' % path) - headers = {'Depth':'infinity'} # collections require infinity - response = self.request ('DELETE', path, None, headers) - if response.status == 204: - return # no content - if response.status == 404: - return # hmm - self.__err (response, ('DELETE', path, None, headers, 0)) - - - def list_directory (self, path, dir_filter=None, allow_cache=True, - minimum_cache_time=False): - """ - Request an http directory listing and parse the filenames out of lines - like: '
  • X'. If a filter function is provided, - only return filenames that the filter returns True for. - - This is sort of grody, but it seems faster than other ways of getting - this information from an isilon. - """ - self.log ('list_directory %s' % path) - - def try_match (lline, before, after): - """ try_match docstring """ - try: - blen = len (before) - asset_start_index = lline.index (before) - asset_end_index = lline.index (after, asset_start_index + blen) - asset = line[ asset_start_index + blen : asset_end_index ] - - if not dir_filter or dir_filter (asset): - return [ asset ] - return [] - except ValueError: - return [] - - if len (path) > 0 and path[-1:] != '/': - path += '/' - - response = self.request ('GET', path, None, {}, False, - allow_cache=allow_cache) - - if allow_cache and minimum_cache_time: # XXX - print response.getheader ('Date') - # s = "2005-12-06T12:13:14" - # from datetime import datetime - # from time import strptime - # datetime(*strptime(s, "%Y-%m-%dT%H:%M:%S")[0:6]) - # datetime.datetime(2005, 12, 6, 12, 13, 14) - - if response.status != 200: - self.__err (response, ('GET', path, None, {}, 0)) - assets = [] - for line in response.read ().split ('\n'): - lline = line.lower () - if lline.find ("parent directory") == -1: - # isilon file - assets += try_match (lline, '
  • ') - # apache dir - assets += try_match (lline, 'alt="[dir]"> ') - # apache file - assets += try_match (lline, 'alt="[ ]"> ') - return assets - - - def __tmp_filename (self, path_and_file): - """ __tmp_filename docstring """ - head, tail = os.path.split (path_and_file) - if head != '': - return head + '/.' + tail + '.' + str (os.getpid ()) - else: - return head + '.' + tail + '.' + str (os.getpid ()) - - - def __put__ (self, filesize, body_hook, remotefile): - """ __put__ docstring """ - headers = {'Content-Length' : str (filesize)} - remotefile_tmp = self.__tmp_filename (remotefile) - response = self.request ('PUT', remotefile_tmp, None, - headers, True, body_hook) - if not response.status in (201, 204): # created, no content - self.__err (response, ('PUT', remotefile, None, headers, 0)) - if filesize != self.get_file_size (remotefile_tmp): - try: - self.delete (remotefile_tmp) - except: - pass - raise DAVError (0, 'tmp upload error', remotefile_tmp) - # move the file to its final location - try: - self.rename (remotefile_tmp, remotefile) - except DAVError, exc: - if exc.status == 403: # try to clean up the tmp file - try: - self.delete (remotefile_tmp) - except: - pass - raise - if filesize != self.get_file_size (remotefile): - raise DAVError (0, 'file upload error', str (remotefile_tmp)) - - - def put_string (self, strng, remotefile): - """ put_string docstring """ - self.log ('put_string %d -> %s' % (len (strng), remotefile)) - filesize = len (strng) - def body_hook (): - """ body_hook docstring """ - self.connection.send (strng) - self.__put__ (filesize, body_hook, remotefile) - - - def put_file (self, localfile, remotefile): - """ - Send a local file to a remote webdav store. First, upload to - a temporary filename. Next make sure the file is the size we - expected. Next, move the file to its final location. Next, - check the file size at the final location. - """ - self.log ('put_file %s -> %s' % (localfile, remotefile)) - filesize = os.path.getsize (localfile) - def body_hook (): - """ body_hook docstring """ - handle = open (localfile) - while True: - data = handle.read (1300) - if len (data) == 0: - break - self.connection.send (data) - handle.close () - self.__put__ (filesize, body_hook, remotefile) - - - def create_empty_file (self, remotefile): - """ create an empty file """ - self.log ('touch_file %s' % (remotefile)) - headers = {'Content-Length' : '0'} - response = self.request ('PUT', remotefile, None, headers) - if not response.status in (201, 204): # created, no content - self.__err (response, ('PUT', remotefile, None, headers, 0)) - if self.get_file_size (remotefile) != 0: - raise DAVError (0, 'file upload error', str (remotefile)) - - - def __get_file_setup (self, remotefile, check_size=True): - """ __get_file_setup docstring """ - if check_size: - remotesize = self.get_file_size (remotefile) - response = self.request ('GET', remotefile, None, {}, False) - if response.status != 200: - self.__err (response, ('GET', remotefile, None, {}, 0)) - try: - content_length = int (response.getheader ("Content-Length")) - except TypeError: - content_length = None - if check_size: - if content_length != remotesize: - raise DAVError (0, 'file DL size error', remotefile) - return (response, content_length) - - - def __get_file_read (self, writehandle, response, content_length): - """ __get_file_read docstring """ - if content_length != None: - so_far_length = 0 - while so_far_length < content_length: - data = response.read (content_length - so_far_length) - if len (data) == 0: - raise DAVError (0, 'short file download') - so_far_length += len (data) - writehandle.write (data) - while len (response.read ()) > 0: - pass - else: - while True: - data = response.read () - if (len (data) < 1): - break - writehandle.write (data) - - - def get_file (self, remotefile, localfile, check_size=True): - """ - Get a remote file from a webdav server. Download to a local - tmp file, then move into place. Sanity check file sizes as - we go. - """ - self.log ('get_file %s -> %s' % (remotefile, localfile)) - (response, content_length) = \ - self.__get_file_setup (remotefile, check_size) - localfile_tmp = self.__tmp_filename (localfile) - handle = open (localfile_tmp, 'w') - self.__get_file_read (handle, response, content_length) - handle.close () - if check_size: - if content_length != os.path.getsize (localfile_tmp): - raise DAVError (0, 'file DL size error', - remotefile+','+localfile) - os.rename (localfile_tmp, localfile) - - - def get_file_as_string (self, remotefile, check_size=True): - """ - download a file from a webdav server and return it as a string. - """ - self.log ('get_file_as_string %s' % remotefile) - (response, content_length) = \ - self.__get_file_setup (remotefile, check_size) - # (tmp_handle, tmp_filename) = tempfile.mkstemp () - tmp_handle = os.tmpfile () - self.__get_file_read (tmp_handle, response, content_length) - tmp_handle.seek (0) - ret = tmp_handle.read () - tmp_handle.close () - # os.unlink (tmp_filename) - return ret - - - def get_post_as_string (self, remotefile, body): - """ - Do an http POST, send body, get response and return it. - """ - self.log ('get_post_as_string %s' % remotefile) - # headers = {'Content-Type':'application/x-www-form-urlencoded'} - headers = {'Content-Type':'text/xml; charset="utf-8"'} - # b64body = urlsafe_b64encode (asset_url) - response = self.request ('POST', remotefile, body, headers, False) - if response.status != 200: - self.__err (response, ('POST', remotefile, body, headers, 0)) - try: - content_length = int (response.getheader ('Content-Length')) - except TypeError: - content_length = None - tmp_handle = os.tmpfile () - self.__get_file_read (tmp_handle, response, content_length) - tmp_handle.seek (0) - ret = tmp_handle.read () - tmp_handle.close () - return ret - - - def __destination_command (self, verb, remotesrc, dstdav, remotedst): - """ - self and dstdav should point to the same http server. - """ - if len (remotedst) > 0 and remotedst[ 0 ] == '/': - remotedst = remotedst[1:] - headers = {'Destination': 'http://%s:%d%s%s' % (dstdav.host, - dstdav.port, - dstdav.top_path, - remotedst)} - response = self.request (verb, remotesrc, None, headers) - if response.status == 201: - return # created - if response.status == 204: - return # no content - self.__err (response, (verb, remotesrc, None, headers, 0)) - - - def rename (self, remotesrc, remotedst): - """ rename a file on a webdav server """ - self.log ('rename %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('MOVE', remotesrc, self, remotedst) - def xrename (self, remotesrc, dstdav, remotedst): - """ rename a file on a webdav server """ - self.log ('xrename %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('MOVE', remotesrc, dstdav, remotedst) - - - def copy (self, remotesrc, remotedst): - """ copy a file on a webdav server """ - self.log ('copy %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('COPY', remotesrc, self, remotedst) - def xcopy (self, remotesrc, dstdav, remotedst): - """ copy a file on a webdav server """ - self.log ('xcopy %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('COPY', remotesrc, dstdav, remotedst) - - -def put_string (data, url): - """ - upload string s to a url - """ - url_parsed = urlparse.urlsplit (url) - dav = WebDAV ('%s://%s/' % (url_parsed[ 0 ], url_parsed[ 1 ])) - dav.put_string (data, url_parsed[ 2 ]) - - -def get_string (url, check_size=True): - """ - return the contents of a url as a string - """ - url_parsed = urlparse.urlsplit (url) - dav = WebDAV ('%s://%s/' % (url_parsed[ 0 ], url_parsed[ 1 ])) - return dav.get_file_as_string (url_parsed[ 2 ], check_size) diff --git a/indra/lib/python/indra/ipc/xml_rpc.py b/indra/lib/python/indra/ipc/xml_rpc.py deleted file mode 100644 index 47536c10c..000000000 --- a/indra/lib/python/indra/ipc/xml_rpc.py +++ /dev/null @@ -1,273 +0,0 @@ -"""\ -@file xml_rpc.py -@brief An implementation of a parser/generator for the XML-RPC xml format. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - - -from greenlet import greenlet - -from mulib import mu - -from xml.sax import handler -from xml.sax import parseString - - -# States -class Expected(object): - def __init__(self, tag): - self.tag = tag - - def __getattr__(self, name): - return type(self)(name) - - def __repr__(self): - return '%s(%r)' % ( - type(self).__name__, self.tag) - - -class START(Expected): - pass - - -class END(Expected): - pass - - -class STR(object): - tag = '' - - -START = START('') -END = END('') - - -class Malformed(Exception): - pass - - -class XMLParser(handler.ContentHandler): - def __init__(self, state_machine, next_states): - handler.ContentHandler.__init__(self) - self.state_machine = state_machine - if not isinstance(next_states, tuple): - next_states = (next_states, ) - self.next_states = next_states - self._character_buffer = '' - - def assertState(self, state, name, *rest): - if not isinstance(self.next_states, tuple): - self.next_states = (self.next_states, ) - for next in self.next_states: - if type(state) == type(next): - if next.tag and next.tag != name: - raise Malformed( - "Expected %s, got %s %s %s" % ( - next, state, name, rest)) - break - else: - raise Malformed( - "Expected %s, got %s %s %s" % ( - self.next_states, state, name, rest)) - - def startElement(self, name, attrs): - self.assertState(START, name.lower(), attrs) - self.next_states = self.state_machine.switch(START, (name.lower(), dict(attrs))) - - def endElement(self, name): - if self._character_buffer.strip(): - characters = self._character_buffer.strip() - self._character_buffer = '' - self.assertState(STR, characters) - self.next_states = self.state_machine.switch(characters) - self.assertState(END, name.lower()) - self.next_states = self.state_machine.switch(END, name.lower()) - - def error(self, exc): - self.bozo = 1 - self.exc = exc - - def fatalError(self, exc): - self.error(exc) - raise exc - - def characters(self, characters): - self._character_buffer += characters - - -def parse(what): - child = greenlet(xml_rpc) - me = greenlet.getcurrent() - startup_states = child.switch(me) - parser = XMLParser(child, startup_states) - try: - parseString(what, parser) - except Malformed: - print what - raise - return child.switch() - - -def xml_rpc(yielder): - yielder.switch(START.methodcall) - yielder.switch(START.methodname) - methodName = yielder.switch(STR) - yielder.switch(END.methodname) - - yielder.switch(START.params) - - root = None - params = [] - while True: - state, _ = yielder.switch(START.param, END.params) - if state == END: - break - - yielder.switch(START.value) - - params.append( - handle(yielder)) - - yielder.switch(END.value) - yielder.switch(END.param) - - yielder.switch(END.methodcall) - ## Resume parse - yielder.switch() - ## Return result to parse - return methodName.strip(), params - - -def handle(yielder): - _, (tag, attrs) = yielder.switch(START) - if tag in ['int', 'i4']: - result = int(yielder.switch(STR)) - elif tag == 'boolean': - result = bool(int(yielder.switch(STR))) - elif tag == 'string': - result = yielder.switch(STR) - elif tag == 'double': - result = float(yielder.switch(STR)) - elif tag == 'datetime.iso8601': - result = yielder.switch(STR) - elif tag == 'base64': - result = base64.b64decode(yielder.switch(STR)) - elif tag == 'struct': - result = {} - while True: - state, _ = yielder.switch(START.member, END.struct) - if state == END: - break - - yielder.switch(START.name) - key = yielder.switch(STR) - yielder.switch(END.name) - - yielder.switch(START.value) - result[key] = handle(yielder) - yielder.switch(END.value) - - yielder.switch(END.member) - ## We already handled above, don't want to handle it below - return result - elif tag == 'array': - result = [] - yielder.switch(START.data) - while True: - state, _ = yielder.switch(START.value, END.data) - if state == END: - break - - result.append(handle(yielder)) - - yielder.switch(END.value) - - yielder.switch(getattr(END, tag)) - - return result - - -VALUE = mu.tag_factory('value') -BOOLEAN = mu.tag_factory('boolean') -INT = mu.tag_factory('int') -STRUCT = mu.tag_factory('struct') -MEMBER = mu.tag_factory('member') -NAME = mu.tag_factory('name') -ARRAY = mu.tag_factory('array') -DATA = mu.tag_factory('data') -STRING = mu.tag_factory('string') -DOUBLE = mu.tag_factory('double') -METHODRESPONSE = mu.tag_factory('methodResponse') -PARAMS = mu.tag_factory('params') -PARAM = mu.tag_factory('param') - -mu.inline_elements['string'] = True -mu.inline_elements['boolean'] = True -mu.inline_elements['name'] = True - - -def _generate(something): - if isinstance(something, dict): - result = STRUCT() - for key, value in something.items(): - result[ - MEMBER[ - NAME[key], _generate(value)]] - return VALUE[result] - elif isinstance(something, list): - result = DATA() - for item in something: - result[_generate(item)] - return VALUE[ARRAY[[result]]] - elif isinstance(something, basestring): - return VALUE[STRING[something]] - elif isinstance(something, bool): - if something: - return VALUE[BOOLEAN['1']] - return VALUE[BOOLEAN['0']] - elif isinstance(something, int): - return VALUE[INT[something]] - elif isinstance(something, float): - return VALUE[DOUBLE[something]] - -def generate(*args): - params = PARAMS() - for arg in args: - params[PARAM[_generate(arg)]] - return METHODRESPONSE[params] - - -if __name__ == '__main__': - print parse(""" examples.getStateName 41 -""") - - - - - - - - - diff --git a/indra/lib/python/indra/util/fastest_elementtree.py b/indra/lib/python/indra/util/fastest_elementtree.py deleted file mode 100644 index 4fcf662dd..000000000 --- a/indra/lib/python/indra/util/fastest_elementtree.py +++ /dev/null @@ -1,64 +0,0 @@ -"""\ -@file fastest_elementtree.py -@brief Concealing some gnarly import logic in here. This should export the interface of elementtree. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -# The parsing exception raised by the underlying library depends -# on the ElementTree implementation we're using, so we provide an -# alias here. -# -# Use ElementTreeError as the exception type for catching parsing -# errors. - - -# Using cElementTree might cause some unforeseen problems, so here's a -# convenient off switch. -use_celementree = True - -try: - if not use_celementree: - raise ImportError() - # Python 2.3 and 2.4. - from cElementTree import * - ElementTreeError = SyntaxError -except ImportError: - try: - if not use_celementree: - raise ImportError() - # Python 2.5 and above. - from xml.etree.cElementTree import * - ElementTreeError = SyntaxError - except ImportError: - # Pure Python code. - try: - # Python 2.3 and 2.4. - from elementtree.ElementTree import * - except ImportError: - # Python 2.5 and above. - from xml.etree.ElementTree import * - - # The pure Python ElementTree module uses Expat for parsing. - from xml.parsers.expat import ExpatError as ElementTreeError diff --git a/indra/lib/python/indra/util/helpformatter.py b/indra/lib/python/indra/util/helpformatter.py deleted file mode 100644 index ba5c9b67d..000000000 --- a/indra/lib/python/indra/util/helpformatter.py +++ /dev/null @@ -1,52 +0,0 @@ -"""\ -@file helpformatter.py -@author Phoenix -@brief Class for formatting optparse descriptions. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import optparse -import textwrap - -class Formatter(optparse.IndentedHelpFormatter): - def __init__( - self, - p_indentIncrement = 2, - p_maxHelpPosition = 24, - p_width = 79, - p_shortFirst = 1) : - optparse.HelpFormatter.__init__( - self, - p_indentIncrement, - p_maxHelpPosition, - p_width, - p_shortFirst) - def format_description(self, p_description): - t_descWidth = self.width - self.current_indent - t_indent = " " * (self.current_indent + 2) - return "\n".join( - [textwrap.fill(descr, t_descWidth, initial_indent = t_indent, - subsequent_indent = t_indent) - for descr in p_description.split("\n")] ) diff --git a/indra/lib/python/indra/util/iterators.py b/indra/lib/python/indra/util/iterators.py deleted file mode 100644 index 9013fa630..000000000 --- a/indra/lib/python/indra/util/iterators.py +++ /dev/null @@ -1,63 +0,0 @@ -"""\ -@file iterators.py -@brief Useful general-purpose iterators. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from __future__ import nested_scopes - -def iter_chunks(rows, aggregate_size=100): - """ - Given an iterable set of items (@p rows), produces lists of up to @p - aggregate_size items at a time, for example: - - iter_chunks([1,2,3,4,5,6,7,8,9,10], 3) - - Values for @p aggregate_size < 1 will raise ValueError. - - Will return a generator that produces, in the following order: - - [1, 2, 3] - - [4, 5, 6] - - [7, 8, 9] - - [10] - """ - if aggregate_size < 1: - raise ValueError() - - def iter_chunks_inner(): - row_iter = iter(rows) - done = False - agg = [] - while not done: - try: - row = row_iter.next() - agg.append(row) - except StopIteration: - done = True - if agg and (len(agg) >= aggregate_size or done): - yield agg - agg = [] - - return iter_chunks_inner() diff --git a/indra/lib/python/indra/util/iterators_test.py b/indra/lib/python/indra/util/iterators_test.py deleted file mode 100755 index 66928c8e7..000000000 --- a/indra/lib/python/indra/util/iterators_test.py +++ /dev/null @@ -1,72 +0,0 @@ -"""\ -@file iterators_test.py -@brief Test cases for iterators module. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import unittest - -from indra.util.iterators import iter_chunks - -class TestIterChunks(unittest.TestCase): - """Unittests for iter_chunks""" - def test_bad_agg_size(self): - rows = [1,2,3,4] - self.assertRaises(ValueError, iter_chunks, rows, 0) - self.assertRaises(ValueError, iter_chunks, rows, -1) - - try: - for i in iter_chunks(rows, 0): - pass - except ValueError: - pass - else: - self.fail() - - try: - result = list(iter_chunks(rows, 0)) - except ValueError: - pass - else: - self.fail() - def test_empty(self): - rows = [] - result = list(iter_chunks(rows)) - self.assertEqual(result, []) - def test_small(self): - rows = [[1]] - result = list(iter_chunks(rows, 2)) - self.assertEqual(result, [[[1]]]) - def test_size(self): - rows = [[1],[2]] - result = list(iter_chunks(rows, 2)) - self.assertEqual(result, [[[1],[2]]]) - def test_multi_agg(self): - rows = [[1],[2],[3],[4],[5]] - result = list(iter_chunks(rows, 2)) - self.assertEqual(result, [[[1],[2]],[[3],[4]],[[5]]]) - -if __name__ == "__main__": - unittest.main() diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 5cd8220aa..b285b46c7 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -33,21 +33,26 @@ import filecmp import fnmatch import getopt import glob +import itertools +import operator import os import re import shutil +import subprocess import sys import tarfile -import errno -import subprocess class ManifestError(RuntimeError): """Use an exception more specific than generic Python RuntimeError""" - pass + def __init__(self, msg): + self.msg = msg + super(ManifestError, self).__init__(self.msg) class MissingError(ManifestError): """You specified a file that doesn't exist""" - pass + def __init__(self, msg): + self.msg = msg + super(MissingError, self).__init__(self.msg) def path_ancestors(path): drive, path = os.path.splitdrive(os.path.normpath(path)) @@ -88,7 +93,7 @@ DEFAULT_SRCTREE = os.path.dirname(sys.argv[0]) CHANNEL_VENDOR_BASE = 'Singularity' RELEASE_CHANNEL = CHANNEL_VENDOR_BASE + ' Release' -ARGUMENTS=[ +BASE_ARGUMENTS=[ dict(name='actions', description="""This argument specifies the actions that are to be taken when the script is run. The meaningful actions are currently: @@ -106,9 +111,17 @@ ARGUMENTS=[ Example use: %(name)s --arch=i686 On Linux this would try to use Linux_i686Manifest.""", default=""), + dict(name='artwork', description='Artwork directory.', default=DEFAULT_SRCTREE), dict(name='branding_id', description="Identifier for the branding set to use.", default='singularity'), dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE), dict(name='buildtype', description='Build type (i.e. Debug, Release, RelWithDebInfo).', default=None), + dict(name='channel', + description="""The channel to use for updates, packaging, settings name, etc.""", + default='CHANNEL UNSET'), + dict(name='channel_suffix', + description="""Addition to the channel for packaging and channel value, + but not application name (used internally)""", + default=None), dict(name='configuration', description="""The build configuration used.""", default="Release"), @@ -116,12 +129,6 @@ ARGUMENTS=[ dict(name='grid', description="""Which grid the client will try to connect to.""", default=None), - dict(name='channel', - description="""The channel to use for updates, packaging, settings name, etc.""", - default='CHANNEL UNSET'), - dict(name='channel_suffix', - description="""Addition to the channel for packaging and channel value, but not application name (used internally)""", - default=None), dict(name='installer_name', description=""" The name of the file that the installer should be packaged up into. Only used on Linux at the moment.""", @@ -133,13 +140,17 @@ ARGUMENTS=[ description="""The current platform, to be used for looking up which manifest class to run.""", default=get_default_platform), + dict(name='signature', + description="""This specifies an identity to sign the viewer with, if any. + If no value is supplied, the default signature will be used, if any. Currently + only used on Mac OS X.""", + default=None), dict(name='source', description='Source directory.', default=DEFAULT_SRCTREE), dict(name='standalone', description='Set to ON if this is a standalone build.', default="OFF"), - dict(name='artwork', description='Artwork directory.', default=DEFAULT_SRCTREE), dict(name='touch', description="""File to touch when action is finished. Touch file will contain the name of the final package in a form suitable @@ -147,20 +158,15 @@ ARGUMENTS=[ default=None), dict(name='versionfile', description="""The name of a file containing the full version number."""), - dict(name='signature', - description="""This specifies an identity to sign the viewer with, if any. - If no value is supplied, the default signature will be used, if any. Currently - only used on Mac OS X.""", - default=None) ] -def usage(srctree=""): +def usage(arguments, srctree=""): nd = {'name':sys.argv[0]} print """Usage: %(name)s [options] [destdir] Options: """ % nd - for arg in ARGUMENTS: + for arg in arguments: default = arg['default'] if hasattr(default, '__call__'): default = "(computed value) \"" + str(default(srctree)) + '"' @@ -171,11 +177,15 @@ def usage(srctree=""): default, arg['description'] % nd) -def main(): -## import itertools +def main(extra=[]): ## print ' '.join((("'%s'" % item) if ' ' in item else item) ## for item in itertools.chain([sys.executable], sys.argv)) - option_names = [arg['name'] + '=' for arg in ARGUMENTS] + # Supplement our default command-line switches with any desired by + # application-specific caller. + arguments = list(itertools.chain(BASE_ARGUMENTS, extra)) + # Alphabetize them by option name in case we display usage. + arguments.sort(key=operator.itemgetter('name')) + option_names = [arg['name'] + '=' for arg in arguments] option_names.append('help') options, remainder = getopt.getopt(sys.argv[1:], "", option_names) @@ -198,11 +208,11 @@ def main(): # early out for help if 'help' in args: # *TODO: it is a huge hack to pass around the srctree like this - usage(args['source']) + usage(arguments, srctree=args['source']) return # defaults - for arg in ARGUMENTS: + for arg in arguments: if arg['name'] not in args: default = arg['default'] if hasattr(default, '__call__'): @@ -231,101 +241,68 @@ def main(): print "Option:", opt, "=", args[opt] # pass in sourceid as an argument now instead of an environment variable - try: - args['sourceid'] = os.environ["sourceid"] - except KeyError: - args['sourceid'] = "" + args['sourceid'] = os.environ.get("sourceid", "") # Build base package. touch = args.get('touch') if touch: - print 'Creating base package' - args['package_id'] = "" # base package has no package ID + print '================ Creating base package' + else: + print '================ Starting base copy' wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) # Store package file for later if making touched file. base_package_file = "" if touch: - print 'Created base package ', wm.package_file + print '================ Created base package ', wm.package_file base_package_file = "" + wm.package_file + else: + print '================ Finished base copy' # handle multiple packages if set - try: - additional_packages = os.environ["additional_packages"] - except KeyError: - additional_packages = "" + # ''.split() produces empty list + additional_packages = os.environ.get("additional_packages", "").split() if additional_packages: # Determine destination prefix / suffix for additional packages. - base_dest_postfix = args['dest'] - base_dest_prefix = "" - base_dest_parts = args['dest'].split(os.sep) - if len(base_dest_parts) > 1: - base_dest_postfix = base_dest_parts[len(base_dest_parts) - 1] - base_dest_prefix = base_dest_parts[0] - i = 1 - while i < len(base_dest_parts) - 1: - base_dest_prefix = base_dest_prefix + os.sep + base_dest_parts[i] - i = i + 1 + base_dest_parts = list(os.path.split(args['dest'])) + base_dest_parts.insert(-1, "{}") + base_dest_template = os.path.join(*base_dest_parts) # Determine touched prefix / suffix for additional packages. - base_touch_postfix = "" - base_touch_prefix = "" if touch: - base_touch_postfix = touch - base_touch_parts = touch.split('/') + base_touch_parts = list(os.path.split(touch)) + # Because of the special insert() logic below, we don't just want + # [dirpath, basename]; we want [dirpath, directory, basename]. + # Further split the dirpath and replace it in the list. + base_touch_parts[0:1] = os.path.split(base_touch_parts[0]) if "arwin" in args['platform']: - if len(base_touch_parts) > 1: - base_touch_postfix = base_touch_parts[len(base_touch_parts) - 1] - base_touch_prefix = base_touch_parts[0] - i = 1 - while i < len(base_touch_parts) - 1: - base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i] - i = i + 1 + base_touch_parts.insert(-1, "{}") else: - if len(base_touch_parts) > 2: - base_touch_postfix = base_touch_parts[len(base_touch_parts) - 2] + '/' + base_touch_parts[len(base_touch_parts) - 1] - base_touch_prefix = base_touch_parts[0] - i = 1 - while i < len(base_touch_parts) - 2: - base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i] - i = i + 1 - # Store base channel name. - base_channel_name = args['channel'] - # Build each additional package. - package_id_list = additional_packages.split(" ") - args['channel'] = base_channel_name - for package_id in package_id_list: + base_touch_parts.insert(-2, "{}") + base_touch_template = os.path.join(*base_touch_parts) + for package_id in additional_packages: + args['channel_suffix'] = os.environ.get(package_id + "_viewer_channel_suffix") + args['sourceid'] = os.environ.get(package_id + "_sourceid") + args['dest'] = base_dest_template.format(package_id) + if touch: + print '================ Creating additional package for "', package_id, '" in ', args['dest'] + else: + print '================ Starting additional copy for "', package_id, '" in ', args['dest'] try: - if package_id + "_viewer_channel_suffix" in os.environ: - args['channel_suffix'] = os.environ[package_id + "_viewer_channel_suffix"] - else: - args['channel_suffix'] = None - if package_id + "_sourceid" in os.environ: - args['sourceid'] = os.environ[package_id + "_sourceid"] - else: - args['sourceid'] = None - args['dest'] = base_dest_prefix + os.sep + package_id + os.sep + base_dest_postfix - except KeyError: - sys.stderr.write("Failed to create package for package_id: %s" % package_id) - sys.stderr.flush() - continue + wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) + wm.do(*args['actions']) + except Exception as err: + sys.exit(str(err)) if touch: - print 'Creating additional package for "', package_id, '" in ', args['dest'] - wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) - wm.do(*args['actions']) - if touch: - print 'Created additional package ', wm.package_file, ' for ', package_id - faketouch = base_touch_prefix + '/' + package_id + '/' + base_touch_postfix - fp = open(faketouch, 'w') - fp.write('set package_file=%s\n' % wm.package_file) - fp.close() - + print '================ Created additional package ', wm.package_file, ' for ', package_id + with open(base_touch_template.format(package_id), 'w') as fp: + fp.write('set package_file=%s\n' % wm.package_file) + else: + print '================ Finished additional copy "', package_id, '" in ', args['dest'] # Write out the package file in this format, so that it can easily be called # and used in a .bat file - yeah, it sucks, but this is the simplest... - touch = args.get('touch') if touch: - fp = open(touch, 'w') - fp.write('set package_file=%s\n' % base_package_file) - fp.close() + with open(touch, 'w') as fp: + fp.write('set package_file=%s\n' % base_package_file) print 'touched', touch return 0 @@ -371,22 +348,113 @@ class LLManifest(object): in the file list by path().""" self.excludes.append(glob) - def prefix(self, src='', build=None, dst=None): - """ Pushes a prefix onto the stack. Until end_prefix is - called, all relevant method calls (esp. to path()) will prefix - paths with the entire prefix stack. Source and destination - prefixes can be different, though if only one is provided they - are both equal. To specify a no-op, use an empty string, not - None.""" - if dst is None: - dst = src - if build is None: - build = src + def prefix(self, src='', build='', dst='', src_dst=None): + """ + Usage: + + with self.prefix(...args as described...): + self.path(...) + + For the duration of the 'with' block, pushes a prefix onto the stack. + Within that block, all relevant method calls (esp. to path()) will + prefix paths with the entire prefix stack. Source and destination + prefixes are independent; if omitted (or passed as the empty string), + the prefix has no effect. Thus: + + with self.prefix(src='foo'): + # no effect on dst + + with self.prefix(dst='bar'): + # no effect on src + + If you want to set both at once, use src_dst: + + with self.prefix(src_dst='subdir'): + # same as self.prefix(src='subdir', dst='subdir') + # Passing src_dst makes any src or dst argument in the same + # parameter list irrelevant. + + Also supports the older (pre-Python-2.5) syntax: + + if self.prefix(...args as described...): + self.path(...) + self.end_prefix(...) + + Before the arrival of the 'with' statement, one was required to code + self.prefix() and self.end_prefix() in matching pairs to push and to + pop the prefix stacks, respectively. The older prefix() method + returned True specifically so that the caller could indent the + relevant block of code with 'if', just for aesthetic purposes. + """ + if src_dst is not None: + src = src_dst + dst = src_dst self.src_prefix.append(src) self.artwork_prefix.append(src) self.build_prefix.append(build) self.dst_prefix.append(dst) - return True # so that you can wrap it in an if to get indentation + +## self.display_stacks() + + # The above code is unchanged from the original implementation. What's + # new is the return value. We're going to return an instance of + # PrefixManager that binds this LLManifest instance and Does The Right + # Thing on exit. + return self.PrefixManager(self) + + def display_stacks(self): + width = 1 + max(len(stack) for stack in self.PrefixManager.stacks) + for stack in self.PrefixManager.stacks: + print "{} {}".format((stack + ':').ljust(width), + os.path.join(*getattr(self, stack))) + + class PrefixManager(object): + # stack attributes we manage in this LLManifest (sub)class + # instance + stacks = ("src_prefix", "artwork_prefix", "build_prefix", "dst_prefix") + + def __init__(self, manifest): + self.manifest = manifest + # If the caller wrote: + # with self.prefix(...): + # as intended, then bind the state of each prefix stack as it was + # just BEFORE the call to prefix(). Since prefix() appended an + # entry to each prefix stack, capture len()-1. + self.prevlen = { stack: len(getattr(self.manifest, stack)) - 1 + for stack in self.stacks } + + def __nonzero__(self): + # If the caller wrote: + # if self.prefix(...): + # then a value of this class had better evaluate as 'True'. + return True + + def __enter__(self): + # nobody uses 'with self.prefix(...) as variable:' + return None + + def __exit__(self, type, value, traceback): + # First, if the 'with' block raised an exception, just propagate. + # Do NOT swallow it. + if type is not None: + return False + + # Okay, 'with' block completed successfully. Restore previous + # state of each of the prefix stacks in self.stacks. + # Note that we do NOT simply call pop() on them as end_prefix() + # does. This is to cope with the possibility that the coder + # changed 'if self.prefix(...):' to 'with self.prefix(...):' yet + # forgot to remove the self.end_prefix(...) call at the bottom of + # the block. In that case, calling pop() again would be Bad! But + # if we restore the length of each stack to what it was before the + # current prefix() block, it doesn't matter whether end_prefix() + # was called or not. + for stack, prevlen in self.prevlen.items(): + # find the attribute in 'self.manifest' named by 'stack', and + # truncate that list back to 'prevlen' + del getattr(self.manifest, stack)[prevlen:] + +## self.manifest.display_stacks() def end_prefix(self, descr=None): """Pops a prefix off the stack. If given an argument, checks @@ -433,6 +501,19 @@ class LLManifest(object): relative to the destination directory.""" return os.path.join(self.get_dst_prefix(), relpath) + def _relative_dst_path(self, dstpath): + """ + Returns the path to a file or directory relative to the destination directory. + This should only be used for generating diagnostic output in the path method. + """ + dest_root=self.dst_prefix[0] + if dstpath.startswith(dest_root+os.path.sep): + return dstpath[len(dest_root)+1:] + elif dstpath.startswith(dest_root): + return dstpath[len(dest_root):] + else: + return dstpath + def ensure_src_dir(self, reldir): """Construct the path for a directory relative to the source path, and ensures that it exists. Returns the @@ -450,29 +531,17 @@ class LLManifest(object): return path def run_command(self, command): - """ Runs an external command, and returns the output. Raises - an exception if the command returns a nonzero status code. For - debugging/informational purposes, prints out the command's - output as it is received.""" + """ + Runs an external command. + Raises ManifestError exception if the command returns a nonzero status. + """ print "Running command:", command sys.stdout.flush() - child = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - shell=True) - lines = [] - while True: - lines.append(child.stdout.readline()) - if lines[-1] == '': - break - else: - print lines[-1], - output = ''.join(lines) - child.stdout.close() - status = child.wait() - if status: - raise ManifestError( - "Command %s returned non-zero status (%s) \noutput:\n%s" - % (command, status, output) ) - return output + try: + subprocess.check_call(command) + except subprocess.CalledProcessError as err: + raise ManifestError( "Command %s returned non-zero status (%s)" + % (command, err.returncode) ) def created_path(self, path): """ Declare that you've created a path in order to @@ -485,6 +554,7 @@ class LLManifest(object): def put_in_file(self, contents, dst, src=None): # write contents as dst dst_path = self.dst_path_of(dst) + self.cmakedirs(os.path.dirname(dst_path)) f = open(dst_path, "wb") try: f.write(contents) @@ -546,9 +616,16 @@ class LLManifest(object): # *TODO is this gonna be useful? print "Cleaning up " + c + def process_either(self, src, dst): + # If it's a real directory, recurse through it -- + # but not a symlink! Handle those like files. + if os.path.isdir(src) and not os.path.islink(src): + return self.process_directory(src, dst) + else: + return self.process_file(src, dst) + def process_file(self, src, dst): if self.includes(src, dst): -# print src, "=>", dst for action in self.actions: methodname = action + "_action" method = getattr(self, methodname, None) @@ -573,10 +650,7 @@ class LLManifest(object): for name in names: srcname = os.path.join(src, name) dstname = os.path.join(dst, name) - if os.path.isdir(srcname): - count += self.process_directory(srcname, dstname) - else: - count += self.process_file(srcname, dstname) + count += self.process_either(srcname, dstname) return count def includes(self, src, dst): @@ -616,16 +690,21 @@ class LLManifest(object): # Don't recopy file if it's up-to-date. # If we seem to be not not overwriting files that have been # updated, set the last arg to False, but it will take longer. +## reldst = (dst[len(self.dst_prefix[0]):] +## if dst.startswith(self.dst_prefix[0]) +## else dst).lstrip(r'\/') if os.path.exists(dst) and filecmp.cmp(src, dst, True): +## print "{} (skipping, {} exists)".format(src, reldst) return # only copy if it's not excluded if self.includes(src, dst): try: os.unlink(dst) - except OSError, err: + except OSError as err: if err.errno != errno.ENOENT: raise +## print "{} => {}".format(src, reldst) shutil.copy2(src, dst) def ccopytree(self, src, dst): @@ -643,7 +722,7 @@ class LLManifest(object): dstname = os.path.join(dst, name) try: self.ccopymumble(srcname, dstname) - except (IOError, os.error), why: + except (IOError, os.error) as why: errors.append((srcname, dstname, why)) if errors: raise ManifestError, errors @@ -724,13 +803,13 @@ class LLManifest(object): return self.path(os.path.join(path, file), file) def path(self, src, dst=None): - sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.flush() if src == None: raise ManifestError("No source file, dst is " + dst) if dst == None: dst = src dst = os.path.join(self.get_dst_prefix(), dst) + sys.stdout.write("Processing %s => %s ... " % (src, self._relative_dst_path(dst))) def try_path(src): # expand globs @@ -743,29 +822,21 @@ class LLManifest(object): # if we're specifying a single path (not a glob), # we should error out if it doesn't exist self.check_file_exists(src) - # if it's a directory, recurse through it - if os.path.isdir(src): - count += self.process_directory(src, dst) - else: - count += self.process_file(src, dst) + count += self.process_either(src, dst) return count - for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix(): + try_prefixes = [self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix()] + tried=[] + count=0 + while not count and try_prefixes: + pfx = try_prefixes.pop(0) try: count = try_path(os.path.join(pfx, src)) except MissingError: - # If src isn't a wildcard, and if that file doesn't exist in - # this pfx, try next pfx. - count = 0 - continue - - # Here try_path() didn't raise MissingError. Did it process any files? - if count: - break - # Even though try_path() didn't raise MissingError, it returned 0 - # files. src is probably a wildcard meant for some other pfx. Loop - # back to try the next. - + tried.append(pfx) + if not try_prefixes: + # no more prefixes left to try + print "unable to find '%s'; looked in:\n %s" % (src, '\n '.join(tried)) print "%d files" % count # Let caller check whether we processed as many files as expected. In diff --git a/indra/lib/python/indra/util/llperformance.py b/indra/lib/python/indra/util/llperformance.py deleted file mode 100755 index 57dd64de3..000000000 --- a/indra/lib/python/indra/util/llperformance.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python -"""\ -@file llperformance.py - -$LicenseInfo:firstyear=2010&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2010-2011, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -# ------------------------------------------------ -# Sim metrics utility functions. - -import glob, os, time, sys, stat, exceptions - -from indra.base import llsd - -gBlockMap = {} #Map of performance metric data with function hierarchy information. -gCurrentStatPath = "" - -gIsLoggingEnabled=False - -class LLPerfStat: - def __init__(self,key): - self.mTotalTime = 0 - self.mNumRuns = 0 - self.mName=key - self.mTimeStamp = int(time.time()*1000) - self.mUTCTime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) - - def __str__(self): - return "%f" % self.mTotalTime - - def start(self): - self.mStartTime = int(time.time() * 1000000) - self.mNumRuns += 1 - - def stop(self): - execution_time = int(time.time() * 1000000) - self.mStartTime - self.mTotalTime += execution_time - - def get_map(self): - results={} - results['name']=self.mName - results['utc_time']=self.mUTCTime - results['timestamp']=self.mTimeStamp - results['us']=self.mTotalTime - results['count']=self.mNumRuns - return results - -class PerfError(exceptions.Exception): - def __init__(self): - return - - def __Str__(self): - print "","Unfinished LLPerfBlock" - -class LLPerfBlock: - def __init__( self, key ): - global gBlockMap - global gCurrentStatPath - global gIsLoggingEnabled - - #Check to see if we're running metrics right now. - if gIsLoggingEnabled: - self.mRunning = True #Mark myself as running. - - self.mPreviousStatPath = gCurrentStatPath - gCurrentStatPath += "/" + key - if gCurrentStatPath not in gBlockMap: - gBlockMap[gCurrentStatPath] = LLPerfStat(key) - - self.mStat = gBlockMap[gCurrentStatPath] - self.mStat.start() - - def finish( self ): - global gBlockMap - global gIsLoggingEnabled - - if gIsLoggingEnabled: - self.mStat.stop() - self.mRunning = False - gCurrentStatPath = self.mPreviousStatPath - -# def __del__( self ): -# if self.mRunning: -# #SPATTERS FIXME -# raise PerfError - -class LLPerformance: - #-------------------------------------------------- - # Determine whether or not we want to log statistics - - def __init__( self, process_name = "python" ): - self.process_name = process_name - self.init_testing() - self.mTimeStamp = int(time.time()*1000) - self.mUTCTime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) - - def init_testing( self ): - global gIsLoggingEnabled - - host_performance_file = "/dev/shm/simperf/simperf_proc_config.llsd" - - #If file exists, open - if os.path.exists(host_performance_file): - file = open (host_performance_file,'r') - - #Read serialized LLSD from file. - body = llsd.parse(file.read()) - - #Calculate time since file last modified. - stats = os.stat(host_performance_file) - now = time.time() - mod = stats[stat.ST_MTIME] - age = now - mod - - if age < ( body['duration'] ): - gIsLoggingEnabled = True - - - def get ( self ): - global gIsLoggingEnabled - return gIsLoggingEnabled - - #def output(self,ptr,path): - # if 'stats' in ptr: - # stats = ptr['stats'] - # self.mOutputPtr[path] = stats.get_map() - - # if 'children' in ptr: - # children=ptr['children'] - - # curptr = self.mOutputPtr - # curchildren={} - # curptr['children'] = curchildren - - # for key in children: - # curchildren[key]={} - # self.mOutputPtr = curchildren[key] - # self.output(children[key],path + '/' + key) - - def done(self): - global gBlockMap - - if not self.get(): - return - - output_name = "/dev/shm/simperf/%s_proc.%d.llsd" % (self.process_name, os.getpid()) - output_file = open(output_name, 'w') - process_info = { - "name" : self.process_name, - "pid" : os.getpid(), - "ppid" : os.getppid(), - "timestamp" : self.mTimeStamp, - "utc_time" : self.mUTCTime, - } - output_file.write(llsd.format_notation(process_info)) - output_file.write('\n') - - for key in gBlockMap.keys(): - gBlockMap[key] = gBlockMap[key].get_map() - output_file.write(llsd.format_notation(gBlockMap)) - output_file.write('\n') - output_file.close() - diff --git a/indra/lib/python/indra/util/llsubprocess.py b/indra/lib/python/indra/util/llsubprocess.py deleted file mode 100644 index 7e0e115d1..000000000 --- a/indra/lib/python/indra/util/llsubprocess.py +++ /dev/null @@ -1,117 +0,0 @@ -"""\ -@file llsubprocess.py -@author Phoenix -@date 2008-01-18 -@brief The simplest possible wrapper for a common sub-process paradigm. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import os -import popen2 -import time -import select - -class Timeout(RuntimeError): - "Exception raised when a subprocess times out." - pass - -def run(command, args=None, data=None, timeout=None): - """\ -@brief Run command with arguments - -This is it. This is the function I want to run all the time when doing -subprocces, but end up copying the code everywhere. none of the -standard commands are secure and provide a way to specify input, get -all the output, and get the result. -@param command A string specifying a process to launch. -@param args Arguments to be passed to command. Must be list, tuple or None. -@param data input to feed to the command. -@param timeout Maximum number of many seconds to run. -@return Returns (result, stdout, stderr) from process. -""" - cmd = [command] - if args: - cmd.extend([str(arg) for arg in args]) - #print "cmd: ","' '".join(cmd) - child = popen2.Popen3(cmd, True) - #print child.pid - out = [] - err = [] - result = -1 - time_left = timeout - tochild = [child.tochild.fileno()] - while True: - time_start = time.time() - #print "time:",time_left - p_in, p_out, p_err = select.select( - [child.fromchild.fileno(), child.childerr.fileno()], - tochild, - [], - time_left) - if p_in: - new_line = os.read(child.fromchild.fileno(), 32 * 1024) - if new_line: - #print "line:",new_line - out.append(new_line) - new_line = os.read(child.childerr.fileno(), 32 * 1024) - if new_line: - #print "error:", new_line - err.append(new_line) - if p_out: - if data: - #print "p_out" - bytes = os.write(child.tochild.fileno(), data) - data = data[bytes:] - if len(data) == 0: - data = None - tochild = [] - child.tochild.close() - result = child.poll() - if result != -1: - # At this point, the child process has exited and result - # is the return value from the process. Between the time - # we called select() and poll() the process may have - # exited so read all the data left on the child process - # stdout and stderr. - last = child.fromchild.read() - if last: - out.append(last) - last = child.childerr.read() - if last: - err.append(last) - child.tochild.close() - child.fromchild.close() - child.childerr.close() - break - if time_left is not None: - time_left -= (time.time() - time_start) - if time_left < 0: - raise Timeout - #print "result:",result - out = ''.join(out) - #print "stdout:", out - err = ''.join(err) - #print "stderr:", err - return result, out, err diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py deleted file mode 100644 index 6bf956107..000000000 --- a/indra/lib/python/indra/util/named_query.py +++ /dev/null @@ -1,592 +0,0 @@ -"""\ -@file named_query.py -@author Ryan Williams, Phoenix -@date 2007-07-31 -@brief An API for running named queries. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import errno -import MySQLdb -import MySQLdb.cursors -import os -import os.path -import re -import time - -from indra.base import llsd -from indra.base import config - -DEBUG = False -NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') -NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) - -_g_named_manager = None - -def _init_g_named_manager(sql_dir = None): - """Initializes a global NamedManager object to point at a - specified named queries hierarchy. - - This function is intended entirely for testing purposes, - because it's tricky to control the config from inside a test.""" - global NQ_FILE_SUFFIX - NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') - global NQ_FILE_SUFFIX_LEN - NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) - - if sql_dir is None: - sql_dir = config.get('named-query-base-dir') - - # extra fallback directory in case config doesn't return what we want - if sql_dir is None: - sql_dir = os.path.abspath( - os.path.join( - os.path.realpath(os.path.dirname(__file__)), "..", "..", "..", "..", "web", "dataservice", "sql")) - - global _g_named_manager - _g_named_manager = NamedQueryManager( - os.path.abspath(os.path.realpath(sql_dir))) - -def get(name, schema = None): - "Get the named query object to be used to perform queries" - if _g_named_manager is None: - _init_g_named_manager() - return _g_named_manager.get(name).for_schema(schema) - -def sql(connection, name, params): - # use module-global NamedQuery object to perform default substitution - return get(name).sql(connection, params) - -def run(connection, name, params, expect_rows = None): - """\ -@brief given a connection, run a named query with the params - -Note that this function will fetch ALL rows. -@param connection The connection to use -@param name The name of the query to run -@param params The parameters passed into the query -@param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack. -@return Returns the result set as a list of dicts. -""" - return get(name).run(connection, params, expect_rows) - -class ExpectationFailed(Exception): - """ Exception that is raised when an expectation for an sql query - is not met.""" - def __init__(self, message): - Exception.__init__(self, message) - self.message = message - -class NamedQuery(object): - def __init__(self, name, filename): - """ Construct a NamedQuery object. The name argument is an - arbitrary name as a handle for the query, and the filename is - a path to a file or a file-like object containing an llsd named - query document.""" - self._stat_interval_seconds = 5 # 5 seconds - self._name = name - if (filename is not None and isinstance(filename, (str, unicode)) - and NQ_FILE_SUFFIX != filename[-NQ_FILE_SUFFIX_LEN:]): - filename = filename + NQ_FILE_SUFFIX - self._location = filename - self._alternative = dict() - self._last_mod_time = 0 - self._last_check_time = 0 - self.deleted = False - self.load_contents() - - def name(self): - """ The name of the query. """ - return self._name - - def get_modtime(self): - """ Returns the mtime (last modified time) of the named query - filename. For file-like objects, expect a modtime of 0""" - if self._location and isinstance(self._location, (str, unicode)): - return os.path.getmtime(self._location) - return 0 - - def load_contents(self): - """ Loads and parses the named query file into self. Does - nothing if self.location is nonexistant.""" - if self._location: - if isinstance(self._location, (str, unicode)): - contents = llsd.parse(open(self._location).read()) - else: - # we probably have a file-like object. Godspeed! - contents = llsd.parse(self._location.read()) - self._reference_contents(contents) - # Check for alternative implementations - try: - for name, alt in self._contents['alternative'].items(): - nq = NamedQuery(name, None) - nq._reference_contents(alt) - self._alternative[name] = nq - except KeyError, e: - pass - self._last_mod_time = self.get_modtime() - self._last_check_time = time.time() - - def _reference_contents(self, contents): - "Helper method which builds internal structure from parsed contents" - self._contents = contents - self._ttl = int(self._contents.get('ttl', 0)) - self._return_as_map = bool(self._contents.get('return_as_map', False)) - self._legacy_dbname = self._contents.get('legacy_dbname', None) - - # reset these before doing the sql conversion because we will - # read them there. reset these while loading so we pick up - # changes. - self._around = set() - self._append = set() - self._integer = set() - self._options = self._contents.get('dynamic_where', {}) - for key in self._options: - if isinstance(self._options[key], basestring): - self._options[key] = self._convert_sql(self._options[key]) - elif isinstance(self._options[key], list): - lines = [] - for line in self._options[key]: - lines.append(self._convert_sql(line)) - self._options[key] = lines - else: - moreopt = {} - for kk in self._options[key]: - moreopt[kk] = self._convert_sql(self._options[key][kk]) - self._options[key] = moreopt - self._base_query = self._convert_sql(self._contents['base_query']) - self._query_suffix = self._convert_sql( - self._contents.get('query_suffix', '')) - - def _convert_sql(self, sql): - """convert the parsed sql into a useful internal structure. - - This function has to turn the named query format into a pyformat - style. It also has to look for %:name% and :name% and - ready them for use in LIKE statements""" - if sql: - # This first sub is to properly escape any % signs that - # are meant to be literally passed through to mysql in the - # query. It leaves any %'s that are used for - # like-expressions. - expr = re.compile("(?<=[^a-zA-Z0-9_-])%(?=[^:])") - sql = expr.sub('%%', sql) - - # This should tackle the rest of the %'s in the query, by - # converting them to LIKE clauses. - expr = re.compile("(%?):([a-zA-Z][a-zA-Z0-9_-]*)%") - sql = expr.sub(self._prepare_like, sql) - expr = re.compile("#:([a-zA-Z][a-zA-Z0-9_-]*)") - sql = expr.sub(self._prepare_integer, sql) - expr = re.compile(":([a-zA-Z][a-zA-Z0-9_-]*)") - sql = expr.sub("%(\\1)s", sql) - return sql - - def _prepare_like(self, match): - """This function changes LIKE statement replace behavior - - It works by turning %:name% to %(_name_around)s and :name% to - %(_name_append)s. Since a leading '_' is not a valid keyname - input (enforced via unit tests), it will never clash with - existing keys. Then, when building the statement, the query - runner will generate corrected strings.""" - if match.group(1) == '%': - # there is a leading % so this is treated as prefix/suffix - self._around.add(match.group(2)) - return "%(" + self._build_around_key(match.group(2)) + ")s" - else: - # there is no leading %, so this is suffix only - self._append.add(match.group(2)) - return "%(" + self._build_append_key(match.group(2)) + ")s" - - def _build_around_key(self, key): - return "_" + key + "_around" - - def _build_append_key(self, key): - return "_" + key + "_append" - - def _prepare_integer(self, match): - """This function adjusts the sql for #:name replacements - - It works by turning #:name to %(_name_as_integer)s. Since a - leading '_' is not a valid keyname input (enforced via unit - tests), it will never clash with existing keys. Then, when - building the statement, the query runner will generate - corrected strings.""" - self._integer.add(match.group(1)) - return "%(" + self._build_integer_key(match.group(1)) + ")s" - - def _build_integer_key(self, key): - return "_" + key + "_as_integer" - - def _strip_wildcards_to_list(self, value): - """Take string, and strip out the LIKE special characters. - - Technically, this is database dependant, but postgresql and - mysql use the same wildcards, and I am not aware of a general - way to handle this. I think you need a sql statement of the - form: - - LIKE_STRING( [ANY,ONE,str]... ) - - which would treat ANY as their any string, and ONE as their - single glyph, and str as something that needs database - specific encoding to not allow any % or _ to affect the query. - - As it stands, I believe it's impossible to write a named query - style interface which uses like to search the entire space of - text available. Imagine the query: - - % of brain used by average linden - - In order to search for %, it must be escaped, so once you have - escaped the string to not do wildcard searches, and be escaped - for the database, and then prepended the wildcard you come - back with one of: - - 1) %\% of brain used by average linden - 2) %%% of brain used by average linden - - Then, when passed to the database to be escaped to be database - safe, you get back: - - 1) %\\% of brain used by average linden - : which means search for any character sequence, followed by a - backslash, followed by any sequence, followed by ' of - brain...' - 2) %%% of brain used by average linden - : which (I believe) means search for a % followed by any - character sequence followed by 'of brain...' - - Neither of which is what we want! - - So, we need a vendor (or extention) for LIKE_STRING. Anyone - want to write it?""" - if isinstance(value, unicode): - utf8_value = value - else: - utf8_value = unicode(value, "utf-8") - esc_list = [] - remove_chars = set(u"%_") - for glyph in utf8_value: - if glyph in remove_chars: - continue - esc_list.append(glyph.encode("utf-8")) - return esc_list - - def delete(self): - """ Makes this query unusable by deleting all the members and - setting the deleted member. This is desired when the on-disk - query has been deleted but the in-memory copy remains.""" - # blow away all members except _name, _location, and deleted - name, location = self._name, self._location - for key in self.__dict__.keys(): - del self.__dict__[key] - self.deleted = True - self._name, self._location = name, location - - def ttl(self): - """ Estimated time to live of this query. Used for web - services to set the Expires header.""" - return self._ttl - - def legacy_dbname(self): - return self._legacy_dbname - - def return_as_map(self): - """ Returns true if this query is configured to return its - results as a single map (as opposed to a list of maps, the - normal behavior).""" - - return self._return_as_map - - def for_schema(self, db_name): - "Look trough the alternates and return the correct query" - if db_name is None: - return self - try: - return self._alternative[db_name] - except KeyError, e: - pass - return self - - def run(self, connection, params, expect_rows = None, use_dictcursor = True): - """given a connection, run a named query with the params - - Note that this function will fetch ALL rows. We do this because it - opens and closes the cursor to generate the values, and this - isn't a generator so the cursor has no life beyond the method call. - - @param cursor The connection to use (this generates its own cursor for the query) - @param name The name of the query to run - @param params The parameters passed into the query - @param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack. - @param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts. - @return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict. - """ - if use_dictcursor: - cursor = connection.cursor(MySQLdb.cursors.DictCursor) - else: - cursor = connection.cursor() - - full_query, params = self._construct_sql(params) - if DEBUG: - print "SQL:", self.sql(connection, params) - rows = cursor.execute(full_query, params) - - # *NOTE: the expect_rows argument is a very cheesy way to get some - # validation on the result set. If you want to add more expectation - # logic, do something more object-oriented and flexible. Or use an ORM. - if(self._return_as_map): - expect_rows = 1 - if expect_rows is not None and rows != expect_rows: - cursor.close() - raise ExpectationFailed("Statement expected %s rows, got %s. Sql: '%s' %s" % ( - expect_rows, rows, full_query, params)) - - # convert to dicts manually if we're not using a dictcursor - if use_dictcursor: - result_set = cursor.fetchall() - else: - if cursor.description is None: - # an insert or something - x = cursor.fetchall() - cursor.close() - return x - - names = [x[0] for x in cursor.description] - - result_set = [] - for row in cursor.fetchall(): - converted_row = {} - for idx, col_name in enumerate(names): - converted_row[col_name] = row[idx] - result_set.append(converted_row) - - cursor.close() - if self._return_as_map: - return result_set[0] - return result_set - - def _construct_sql(self, params): - """ Returns a query string and a dictionary of parameters, - suitable for directly passing to the execute() method.""" - self.refresh() - - # build the query from the options available and the params - base_query = [] - base_query.append(self._base_query) - for opt, extra_where in self._options.items(): - if type(extra_where) in (dict, list, tuple): - if opt in params: - base_query.append(extra_where[params[opt]]) - else: - if opt in params and params[opt]: - base_query.append(extra_where) - if self._query_suffix: - base_query.append(self._query_suffix) - full_query = '\n'.join(base_query) - - # Go through the query and rewrite all of the ones with the - # @:name syntax. - rewrite = _RewriteQueryForArray(params) - expr = re.compile("@%\(([a-zA-Z][a-zA-Z0-9_-]*)\)s") - full_query = expr.sub(rewrite.operate, full_query) - params.update(rewrite.new_params) - - # build out the params for like. We only have to do this - # parameters which were detected to have ued the where syntax - # during load. - # - # * treat the incoming string as utf-8 - # * strip wildcards - # * append or prepend % as appropriate - new_params = {} - for key in params: - if key in self._around: - new_value = ['%'] - new_value.extend(self._strip_wildcards_to_list(params[key])) - new_value.append('%') - new_params[self._build_around_key(key)] = ''.join(new_value) - if key in self._append: - new_value = self._strip_wildcards_to_list(params[key]) - new_value.append('%') - new_params[self._build_append_key(key)] = ''.join(new_value) - if key in self._integer: - new_params[self._build_integer_key(key)] = int(params[key]) - params.update(new_params) - - return full_query, params - - def sql(self, connection, params): - """ Generates an SQL statement from the named query document - and a dictionary of parameters. - - *NOTE: Only use for debugging, because it uses the - non-standard MySQLdb 'literal' method. - """ - if not DEBUG: - import warnings - warnings.warn("Don't use named_query.sql() when not debugging. Used on %s" % self._location) - # do substitution using the mysql (non-standard) 'literal' - # function to do the escaping. - full_query, params = self._construct_sql(params) - return full_query % connection.literal(params) - - - def refresh(self): - """ Refresh self from the file on the filesystem. - - This is optimized to be callable as frequently as you wish, - without adding too much load. It does so by only stat-ing the - file every N seconds, where N defaults to 5 and is - configurable through the member _stat_interval_seconds. If the stat - reveals that the file has changed, refresh will re-parse the - contents of the file and use them to update the named query - instance. If the stat reveals that the file has been deleted, - refresh will call self.delete to make the in-memory - representation unusable.""" - now = time.time() - if(now - self._last_check_time > self._stat_interval_seconds): - self._last_check_time = now - try: - modtime = self.get_modtime() - if(modtime > self._last_mod_time): - self.load_contents() - except OSError, e: - if e.errno == errno.ENOENT: # file not found - self.delete() # clean up self - raise # pass the exception along to the caller so they know that this query disappeared - -class NamedQueryManager(object): - """ Manages the lifespan of NamedQuery objects, drawing from a - directory hierarchy of named query documents. - - In practice this amounts to a memory cache of NamedQuery objects.""" - - def __init__(self, named_queries_dir): - """ Initializes a manager to look for named queries in a - directory.""" - self._dir = os.path.abspath(os.path.realpath(named_queries_dir)) - self._cached_queries = {} - - def sql(self, connection, name, params): - nq = self.get(name) - return nq.sql(connection, params) - - def get(self, name): - """ Returns a NamedQuery instance based on the name, either - from memory cache, or by parsing from disk. - - The name is simply a relative path to the directory associated - with the manager object. Before returning the instance, the - NamedQuery object is cached in memory, so that subsequent - accesses don't have to read from disk or do any parsing. This - means that NamedQuery objects returned by this method are - shared across all users of the manager object. - NamedQuery.refresh is used to bring the NamedQuery objects in - sync with the actual files on disk.""" - nq = self._cached_queries.get(name) - if nq is None: - nq = NamedQuery(name, os.path.join(self._dir, name)) - self._cached_queries[name] = nq - else: - try: - nq.refresh() - except OSError, e: - if e.errno == errno.ENOENT: # file not found - del self._cached_queries[name] - raise # pass exception along to caller so they know that the query disappeared - - return nq - -class _RewriteQueryForArray(object): - "Helper class for rewriting queries with the @:name syntax" - def __init__(self, params): - self.params = params - self.new_params = dict() - - def operate(self, match): - "Given a match, return the string that should be in use" - key = match.group(1) - value = self.params[key] - if type(value) in (list,tuple): - rv = [] - for idx in range(len(value)): - # if the value@idx is array-like, we are - # probably dealing with a VALUES - new_key = "_%s_%s"%(key, str(idx)) - val_item = value[idx] - if type(val_item) in (list, tuple, dict): - if type(val_item) is dict: - # this is because in Python, the order of - # key, value retrieval from the dict is not - # guaranteed to match what the input intended - # and for VALUES, order is important. - # TODO: Implemented ordered dict in LLSD parser? - raise ExpectationFailed('Only lists/tuples allowed,\ - received dict') - values_keys = [] - for value_idx, item in enumerate(val_item): - # we want a key of the format : - # key_#replacement_#value_row_#value_col - # ugh... so if we are replacing 10 rows in user_note, - # the first values clause would read (for @:user_notes) :- - # ( :_user_notes_0_1_1, :_user_notes_0_1_2, :_user_notes_0_1_3 ) - # the input LLSD for VALUES will look like: - # ... - # - # user_notes - # - # - # ... - # ... - # ... - # - # ... - # - # - # ... - values_key = "%s_%s"%(new_key, value_idx) - self.new_params[values_key] = item - values_keys.append("%%(%s)s"%values_key) - # now collapse all these new place holders enclosed in () - # from [':_key_0_1_1', ':_key_0_1_2', ':_key_0_1_3,...] - # rv will have [ '(:_key_0_1_1, :_key_0_1_2, :_key_0_1_3)', ] - # which is flattened a few lines below join(rv) - rv.append('(%s)' % ','.join(values_keys)) - else: - self.new_params[new_key] = val_item - rv.append("%%(%s)s"%new_key) - return ','.join(rv) - else: - # not something that can be expanded, so just drop the - # leading @ in the front of the match. This will mean that - # the single value we have, be it a string, int, whatever - # (other than dict) will correctly show up, eg: - # - # where foo in (@:foobar) -- foobar is a string, so we get - # where foo in (:foobar) - return match.group(0)[1:] diff --git a/indra/lib/python/indra/util/shutil2.py b/indra/lib/python/indra/util/shutil2.py deleted file mode 100644 index 9e2e7a6de..000000000 --- a/indra/lib/python/indra/util/shutil2.py +++ /dev/null @@ -1,84 +0,0 @@ -''' -@file shutil2.py -@brief a better shutil.copytree replacement - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -''' - -# -# shutil2.py -# Taken from http://www.scons.org/wiki/AccumulateBuilder -# the stock copytree sucks because it insists that the -# target dir not exist -# - -import os.path -import shutil - -def copytree(src, dest, symlinks=False): - """My own copyTree which does not fail if the directory exists. - - Recursively copy a directory tree using copy2(). - - If the optional symlinks flag is true, symbolic links in the - source tree result in symbolic links in the destination tree; if - it is false, the contents of the files pointed to by symbolic - links are copied. - - Behavior is meant to be identical to GNU 'cp -R'. - """ - def copyItems(src, dest, symlinks=False): - """Function that does all the work. - - It is necessary to handle the two 'cp' cases: - - destination does exist - - destination does not exist - - See 'cp -R' documentation for more details - """ - for item in os.listdir(src): - srcPath = os.path.join(src, item) - if os.path.isdir(srcPath): - srcBasename = os.path.basename(srcPath) - destDirPath = os.path.join(dest, srcBasename) - if not os.path.exists(destDirPath): - os.makedirs(destDirPath) - copyItems(srcPath, destDirPath) - elif os.path.islink(item) and symlinks: - linkto = os.readlink(item) - os.symlink(linkto, dest) - else: - shutil.copy2(srcPath, dest) - - # case 'cp -R src/ dest/' where dest/ already exists - if os.path.exists(dest): - destPath = os.path.join(dest, os.path.basename(src)) - if not os.path.exists(destPath): - os.makedirs(destPath) - # case 'cp -R src/ dest/' where dest/ does not exist - else: - os.makedirs(dest) - destPath = dest - # actually copy the files - copyItems(src, destPath) diff --git a/indra/lib/python/indra/util/simperf_host_xml_parser.py b/indra/lib/python/indra/util/simperf_host_xml_parser.py deleted file mode 100755 index 672c1050c..000000000 --- a/indra/lib/python/indra/util/simperf_host_xml_parser.py +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/env python -"""\ -@file simperf_host_xml_parser.py -@brief Digest collector's XML dump and convert to simple dict/list structure - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys, os, getopt, time -import simplejson -from xml import sax - - -def usage(): - print "Usage:" - print sys.argv[0] + " [options]" - print " Convert RRD's XML dump to JSON. Script to convert the simperf_host_collector-" - print " generated RRD dump into JSON. Steps include converting selected named" - print " fields from GAUGE type to COUNTER type by computing delta with preceding" - print " values. Top-level named fields are:" - print - print " lastupdate Time (javascript timestamp) of last data sample" - print " step Time in seconds between samples" - print " ds Data specification (name/type) for each column" - print " database Table of data samples, one time step per row" - print - print "Options:" - print " -i, --in Input settings filename. (Default: stdin)" - print " -o, --out Output settings filename. (Default: stdout)" - print " -h, --help Print this message and exit." - print - print "Example: %s -i rrddump.xml -o rrddump.json" % sys.argv[0] - print - print "Interfaces:" - print " class SimPerfHostXMLParser() # SAX content handler" - print " def simperf_host_xml_fixup(parser) # post-parse value fixup" - -class SimPerfHostXMLParser(sax.handler.ContentHandler): - - def __init__(self): - pass - - def startDocument(self): - self.rrd_last_update = 0 # public - self.rrd_step = 0 # public - self.rrd_ds = [] # public - self.rrd_records = [] # public - self._rrd_level = 0 - self._rrd_parse_state = 0 - self._rrd_chars = "" - self._rrd_capture = False - self._rrd_ds_val = {} - self._rrd_data_row = [] - self._rrd_data_row_has_nan = False - - def endDocument(self): - pass - - # Nasty little ad-hoc state machine to extract the elements that are - # necessary from the 'rrdtool dump' XML output. The same element - # name '' is used for two different data sets so we need to pay - # some attention to the actual structure to get the ones we want - # and ignore the ones we don't. - - def startElement(self, name, attrs): - self._rrd_level = self._rrd_level + 1 - self._rrd_capture = False - if self._rrd_level == 1: - if name == "rrd" and self._rrd_parse_state == 0: - self._rrd_parse_state = 1 # In - self._rrd_capture = True - self._rrd_chars = "" - elif self._rrd_level == 2: - if self._rrd_parse_state == 1: - if name == "lastupdate": - self._rrd_parse_state = 2 # In - self._rrd_capture = True - self._rrd_chars = "" - elif name == "step": - self._rrd_parse_state = 3 # In - self._rrd_capture = True - self._rrd_chars = "" - elif name == "ds": - self._rrd_parse_state = 4 # In - self._rrd_ds_val = {} - self._rrd_chars = "" - elif name == "rra": - self._rrd_parse_state = 5 # In - elif self._rrd_level == 3: - if self._rrd_parse_state == 4: - if name == "name": - self._rrd_parse_state = 6 # In - self._rrd_capture = True - self._rrd_chars = "" - elif name == "type": - self._rrd_parse_state = 7 # In - self._rrd_capture = True - self._rrd_chars = "" - elif self._rrd_parse_state == 5: - if name == "database": - self._rrd_parse_state = 8 # In - elif self._rrd_level == 4: - if self._rrd_parse_state == 8: - if name == "row": - self._rrd_parse_state = 9 # In - self._rrd_data_row = [] - self._rrd_data_row_has_nan = False - elif self._rrd_level == 5: - if self._rrd_parse_state == 9: - if name == "v": - self._rrd_parse_state = 10 # In - self._rrd_capture = True - self._rrd_chars = "" - - def endElement(self, name): - self._rrd_capture = False - if self._rrd_parse_state == 10: - self._rrd_capture = self._rrd_level == 6 - if self._rrd_level == 5: - if self._rrd_chars == "NaN": - self._rrd_data_row_has_nan = True - else: - self._rrd_data_row.append(self._rrd_chars) - self._rrd_parse_state = 9 # In - elif self._rrd_parse_state == 9: - if self._rrd_level == 4: - if not self._rrd_data_row_has_nan: - self.rrd_records.append(self._rrd_data_row) - self._rrd_parse_state = 8 # In - elif self._rrd_parse_state == 8: - if self._rrd_level == 3: - self._rrd_parse_state = 5 # In - elif self._rrd_parse_state == 7: - if self._rrd_level == 3: - self._rrd_ds_val["type"] = self._rrd_chars - self._rrd_parse_state = 4 # In - elif self._rrd_parse_state == 6: - if self._rrd_level == 3: - self._rrd_ds_val["name"] = self._rrd_chars - self._rrd_parse_state = 4 # In - elif self._rrd_parse_state == 5: - if self._rrd_level == 2: - self._rrd_parse_state = 1 # In - elif self._rrd_parse_state == 4: - if self._rrd_level == 2: - self.rrd_ds.append(self._rrd_ds_val) - self._rrd_parse_state = 1 # In - elif self._rrd_parse_state == 3: - if self._rrd_level == 2: - self.rrd_step = long(self._rrd_chars) - self._rrd_parse_state = 1 # In - elif self._rrd_parse_state == 2: - if self._rrd_level == 2: - self.rrd_last_update = long(self._rrd_chars) - self._rrd_parse_state = 1 # In - elif self._rrd_parse_state == 1: - if self._rrd_level == 1: - self._rrd_parse_state = 0 # At top - - if self._rrd_level: - self._rrd_level = self._rrd_level - 1 - - def characters(self, content): - if self._rrd_capture: - self._rrd_chars = self._rrd_chars + content.strip() - -def _make_numeric(value): - try: - value = float(value) - except: - value = "" - return value - -def simperf_host_xml_fixup(parser, filter_start_time = None, filter_end_time = None): - # Fixup for GAUGE fields that are really COUNTS. They - # were forced to GAUGE to try to disable rrdtool's - # data interpolation/extrapolation for non-uniform time - # samples. - fixup_tags = [ "cpu_user", - "cpu_nice", - "cpu_sys", - "cpu_idle", - "cpu_waitio", - "cpu_intr", - # "file_active", - # "file_free", - # "inode_active", - # "inode_free", - "netif_in_kb", - "netif_in_pkts", - "netif_in_errs", - "netif_in_drop", - "netif_out_kb", - "netif_out_pkts", - "netif_out_errs", - "netif_out_drop", - "vm_page_in", - "vm_page_out", - "vm_swap_in", - "vm_swap_out", - #"vm_mem_total", - #"vm_mem_used", - #"vm_mem_active", - #"vm_mem_inactive", - #"vm_mem_free", - #"vm_mem_buffer", - #"vm_swap_cache", - #"vm_swap_total", - #"vm_swap_used", - #"vm_swap_free", - "cpu_interrupts", - "cpu_switches", - "cpu_forks" ] - - col_count = len(parser.rrd_ds) - row_count = len(parser.rrd_records) - - # Process the last row separately, just to make all values numeric. - for j in range(col_count): - parser.rrd_records[row_count - 1][j] = _make_numeric(parser.rrd_records[row_count - 1][j]) - - # Process all other row/columns. - last_different_row = row_count - 1 - current_row = row_count - 2 - while current_row >= 0: - # Check for a different value than the previous row. If everything is the same - # then this is probably just a filler/bogus entry. - is_different = False - for j in range(col_count): - parser.rrd_records[current_row][j] = _make_numeric(parser.rrd_records[current_row][j]) - if parser.rrd_records[current_row][j] != parser.rrd_records[last_different_row][j]: - # We're good. This is a different row. - is_different = True - - if not is_different: - # This is a filler/bogus entry. Just ignore it. - for j in range(col_count): - parser.rrd_records[current_row][j] = float('nan') - else: - # Some tags need to be converted into deltas. - for j in range(col_count): - if parser.rrd_ds[j]["name"] in fixup_tags: - parser.rrd_records[last_different_row][j] = \ - parser.rrd_records[last_different_row][j] - parser.rrd_records[current_row][j] - last_different_row = current_row - - current_row -= 1 - - # Set fixup_tags in the first row to 'nan' since they aren't useful anymore. - for j in range(col_count): - if parser.rrd_ds[j]["name"] in fixup_tags: - parser.rrd_records[0][j] = float('nan') - - # Add a timestamp to each row and to the catalog. Format and name - # chosen to match other simulator logging (hopefully). - start_time = parser.rrd_last_update - (parser.rrd_step * (row_count - 1)) - # Build a filtered list of rrd_records if we are limited to a time range. - filter_records = False - if filter_start_time is not None or filter_end_time is not None: - filter_records = True - filtered_rrd_records = [] - if filter_start_time is None: - filter_start_time = start_time * 1000 - if filter_end_time is None: - filter_end_time = parser.rrd_last_update * 1000 - - for i in range(row_count): - record_timestamp = (start_time + (i * parser.rrd_step)) * 1000 - parser.rrd_records[i].insert(0, record_timestamp) - if filter_records: - if filter_start_time <= record_timestamp and record_timestamp <= filter_end_time: - filtered_rrd_records.append(parser.rrd_records[i]) - - if filter_records: - parser.rrd_records = filtered_rrd_records - - parser.rrd_ds.insert(0, {"type": "GAUGE", "name": "javascript_timestamp"}) - - -def main(argv=None): - opts, args = getopt.getopt(sys.argv[1:], "i:o:h", ["in=", "out=", "help"]) - input_file = sys.stdin - output_file = sys.stdout - for o, a in opts: - if o in ("-i", "--in"): - input_file = open(a, 'r') - if o in ("-o", "--out"): - output_file = open(a, 'w') - if o in ("-h", "--help"): - usage() - sys.exit(0) - - # Using the SAX parser as it is at least 4X faster and far, far - # smaller on this dataset than the DOM-based interface in xml.dom.minidom. - # With SAX and a 5.4MB xml file, this requires about seven seconds of - # wall-clock time and 32MB VSZ. With the DOM interface, about 22 seconds - # and over 270MB VSZ. - - handler = SimPerfHostXMLParser() - sax.parse(input_file, handler) - if input_file != sys.stdin: - input_file.close() - - # Various format fixups: string-to-num, gauge-to-counts, add - # a time stamp, etc. - simperf_host_xml_fixup(handler) - - # Create JSONable dict with interesting data and format/print it - print >>output_file, simplejson.dumps({ "step" : handler.rrd_step, - "lastupdate": handler.rrd_last_update * 1000, - "ds" : handler.rrd_ds, - "database" : handler.rrd_records }) - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/indra/lib/python/indra/util/simperf_oprof_interface.py b/indra/lib/python/indra/util/simperf_oprof_interface.py deleted file mode 100755 index 547d2f998..000000000 --- a/indra/lib/python/indra/util/simperf_oprof_interface.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python -"""\ -@file simperf_oprof_interface.py -@brief Manage OProfile data collection on a host - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys, os, getopt -import simplejson - - -def usage(): - print "Usage:" - print sys.argv[0] + " [options]" - print " Digest the OProfile report forms that come out of the" - print " simperf_oprof_ctl program's -r/--report command. The result" - print " is an array of dictionaires with the following keys:" - print - print " symbol Name of sampled, calling, or called procedure" - print " file Executable or library where symbol resides" - print " percentage Percentage contribution to profile, calls or called" - print " samples Sample count" - print " calls Methods called by the method in question (full only)" - print " called_by Methods calling the method (full only)" - print - print " For 'full' reports the two keys 'calls' and 'called_by' are" - print " themselves arrays of dictionaries based on the first four keys." - print - print "Return Codes:" - print " None. Aggressively digests everything. Will likely mung results" - print " if a program or library has whitespace in its name." - print - print "Options:" - print " -i, --in Input settings filename. (Default: stdin)" - print " -o, --out Output settings filename. (Default: stdout)" - print " -h, --help Print this message and exit." - print - print "Interfaces:" - print " class SimPerfOProfileInterface()" - -class SimPerfOProfileInterface: - def __init__(self): - self.isBrief = True # public - self.isValid = False # public - self.result = [] # public - - def parse(self, input): - in_samples = False - for line in input: - if in_samples: - if line[0:6] == "------": - self.isBrief = False - self._parseFull(input) - else: - self._parseBrief(input, line) - self.isValid = True - return - try: - hd1, remain = line.split(None, 1) - if hd1 == "samples": - in_samples = True - except ValueError: - pass - - def _parseBrief(self, input, line1): - try: - fld1, fld2, fld3, fld4 = line1.split(None, 3) - self.result.append({"samples" : fld1, - "percentage" : fld2, - "file" : fld3, - "symbol" : fld4.strip("\n")}) - except ValueError: - pass - for line in input: - try: - fld1, fld2, fld3, fld4 = line.split(None, 3) - self.result.append({"samples" : fld1, - "percentage" : fld2, - "file" : fld3, - "symbol" : fld4.strip("\n")}) - except ValueError: - pass - - def _parseFull(self, input): - state = 0 # In 'called_by' section - calls = [] - called_by = [] - current = {} - for line in input: - if line[0:6] == "------": - if len(current): - current["calls"] = calls - current["called_by"] = called_by - self.result.append(current) - state = 0 - calls = [] - called_by = [] - current = {} - else: - try: - fld1, fld2, fld3, fld4 = line.split(None, 3) - tmp = {"samples" : fld1, - "percentage" : fld2, - "file" : fld3, - "symbol" : fld4.strip("\n")} - except ValueError: - continue - if line[0] != " ": - current = tmp - state = 1 # In 'calls' section - elif state == 0: - called_by.append(tmp) - else: - calls.append(tmp) - if len(current): - current["calls"] = calls - current["called_by"] = called_by - self.result.append(current) - - -def main(argv=None): - opts, args = getopt.getopt(sys.argv[1:], "i:o:h", ["in=", "out=", "help"]) - input_file = sys.stdin - output_file = sys.stdout - for o, a in opts: - if o in ("-i", "--in"): - input_file = open(a, 'r') - if o in ("-o", "--out"): - output_file = open(a, 'w') - if o in ("-h", "--help"): - usage() - sys.exit(0) - - oprof = SimPerfOProfileInterface() - oprof.parse(input_file) - if input_file != sys.stdin: - input_file.close() - - # Create JSONable dict with interesting data and format/print it - print >>output_file, simplejson.dumps(oprof.result) - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/indra/lib/python/indra/util/simperf_proc_interface.py b/indra/lib/python/indra/util/simperf_proc_interface.py deleted file mode 100755 index de061f68c..000000000 --- a/indra/lib/python/indra/util/simperf_proc_interface.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python -"""\ -@file simperf_proc_interface.py -@brief Utility to extract log messages from *..llsd files containing performance statistics. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -# ---------------------------------------------------- -# Utility to extract log messages from *..llsd -# files that contain performance statistics. - -# ---------------------------------------------------- -import sys, os - -if os.path.exists("setup-path.py"): - execfile("setup-path.py") - -from indra.base import llsd - -DEFAULT_PATH="/dev/shm/simperf/" - - -# ---------------------------------------------------- -# Pull out the stats and return a single document -def parse_logfile(filename, target_column=None, verbose=False): - full_doc = [] - # Open source temp log file. Let exceptions percolate up. - sourcefile = open( filename,'r') - - if verbose: - print "Reading " + filename - - # Parse and output all lines from the temp file - for line in sourcefile.xreadlines(): - partial_doc = llsd.parse(line) - if partial_doc is not None: - if target_column is None: - full_doc.append(partial_doc) - else: - trim_doc = { target_column: partial_doc[target_column] } - if target_column != "fps": - trim_doc[ 'fps' ] = partial_doc[ 'fps' ] - trim_doc[ '/total_time' ] = partial_doc[ '/total_time' ] - trim_doc[ 'utc_time' ] = partial_doc[ 'utc_time' ] - full_doc.append(trim_doc) - - sourcefile.close() - return full_doc - -# Extract just the meta info line, and the timestamp of the first/last frame entry. -def parse_logfile_info(filename, verbose=False): - # Open source temp log file. Let exceptions percolate up. - sourcefile = open(filename, 'rU') # U is to open with Universal newline support - - if verbose: - print "Reading " + filename - - # The first line is the meta info line. - info_line = sourcefile.readline() - if not info_line: - sourcefile.close() - return None - - # The rest of the lines are frames. Read the first and last to get the time range. - info = llsd.parse( info_line ) - info['start_time'] = None - info['end_time'] = None - first_frame = sourcefile.readline() - if first_frame: - try: - info['start_time'] = int(llsd.parse(first_frame)['timestamp']) - except: - pass - - # Read the file backwards to find the last two lines. - sourcefile.seek(0, 2) - file_size = sourcefile.tell() - offset = 1024 - num_attempts = 0 - end_time = None - if file_size < offset: - offset = file_size - while 1: - sourcefile.seek(-1*offset, 2) - read_str = sourcefile.read(offset) - # Remove newline at the end - if read_str[offset - 1] == '\n': - read_str = read_str[0:-1] - lines = read_str.split('\n') - full_line = None - if len(lines) > 2: # Got two line - try: - end_time = llsd.parse(lines[-1])['timestamp'] - except: - # We couldn't parse this line. Try once more. - try: - end_time = llsd.parse(lines[-2])['timestamp'] - except: - # Nope. Just move on. - pass - break - if len(read_str) == file_size: # Reached the beginning - break - offset += 1024 - - info['end_time'] = int(end_time) - - sourcefile.close() - return info - - -def parse_proc_filename(filename): - try: - name_as_list = filename.split(".") - cur_stat_type = name_as_list[0].split("_")[0] - cur_pid = name_as_list[1] - except IndexError, ValueError: - return (None, None) - return (cur_pid, cur_stat_type) - -# ---------------------------------------------------- -def get_simstats_list(path=None): - """ Return stats (pid, type) listed in _proc..llsd """ - if path is None: - path = DEFAULT_PATH - simstats_list = [] - for file_name in os.listdir(path): - if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd": - simstats_info = parse_logfile_info(path + file_name) - if simstats_info is not None: - simstats_list.append(simstats_info) - return simstats_list - -def get_log_info_list(pid=None, stat_type=None, path=None, target_column=None, verbose=False): - """ Return data from all llsd files matching the pid and stat type """ - if path is None: - path = DEFAULT_PATH - log_info_list = {} - for file_name in os.listdir ( path ): - if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd": - (cur_pid, cur_stat_type) = parse_proc_filename(file_name) - if cur_pid is None: - continue - if pid is not None and pid != cur_pid: - continue - if stat_type is not None and stat_type != cur_stat_type: - continue - log_info_list[cur_pid] = parse_logfile(path + file_name, target_column, verbose) - return log_info_list - -def delete_simstats_files(pid=None, stat_type=None, path=None): - """ Delete *..llsd files """ - if path is None: - path = DEFAULT_PATH - del_list = [] - for file_name in os.listdir(path): - if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd": - (cur_pid, cur_stat_type) = parse_proc_filename(file_name) - if cur_pid is None: - continue - if pid is not None and pid != cur_pid: - continue - if stat_type is not None and stat_type != cur_stat_type: - continue - del_list.append(cur_pid) - # Allow delete related exceptions to percolate up if this fails. - os.unlink(os.path.join(DEFAULT_PATH, file_name)) - return del_list - diff --git a/indra/lib/python/indra/util/term.py b/indra/lib/python/indra/util/term.py deleted file mode 100644 index 8c316a1f1..000000000 --- a/indra/lib/python/indra/util/term.py +++ /dev/null @@ -1,222 +0,0 @@ -''' -@file term.py -@brief a better shutil.copytree replacement - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -''' - -#http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116 - -import sys, re - -class TerminalController: - """ - A class that can be used to portably generate formatted output to - a terminal. - - `TerminalController` defines a set of instance variables whose - values are initialized to the control sequence necessary to - perform a given action. These can be simply included in normal - output to the terminal: - - >>> term = TerminalController() - >>> print 'This is '+term.GREEN+'green'+term.NORMAL - - Alternatively, the `render()` method can used, which replaces - '${action}' with the string required to perform 'action': - - >>> term = TerminalController() - >>> print term.render('This is ${GREEN}green${NORMAL}') - - If the terminal doesn't support a given action, then the value of - the corresponding instance variable will be set to ''. As a - result, the above code will still work on terminals that do not - support color, except that their output will not be colored. - Also, this means that you can test whether the terminal supports a - given action by simply testing the truth value of the - corresponding instance variable: - - >>> term = TerminalController() - >>> if term.CLEAR_SCREEN: - ... print 'This terminal supports clearning the screen.' - - Finally, if the width and height of the terminal are known, then - they will be stored in the `COLS` and `LINES` attributes. - """ - # Cursor movement: - BOL = '' #: Move the cursor to the beginning of the line - UP = '' #: Move the cursor up one line - DOWN = '' #: Move the cursor down one line - LEFT = '' #: Move the cursor left one char - RIGHT = '' #: Move the cursor right one char - - # Deletion: - CLEAR_SCREEN = '' #: Clear the screen and move to home position - CLEAR_EOL = '' #: Clear to the end of the line. - CLEAR_BOL = '' #: Clear to the beginning of the line. - CLEAR_EOS = '' #: Clear to the end of the screen - - # Output modes: - BOLD = '' #: Turn on bold mode - BLINK = '' #: Turn on blink mode - DIM = '' #: Turn on half-bright mode - REVERSE = '' #: Turn on reverse-video mode - NORMAL = '' #: Turn off all modes - - # Cursor display: - HIDE_CURSOR = '' #: Make the cursor invisible - SHOW_CURSOR = '' #: Make the cursor visible - - # Terminal size: - COLS = None #: Width of the terminal (None for unknown) - LINES = None #: Height of the terminal (None for unknown) - - # Foreground colors: - BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' - - # Background colors: - BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' - BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' - - _STRING_CAPABILITIES = """ - BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 - CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold - BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 - HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() - _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() - _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() - - def __init__(self, term_stream=sys.stdout): - """ - Create a `TerminalController` and initialize its attributes - with appropriate values for the current terminal. - `term_stream` is the stream that will be used for terminal - output; if this stream is not a tty, then the terminal is - assumed to be a dumb terminal (i.e., have no capabilities). - """ - # Curses isn't available on all platforms - try: import curses - except: return - - # If the stream isn't a tty, then assume it has no capabilities. - if not term_stream.isatty(): return - - # Check the terminal type. If we fail, then assume that the - # terminal has no capabilities. - try: curses.setupterm() - except: return - - # Look up numeric capabilities. - self.COLS = curses.tigetnum('cols') - self.LINES = curses.tigetnum('lines') - - # Look up string capabilities. - for capability in self._STRING_CAPABILITIES: - (attrib, cap_name) = capability.split('=') - setattr(self, attrib, self._tigetstr(cap_name) or '') - - # Colors - set_fg = self._tigetstr('setf') - if set_fg: - for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, color, curses.tparm(set_fg, i) or '') - set_fg_ansi = self._tigetstr('setaf') - if set_fg_ansi: - for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, color, curses.tparm(set_fg_ansi, i) or '') - set_bg = self._tigetstr('setb') - if set_bg: - for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '') - set_bg_ansi = self._tigetstr('setab') - if set_bg_ansi: - for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '') - - def _tigetstr(self, cap_name): - # String capabilities can include "delays" of the form "$<2>". - # For any modern terminal, we should be able to just ignore - # these, so strip them out. - import curses - cap = curses.tigetstr(cap_name) or '' - return re.sub(r'\$<\d+>[/*]?', '', cap) - - def render(self, template): - """ - Replace each $-substitutions in the given template string with - the corresponding terminal control string (if it's defined) or - '' (if it's not). - """ - return re.sub(r'\$\$|\${\w+}', self._render_sub, template) - - def _render_sub(self, match): - s = match.group() - if s == '$$': return s - else: return getattr(self, s[2:-1]) - -####################################################################### -# Example use case: progress bar -####################################################################### - -class ProgressBar: - """ - A 3-line progress bar, which looks like:: - - Header - 20% [===========----------------------------------] - progress message - - The progress bar is colored, if the terminal supports color - output; and adjusts to the width of the terminal. - """ - BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n' - HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n' - - def __init__(self, term, header): - self.term = term - if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): - raise ValueError("Terminal isn't capable enough -- you " - "should use a simpler progress dispaly.") - self.width = self.term.COLS or 75 - self.bar = term.render(self.BAR) - self.header = self.term.render(self.HEADER % header.center(self.width)) - self.cleared = 1 #: true if we haven't drawn the bar yet. - self.update(0, '') - - def update(self, percent, message): - if self.cleared: - sys.stdout.write(self.header) - self.cleared = 0 - n = int((self.width-10)*percent) - sys.stdout.write( - self.term.BOL + self.term.UP + self.term.CLEAR_EOL + - (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) + - self.term.CLEAR_EOL + message.center(self.width)) - - def clear(self): - if not self.cleared: - sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL + - self.term.UP + self.term.CLEAR_EOL + - self.term.UP + self.term.CLEAR_EOL) - self.cleared = 1 diff --git a/indra/lib/python/uuid.py b/indra/lib/python/uuid.py deleted file mode 100644 index e956383cc..000000000 --- a/indra/lib/python/uuid.py +++ /dev/null @@ -1,508 +0,0 @@ -#!/usr/bin/python -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ -r"""UUID objects (universally unique identifiers) according to RFC 4122. - -This module provides immutable UUID objects (class UUID) and the functions -uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5 -UUIDs as specified in RFC 4122. - -If all you want is a unique ID, you should probably call uuid1() or uuid4(). -Note that uuid1() may compromise privacy since it creates a UUID containing -the computer's network address. uuid4() creates a random UUID. - -Typical usage: - - >>> import uuid - - # make a UUID based on the host ID and current time - >>> uuid.uuid1() - UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') - - # make a UUID using an MD5 hash of a namespace UUID and a name - >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') - UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') - - # make a random UUID - >>> uuid.uuid4() - UUID('16fd2706-8baf-433b-82eb-8c7fada847da') - - # make a UUID using a SHA-1 hash of a namespace UUID and a name - >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') - UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d') - - # make a UUID from a string of hex digits (braces and hyphens ignored) - >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') - - # convert a UUID to a string of hex digits in standard form - >>> str(x) - '00010203-0405-0607-0809-0a0b0c0d0e0f' - - # get the raw 16 bytes of the UUID - >>> x.bytes - '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' - - # make a UUID from a 16-byte string - >>> uuid.UUID(bytes=x.bytes) - UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') - -This module works with Python 2.3 or higher.""" - -__author__ = 'Ka-Ping Yee ' -__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-') -__version__ = '$Revision: 1.30 $'.split()[1] - -RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [ - 'reserved for NCS compatibility', 'specified in RFC 4122', - 'reserved for Microsoft compatibility', 'reserved for future definition'] - -class UUID(object): - """Instances of the UUID class represent UUIDs as specified in RFC 4122. - UUID objects are immutable, hashable, and usable as dictionary keys. - Converting a UUID to a string with str() yields something in the form - '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts - four possible forms: a similar string of hexadecimal digits, or a - string of 16 raw bytes as an argument named 'bytes', or a tuple of - six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and - 48-bit values respectively) as an argument named 'fields', or a single - 128-bit integer as an argument named 'int'. - - UUIDs have these read-only attributes: - - bytes the UUID as a 16-byte string - - fields a tuple of the six integer fields of the UUID, - which are also available as six individual attributes - and two derived attributes: - - time_low the first 32 bits of the UUID - time_mid the next 16 bits of the UUID - time_hi_version the next 16 bits of the UUID - clock_seq_hi_variant the next 8 bits of the UUID - clock_seq_low the next 8 bits of the UUID - node the last 48 bits of the UUID - - time the 60-bit timestamp - clock_seq the 14-bit sequence number - - hex the UUID as a 32-character hexadecimal string - - int the UUID as a 128-bit integer - - urn the UUID as a URN as specified in RFC 4122 - - variant the UUID variant (one of the constants RESERVED_NCS, - RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE) - - version the UUID version number (1 through 5, meaningful only - when the variant is RFC_4122) - """ - - def __init__(self, hex=None, bytes=None, fields=None, int=None, - version=None): - r"""Create a UUID from either a string of 32 hexadecimal digits, - a string of 16 bytes as the 'bytes' argument, a tuple of six - integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version, - 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as - the 'fields' argument, or a single 128-bit integer as the 'int' - argument. When a string of hex digits is given, curly braces, - hyphens, and a URN prefix are all optional. For example, these - expressions all yield the same UUID: - - UUID('{12345678-1234-5678-1234-567812345678}') - UUID('12345678123456781234567812345678') - UUID('urn:uuid:12345678-1234-5678-1234-567812345678') - UUID(bytes='\x12\x34\x56\x78'*4) - UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) - UUID(int=0x12345678123456781234567812345678) - - Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given. - The 'version' argument is optional; if given, the resulting UUID - will have its variant and version number set according to RFC 4122, - overriding bits in the given 'hex', 'bytes', 'fields', or 'int'. - """ - - if [hex, bytes, fields, int].count(None) != 3: - raise TypeError('need just one of hex, bytes, fields, or int') - if hex is not None: - hex = hex.replace('urn:', '').replace('uuid:', '') - hex = hex.strip('{}').replace('-', '') - if len(hex) != 32: - raise ValueError('badly formed hexadecimal UUID string') - int = long(hex, 16) - if bytes is not None: - if len(bytes) != 16: - raise ValueError('bytes is not a 16-char string') - int = long(('%02x'*16) % tuple(map(ord, bytes)), 16) - if fields is not None: - if len(fields) != 6: - raise ValueError('fields is not a 6-tuple') - (time_low, time_mid, time_hi_version, - clock_seq_hi_variant, clock_seq_low, node) = fields - if not 0 <= time_low < 1<<32L: - raise ValueError('field 1 out of range (need a 32-bit value)') - if not 0 <= time_mid < 1<<16L: - raise ValueError('field 2 out of range (need a 16-bit value)') - if not 0 <= time_hi_version < 1<<16L: - raise ValueError('field 3 out of range (need a 16-bit value)') - if not 0 <= clock_seq_hi_variant < 1<<8L: - raise ValueError('field 4 out of range (need an 8-bit value)') - if not 0 <= clock_seq_low < 1<<8L: - raise ValueError('field 5 out of range (need an 8-bit value)') - if not 0 <= node < 1<<48L: - raise ValueError('field 6 out of range (need a 48-bit value)') - clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low - int = ((time_low << 96L) | (time_mid << 80L) | - (time_hi_version << 64L) | (clock_seq << 48L) | node) - if int is not None: - if not 0 <= int < 1<<128L: - raise ValueError('int is out of range (need a 128-bit value)') - if version is not None: - if not 1 <= version <= 5: - raise ValueError('illegal version number') - # Set the variant to RFC 4122. - int &= ~(0xc000 << 48L) - int |= 0x8000 << 48L - # Set the version number. - int &= ~(0xf000 << 64L) - int |= version << 76L - self.__dict__['int'] = int - - def __cmp__(self, other): - if isinstance(other, UUID): - return cmp(self.int, other.int) - return NotImplemented - - def __hash__(self): - return hash(self.int) - - def __int__(self): - return self.int - - def __repr__(self): - return 'UUID(%r)' % str(self) - - def __setattr__(self, name, value): - raise TypeError('UUID objects are immutable') - - def __str__(self): - hex = '%032x' % self.int - return '%s-%s-%s-%s-%s' % ( - hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:]) - - def get_bytes(self): - bytes = '' - for shift in range(0, 128, 8): - bytes = chr((self.int >> shift) & 0xff) + bytes - return bytes - - bytes = property(get_bytes) - - def get_fields(self): - return (self.time_low, self.time_mid, self.time_hi_version, - self.clock_seq_hi_variant, self.clock_seq_low, self.node) - - fields = property(get_fields) - - def get_time_low(self): - return self.int >> 96L - - time_low = property(get_time_low) - - def get_time_mid(self): - return (self.int >> 80L) & 0xffff - - time_mid = property(get_time_mid) - - def get_time_hi_version(self): - return (self.int >> 64L) & 0xffff - - time_hi_version = property(get_time_hi_version) - - def get_clock_seq_hi_variant(self): - return (self.int >> 56L) & 0xff - - clock_seq_hi_variant = property(get_clock_seq_hi_variant) - - def get_clock_seq_low(self): - return (self.int >> 48L) & 0xff - - clock_seq_low = property(get_clock_seq_low) - - def get_time(self): - return (((self.time_hi_version & 0x0fffL) << 48L) | - (self.time_mid << 32L) | self.time_low) - - time = property(get_time) - - def get_clock_seq(self): - return (((self.clock_seq_hi_variant & 0x3fL) << 8L) | - self.clock_seq_low) - - clock_seq = property(get_clock_seq) - - def get_node(self): - return self.int & 0xffffffffffff - - node = property(get_node) - - def get_hex(self): - return '%032x' % self.int - - hex = property(get_hex) - - def get_urn(self): - return 'urn:uuid:' + str(self) - - urn = property(get_urn) - - def get_variant(self): - if not self.int & (0x8000 << 48L): - return RESERVED_NCS - elif not self.int & (0x4000 << 48L): - return RFC_4122 - elif not self.int & (0x2000 << 48L): - return RESERVED_MICROSOFT - else: - return RESERVED_FUTURE - - variant = property(get_variant) - - def get_version(self): - # The version bits are only meaningful for RFC 4122 UUIDs. - if self.variant == RFC_4122: - return int((self.int >> 76L) & 0xf) - - version = property(get_version) - -def _ifconfig_getnode(): - """Get the hardware address on Unix by running ifconfig.""" - import os - for dir in ['', '/sbin/', '/usr/sbin']: - try: - path = os.path.join(dir, 'ifconfig') - if os.path.exists(path): - pipe = os.popen(path) - else: - continue - except IOError: - continue - for line in pipe: - words = line.lower().split() - for i in range(len(words)): - if words[i] in ['hwaddr', 'ether']: - return int(words[i + 1].replace(':', ''), 16) - -def _ipconfig_getnode(): - """Get the hardware address on Windows by running ipconfig.exe.""" - import os, re - dirs = ['', r'c:\windows\system32', r'c:\winnt\system32'] - try: - import ctypes - buffer = ctypes.create_string_buffer(300) - ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300) - dirs.insert(0, buffer.value.decode('mbcs')) - except: - pass - for dir in dirs: - try: - pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all') - except IOError: - continue - for line in pipe: - value = line.split(':')[-1].strip().lower() - if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value): - return int(value.replace('-', ''), 16) - -def _netbios_getnode(): - """Get the hardware address on Windows using NetBIOS calls. - See http://support.microsoft.com/kb/118623 for details.""" - import win32wnet, netbios - ncb = netbios.NCB() - ncb.Command = netbios.NCBENUM - ncb.Buffer = adapters = netbios.LANA_ENUM() - adapters._pack() - if win32wnet.Netbios(ncb) != 0: - return - adapters._unpack() - for i in range(adapters.length): - ncb.Reset() - ncb.Command = netbios.NCBRESET - ncb.Lana_num = ord(adapters.lana[i]) - if win32wnet.Netbios(ncb) != 0: - continue - ncb.Reset() - ncb.Command = netbios.NCBASTAT - ncb.Lana_num = ord(adapters.lana[i]) - ncb.Callname = '*'.ljust(16) - ncb.Buffer = status = netbios.ADAPTER_STATUS() - if win32wnet.Netbios(ncb) != 0: - continue - status._unpack() - bytes = map(ord, status.adapter_address) - return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) + - (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5]) - -# Thanks to Thomas Heller for ctypes and for his help with its use here. - -# If ctypes is available, use it to find system routines for UUID generation. -_uuid_generate_random = _uuid_generate_time = _UuidCreate = None -try: - import ctypes, ctypes.util - _buffer = ctypes.create_string_buffer(16) - - # The uuid_generate_* routines are provided by libuuid on at least - # Linux and FreeBSD, and provided by libc on Mac OS X. - for libname in ['uuid', 'c']: - try: - lib = ctypes.CDLL(ctypes.util.find_library(libname)) - except: - continue - if hasattr(lib, 'uuid_generate_random'): - _uuid_generate_random = lib.uuid_generate_random - if hasattr(lib, 'uuid_generate_time'): - _uuid_generate_time = lib.uuid_generate_time - - # On Windows prior to 2000, UuidCreate gives a UUID containing the - # hardware address. On Windows 2000 and later, UuidCreate makes a - # random UUID and UuidCreateSequential gives a UUID containing the - # hardware address. These routines are provided by the RPC runtime. - try: - lib = ctypes.windll.rpcrt4 - except: - lib = None - _UuidCreate = getattr(lib, 'UuidCreateSequential', - getattr(lib, 'UuidCreate', None)) -except: - pass - -def _unixdll_getnode(): - """Get the hardware address on Unix using ctypes.""" - _uuid_generate_time(_buffer) - return UUID(bytes=_buffer.raw).node - -def _windll_getnode(): - """Get the hardware address on Windows using ctypes.""" - if _UuidCreate(_buffer) == 0: - return UUID(bytes=_buffer.raw).node - -def _random_getnode(): - """Get a random node ID, with eighth bit set as suggested by RFC 4122.""" - import random - return random.randrange(0, 1<<48L) | 0x010000000000L - -_node = None - -def getnode(): - """Get the hardware address as a 48-bit integer. The first time this - runs, it may launch a separate program, which could be quite slow. If - all attempts to obtain the hardware address fail, we choose a random - 48-bit number with its eighth bit set to 1 as recommended in RFC 4122.""" - - global _node - if _node is not None: - return _node - - import sys - if sys.platform == 'win32': - getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] - else: - getters = [_unixdll_getnode, _ifconfig_getnode] - - for getter in getters + [_random_getnode]: - try: - _node = getter() - except: - continue - if _node is not None: - return _node - -def uuid1(node=None, clock_seq=None): - """Generate a UUID from a host ID, sequence number, and the current time. - If 'node' is not given, getnode() is used to obtain the hardware - address. If 'clock_seq' is given, it is used as the sequence number; - otherwise a random 14-bit sequence number is chosen.""" - - # When the system provides a version-1 UUID generator, use it (but don't - # use UuidCreate here because its UUIDs don't conform to RFC 4122). - if _uuid_generate_time and node is clock_seq is None: - _uuid_generate_time(_buffer) - return UUID(bytes=_buffer.raw) - - import time - nanoseconds = int(time.time() * 1e9) - # 0x01b21dd213814000 is the number of 100-ns intervals between the - # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. - timestamp = int(nanoseconds/100) + 0x01b21dd213814000L - if clock_seq is None: - import random - clock_seq = random.randrange(1<<14L) # instead of stable storage - time_low = timestamp & 0xffffffffL - time_mid = (timestamp >> 32L) & 0xffffL - time_hi_version = (timestamp >> 48L) & 0x0fffL - clock_seq_low = clock_seq & 0xffL - clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL - if node is None: - node = getnode() - return UUID(fields=(time_low, time_mid, time_hi_version, - clock_seq_hi_variant, clock_seq_low, node), version=1) - -def uuid3(namespace, name): - """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" - try: - # Python 2.6 - from hashlib import md5 - except ImportError: - # Python 2.5 and earlier - from md5 import new as md5 - - hash = md5(namespace.bytes + name).digest() - return UUID(bytes=hash[:16], version=3) - -def uuid4(): - """Generate a random UUID.""" - - # When the system provides a version-4 UUID generator, use it. - if _uuid_generate_random: - _uuid_generate_random(_buffer) - return UUID(bytes=_buffer.raw) - - # Otherwise, get randomness from urandom or the 'random' module. - try: - import os - return UUID(bytes=os.urandom(16), version=4) - except: - import random - bytes = [chr(random.randrange(256)) for i in range(16)] - return UUID(bytes=bytes, version=4) - -def uuid5(namespace, name): - """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" - import sha - hash = sha.sha(namespace.bytes + name).digest() - return UUID(bytes=hash[:16], version=5) - -# The following standard UUIDs are for use with uuid3() or uuid5(). - -NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') -NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8') -NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8') -NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 743e02236..356016d67 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -5,49 +5,49 @@ @brief Description of all installer viewer files, and methods for packaging them into installers for all supported platforms. -$LicenseInfo:firstyear=2006&license=viewergpl$ +$LicenseInfo:firstyear=2006&license=viewerlgpl$ Second Life Viewer Source Code -Copyright (c) 2006-2009, Linden Research, Inc. +Copyright (C) 2006-2014, Linden Research, Inc. -The source code in this file ("Source Code") is provided by Linden Lab -to you under the terms of the GNU General Public License, version 2.0 -("GPL"), unless you have obtained a separate licensing agreement -("Other License"), formally executed by you and Linden Lab. Terms of -the GPL can be found in doc/GPL-license.txt in this distribution, or -online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. -There are special exceptions to the terms and conditions of the GPL as -it is applied to this Source Code. View the full text of the exception -in the file doc/FLOSS-exception.txt in this software distribution, or -online at -http://secondlifegrid.net/programs/open_source/licensing/flossexception +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. -By copying, modifying or distributing this software, you acknowledge -that you have read and understood your obligations described above, -and agree to abide by those obligations. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -COMPLETENESS OR PERFORMANCE. +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA $/LicenseInfo$ """ -import sys -import os.path 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 random +import zipfile + viewer_dir = os.path.dirname(__file__) # Add indra/lib/python to our path so we don't have to muck with PYTHONPATH. # Put it FIRST because some of our build hosts have an ancient install of # indra.util.llmanifest under their system Python! sys.path.insert(0, os.path.join(viewer_dir, os.pardir, "lib", "python")) from indra.util.llmanifest import LLManifest, main, proper_windows_path, path_ancestors, CHANNEL_VENDOR_BASE, RELEASE_CHANNEL, ManifestError -try: - from llbase import llsd -except ImportError: - from indra.base import llsd +from llbase import llsd class ViewerManifest(LLManifest): def is_packaging_viewer(self): @@ -62,7 +62,7 @@ class ViewerManifest(LLManifest): self.path(xml) self.path(skin_dir + "/*") # include the entire textures directory recursively - if self.prefix(src=skin_dir+"/textures"): + with self.prefix(src_dst=skin_dir+"/textures"): self.path("*/*.tga") self.path("*/*.j2c") self.path("*/*.jpg") @@ -71,8 +71,7 @@ class ViewerManifest(LLManifest): self.path("*.j2c") self.path("*.jpg") self.path("*.png") - self.path("textures.xml") - self.end_prefix(skin_dir+"/textures") + self.path("*.xml") def construct(self): super(ViewerManifest, self).construct() @@ -80,7 +79,7 @@ class ViewerManifest(LLManifest): self.path(src="../../etc/message.xml", dst="app_settings/message.xml") if True: #self.is_packaging_viewer(): - if self.prefix(src="app_settings"): + with self.prefix(src_dst="app_settings"): self.exclude("logcontrol.xml") self.exclude("logcontrol-dev.xml") self.path("*.crt") @@ -102,46 +101,38 @@ class ViewerManifest(LLManifest): self.path(src=os.path.join(self.args['build'],"packages-info.txt"), dst="packages-info.txt") - self.end_prefix("app_settings") - - if self.prefix(src="character"): + with self.prefix(src_dst="character"): self.path("*.llm") self.path("*.xml") self.path("*.tga") - self.end_prefix("character") # Include our fonts - if self.prefix(src=os.path.join(pkgdir, "fonts"), dst="fonts"): + with self.prefix(src=os.path.join(pkgdir, "fonts"), dst="fonts"): self.path("DejaVuSansCondensed*.ttf") self.path("DejaVuSansMono.ttf") self.path("DroidSans*.ttf") self.path("NotoSansDisplay*.ttf") self.path("SourceHanSans*.otf") - self.end_prefix() # Include our font licenses - if self.prefix(src="fonts"): + with self.prefix(src_dst="fonts"): self.path("*.txt") - self.end_prefix("fonts") # skins - if self.prefix(src="skins"): + with self.prefix(src_dst="skins"): self.path("paths.xml") self.path("default/xui/*/*.xml") - self.package_skin("Default.xml", "default") - self.package_skin("dark.xml", "dark") - self.package_skin("Gemini.xml", "gemini") + self.package_skin("Default.xml", "default") + self.package_skin("dark.xml", "dark") + self.package_skin("Gemini.xml", "gemini") # Local HTML files (e.g. loading screen) - if self.prefix(src="*/html"): + with self.prefix(src_dst="*/html"): self.path("*.png") self.path("*/*/*.html") self.path("*/*/*.gif") - self.end_prefix("*/html") - self.end_prefix("skins") - - # Files in the newview/ directory + # File in the newview/ directory self.path("gpu_table.txt") # The summary.json file gets left in the build directory by newview/CMakeLists.txt. if not self.path2basename(os.pardir, "summary.json"): @@ -150,6 +141,9 @@ class ViewerManifest(LLManifest): def standalone(self): return self.args['standalone'] == "ON" + def finish_build_data_dict(self, build_data_dict): + return build_data_dict + def grid(self): return self.args['grid'] @@ -161,8 +155,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): @@ -185,17 +180,18 @@ class ViewerManifest(LLManifest): return channel_type def channel_variant_app_suffix(self): - # get any part of the compiled channel name after the CHANNEL_VENDOR_BASE + # get any part of the channel name after the CHANNEL_VENDOR_BASE suffix=self.channel_variant() # by ancient convention, we don't use Release in the app name 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): @@ -247,13 +243,144 @@ class ViewerManifest(LLManifest): random.shuffle(names) return ', '.join(names) + def relsymlinkf(self, src, dst=None, catch=True): + """ + relsymlinkf() is just like symlinkf(), but instead of requiring the + caller to pass 'src' as a relative pathname, this method expects 'src' + to be absolute, and creates a symlink whose target is the relative + path from 'src' to dirname(dst). + """ + dstdir, dst = self._symlinkf_prep_dst(src, dst) + + # Determine the relative path starting from the directory containing + # dst to the intended src. + src = self.relpath(src, dstdir) + + self._symlinkf(src, dst, catch) + return dst + + def symlinkf(self, src, dst=None, catch=True): + """ + Like ln -sf, but uses os.symlink() instead of running ln. This creates + a symlink at 'dst' that points to 'src' -- see: + https://docs.python.org/2/library/os.html#os.symlink + + If you omit 'dst', this creates a symlink with basename(src) at + get_dst_prefix() -- in other words: put a symlink to this pathname + here at the current dst prefix. + + 'src' must specifically be a *relative* symlink. It makes no sense to + create an absolute symlink pointing to some path on the build machine! + + Also: + - We prepend 'dst' with the current get_dst_prefix(), so it has similar + meaning to associated self.path() calls. + - We ensure that the containing directory os.path.dirname(dst) exists + before attempting the symlink. + + If you pass catch=False, exceptions will be propagated instead of + caught. + """ + dstdir, dst = self._symlinkf_prep_dst(src, dst) + self._symlinkf(src, dst, catch) + return dst + + def _symlinkf_prep_dst(self, src, dst): + # helper for relsymlinkf() and symlinkf() + if dst is None: + dst = os.path.basename(src) + dst = os.path.join(self.get_dst_prefix(), dst) + # Seems silly to prepend get_dst_prefix() to dst only to call + # os.path.dirname() on it again, but this works even when the passed + # 'dst' is itself a pathname. + dstdir = os.path.dirname(dst) + self.cmakedirs(dstdir) + return (dstdir, dst) + + def _symlinkf(self, src, dst, catch): + # helper for relsymlinkf() and symlinkf() + # the passed src must be relative + if os.path.isabs(src): + raise ManifestError("Do not symlinkf(absolute %r, asis=True)" % src) + + # The outer catch is the one that reports failure even after attempted + # recovery. + try: + # At the inner layer, recovery may be possible. + try: + os.symlink(src, dst) + except OSError as err: + if err.errno != errno.EEXIST: + raise + # We could just blithely attempt to remove and recreate the target + # file, but that strategy doesn't work so well if we don't have + # permissions to remove it. Check to see if it's already the + # symlink we want, which is the usual reason for EEXIST. + elif os.path.islink(dst): + if os.readlink(dst) == src: + # the requested link already exists + pass + else: + # dst is the wrong symlink; attempt to remove and recreate it + os.remove(dst) + os.symlink(src, dst) + elif os.path.isdir(dst): + print "Requested symlink (%s) exists but is a directory; replacing" % dst + shutil.rmtree(dst) + os.symlink(src, dst) + elif os.path.exists(dst): + print "Requested symlink (%s) exists but is a file; replacing" % dst + os.remove(dst) + os.symlink(src, dst) + else: + # out of ideas + raise + except Exception as err: + # report + print "Can't symlink %r -> %r: %s: %s" % \ + (dst, src, err.__class__.__name__, err) + # if caller asked us not to catch, re-raise this exception + if not catch: + raise + + def relpath(self, path, base=None, symlink=False): + """ + Return the relative path from 'base' to the passed 'path'. If base is + omitted, self.get_dst_prefix() is assumed. In other words: make a + same-name symlink to this path right here in the current dest prefix. + + Normally we resolve symlinks. To retain symlinks, pass symlink=True. + """ + if base is None: + base = self.get_dst_prefix() + + # Since we use os.path.relpath() for this, which is purely textual, we + # must ensure that both pathnames are absolute. + if symlink: + # symlink=True means: we know path is (or indirects through) a + # symlink, don't resolve, we want to use the symlink. + abspath = os.path.abspath + else: + # symlink=False means to resolve any symlinks we may find + abspath = os.path.realpath + + return os.path.relpath(abspath(path), abspath(base)) + + class WindowsManifest(ViewerManifest): - def is_win64(self): - return self.args.get('arch') == "x86_64" + # We want the platform, per se, for every Windows build to be 'win'. The + # VMP will concatenate that with the address_size. + build_data_json_platform = 'win' def final_exe(self): return self.app_name_oneword()+"Viewer.exe" + 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['Executable'] = self.final_exe() + build_data_dict['AppName'] = self.app_name() + return build_data_dict + def test_msvcrt_and_copy_action(self, src, dst): # This is used to test a dll manifest. # It is used as a temporary override during the construct method @@ -315,13 +442,12 @@ class WindowsManifest(ViewerManifest): # Find singularity-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/%s-bin.exe' % (self.args['configuration'],self.viewer_branding_id()), dst=self.final_exe()) - if self.prefix(src=os.path.join(pkgdir, "redist")): + with self.prefix(src=os.path.join(pkgdir, "redist")): # include the compiled launcher scripts so that it gets included in the file_list - if(self.is_win64()): + if(self.address_size == 64): self.path('vc_redist.x64.exe') else: self.path('vc_redist.x86.exe') - self.end_prefix() # Plugin host application self.path2basename(os.path.join(os.pardir, @@ -329,8 +455,8 @@ class WindowsManifest(ViewerManifest): "SLplugin.exe") # Get shared libs from the shared libs staging directory - if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), - dst=""): + with self.prefix(src=os.path.join(os.pardir, + 'sharedlibs', self.args['configuration'])): # Get llcommon and deps. If missing assume static linkage and continue. try: @@ -352,10 +478,16 @@ class WindowsManifest(ViewerManifest): # Get fmodstudio dll, continue if missing try: - if(self.is_win64()): - self.path("fmod64.dll") + if(self.address_size == 64): + if self.args['configuration'].lower() == 'debug': + self.path("fmodL64.dll") + else: + self.path("fmod64.dll") else: - self.path("fmod.dll") + if self.args['configuration'].lower() == 'debug': + self.path("fmodL.dll") + else: + self.path("fmod.dll") except: print "Skipping fmodstudio audio library(assuming other audio engine)" @@ -363,63 +495,53 @@ class WindowsManifest(ViewerManifest): self.path("SLVoice.exe") self.path("vivoxsdk.dll") self.path("ortp.dll") - self.path("libsndfile-1.dll") - self.path("vivoxoal.dll") - self.path("ca-bundle.crt") # Security - if(self.is_win64()): + if(self.address_size == 64): self.path("libcrypto-1_1-x64.dll") self.path("libssl-1_1-x64.dll") - if not self.is_packaging_viewer(): - self.path("libcrypto-1_1-x64.pdb") - self.path("libssl-1_1-x64.pdb") + #if not self.is_packaging_viewer(): + # self.path("libcrypto-1_1-x64.pdb") + # self.path("libssl-1_1-x64.pdb") else: self.path("libcrypto-1_1.dll") self.path("libssl-1_1.dll") - if not self.is_packaging_viewer(): - self.path("libcrypto-1_1.pdb") - self.path("libssl-1_1.pdb") - - # HTTP/2 - self.path("nghttp2.dll") + #if not self.is_packaging_viewer(): + # self.path("libcrypto-1_1.pdb") + # self.path("libssl-1_1.pdb") # Hunspell self.path("libhunspell.dll") # For google-perftools tcmalloc allocator. - try: - if self.args['configuration'].lower() == 'debug': - self.path('libtcmalloc_minimal-debug.dll') - else: - self.path('libtcmalloc_minimal.dll') - except: - print "Skipping libtcmalloc_minimal.dll" - - self.end_prefix() + if(self.address_size == 32): + try: + if self.args['configuration'].lower() == 'debug': + self.path('libtcmalloc_minimal-debug.dll') + else: + self.path('libtcmalloc_minimal.dll') + except: + print "Skipping libtcmalloc_minimal.dll" self.path(src="licenses-win32.txt", dst="licenses.txt") self.path("featuretable.txt") # Plugins - FilePicker - if self.prefix(src='../plugins/filepicker/%s' % self.args['configuration'], dst="llplugin"): + with self.prefix(src='../plugins/filepicker/%s' % self.args['configuration'], dst="llplugin"): self.path("basic_plugin_filepicker.dll") - self.end_prefix() # Media plugins - LibVLC - if self.prefix(src='../plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): + with self.prefix(src='../plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): self.path("media_plugin_libvlc.dll") - self.end_prefix() # Media plugins - CEF - if self.prefix(src='../plugins/cef/%s' % self.args['configuration'], dst="llplugin"): + with self.prefix(src='../plugins/cef/%s' % self.args['configuration'], dst="llplugin"): self.path("media_plugin_cef.dll") - self.end_prefix() # CEF runtime files - debug # CEF runtime files - not debug (release, relwithdebinfo etc.) config = 'debug' if self.args['configuration'].lower() == 'debug' else 'release' - if self.prefix(src=os.path.join(pkgdir, 'bin', config), dst="llplugin"): + 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") @@ -430,24 +552,21 @@ class WindowsManifest(ViewerManifest): self.path("natives_blob.bin") self.path("snapshot_blob.bin") self.path("v8_context_snapshot.bin") - self.end_prefix() - if self.prefix(src=os.path.join(pkgdir, 'bin', config, 'swiftshader'), dst=os.path.join("llplugin", 'swiftshader')): + with self.prefix(src=os.path.join(pkgdir, 'bin', config, 'swiftshader'), dst=os.path.join("llplugin", 'swiftshader')): self.path("libEGL.dll") self.path("libGLESv2.dll") - self.end_prefix() # CEF files common to all configurations - if self.prefix(src=os.path.join(pkgdir, 'resources'), dst="llplugin"): + 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") - self.end_prefix() - if self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('llplugin', 'locales')): + 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") @@ -501,13 +620,11 @@ class WindowsManifest(ViewerManifest): self.path("vi.pak") self.path("zh-CN.pak") self.path("zh-TW.pak") - self.end_prefix() - if self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): + with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): self.path("libvlc.dll") self.path("libvlccore.dll") self.path("plugins/") - self.end_prefix() if not self.is_packaging_viewer(): @@ -583,6 +700,8 @@ class WindowsManifest(ViewerManifest): 'version' : '.'.join(self.args['version']), 'version_short' : '.'.join(self.args['version'][:-1]), 'version_dashes' : '-'.join(self.args['version']), + 'version_registry' : '%s(%s)' % + ('.'.join(self.args['version']), self.address_size), 'final_exe' : self.final_exe(), 'flags':'', 'app_name':self.app_name(), @@ -605,16 +724,16 @@ class WindowsManifest(ViewerManifest): substitution_strings['caption'] = self.app_name() + ' ${VERSION}' inst_vars_template = """ - !define INSTOUTFILE "%(installer_file)s" !define INSTEXE "%(final_exe)s" + !define INSTOUTFILE "%(installer_file)s" !define APPNAME "%(app_name)s" !define APPNAMEONEWORD "%(app_name_oneword)s" - !define VERSION "%(version_short)s" - !define VERSION_LONG "%(version)s" - !define VERSION_DASHES "%(version_dashes)s" !define URLNAME "secondlife" !define CAPTIONSTR "%(caption)s" !define VENDORSTR "Singularity Viewer Project" + !define VERSION "%(version_short)s" + !define VERSION_LONG "%(version)s" + !define VERSION_DASHES "%(version_dashes)s" """ tempfile = "%s_setup_tmp.nsi" % self.viewer_branding_id() @@ -626,8 +745,7 @@ class WindowsManifest(ViewerManifest): "%%INST_VARS%%":inst_vars_template % substitution_strings, "%%INSTALL_FILES%%":self.nsi_file_commands(True), "%%DELETE_FILES%%":self.nsi_file_commands(False), - "%%WIN64_BIN_BUILD%%":"!define WIN64_BIN_BUILD 1" if self.is_win64() else "", - }) + "%%WIN64_BIN_BUILD%%":"!define WIN64_BIN_BUILD 1" if (self.address_size == 64) else "",}) # We use the Unicode version of NSIS, available from # http://www.scratchpaper.com/ @@ -656,85 +774,51 @@ class WindowsManifest(ViewerManifest): class Windows_i686_Manifest(WindowsManifest): - def construct(self): - super(Windows_i686_Manifest, self).construct() - - # Get shared libs from the shared libs staging directory - if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), - dst=""): - - # Security - self.path("libcrypto-1_1.dll") - self.path("libssl-1_1.dll") - - # Get fmod studio dll, continue if missing - try: - if self.args['configuration'].lower() == 'debug': - self.path("fmodL.dll") - else: - self.path("fmod.dll") - except: - print "Skipping fmodstudio audio library(assuming other audio engine)" - - self.end_prefix() - - if self.prefix(src=os.path.join(self.args['build'], os.pardir, 'packages', 'bin'), dst="redist"): - self.path("vc_redist.x86.exe") - self.end_prefix() - + # Although we aren't literally passed ADDRESS_SIZE, we can infer it from + # the passed 'arch', which is used to select the specific subclass. + address_size = 32 class Windows_x86_64_Manifest(WindowsManifest): - def construct(self): - super(Windows_x86_64_Manifest, self).construct() - - # Get shared libs from the shared libs staging directory - if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), - dst=""): - - # Security - self.path("libcrypto-1_1-x64.dll") - self.path("libssl-1_1-x64.dll") - - # Get fmodstudio dll, continue if missing - try: - if self.args['configuration'].lower() == 'debug': - self.path("fmodL64.dll") - else: - self.path("fmod64.dll") - except: - print "Skipping fmodstudio audio library(assuming other audio engine)" - - self.end_prefix() - - if self.prefix(src=os.path.join(self.args['build'], os.pardir, 'packages', 'bin'), dst="redist"): - self.path("vc_redist.x64.exe") - self.end_prefix() + address_size = 64 class DarwinManifest(ViewerManifest): + build_data_json_platform = 'mac' + + def finish_build_data_dict(self, build_data_dict): + build_data_dict.update({'Bundle Id':self.args['bundleid']}) + return build_data_dict + + def is_packaging_viewer(self): + # darwin requires full app bundle packaging even for debugging. + return True + + 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(self.args['configuration'] + "/" + self.app_name() + ".app", dst="") + self.path(os.path.join(self.args['configuration'], self.app_name()+".app"), dst="") - if self.prefix(src="", dst="Contents"): # everything goes in Contents + with self.prefix(src="", dst="Contents"): # everything goes in Contents # most everything goes in the Resources directory - if self.prefix(src="", dst="Resources"): + with self.prefix(src="", dst="Resources"): super(DarwinManifest, self).construct() - if self.prefix("cursors_mac"): + with self.prefix(src_dst="cursors_mac"): self.path("*.tif") - self.end_prefix("cursors_mac") self.path("licenses-mac.txt", dst="licenses.txt") self.path("featuretable_mac.txt") + self.path("SecondLife.nib") icon_path = self.icon_path() - if self.prefix(src=icon_path, dst="") : + with self.prefix(src=icon_path, dst="") : self.path("%s.icns" % self.viewer_branding_id()) - self.end_prefix(icon_path) - - self.path("SecondLife.nib") # Translations self.path("English.lproj") @@ -769,7 +853,7 @@ class DarwinManifest(ViewerManifest): os.path.join(libdir, libfile)), dst=libfile) - if self.prefix(src=libdir, alt_build=alt_libdir, dst=""): + with self.prefix(src=libdir, alt_build=alt_libdir, dst=""): for libfile in ( "libapr-1.0.dylib", "libaprutil-1.0.dylib", @@ -782,6 +866,7 @@ class DarwinManifest(ViewerManifest): ): dylibs += self.path_optional(libfile) + # SLVoice and vivox lols, no symlinks needed for libfile in ( 'libortp.dylib', 'libsndfile.dylib', @@ -792,12 +877,9 @@ class DarwinManifest(ViewerManifest): 'SLVoice' ): self.path(libfile) - - self.end_prefix() - if self.prefix(src= '', alt_build=libdir): + with self.prefix(src= '', alt_build=libdir): dylibs += self.add_extra_libraries() - self.end_prefix() # our apps for app_bld_dir, app in (#("mac_crash_logger", "mac-crash-logger.app"), @@ -821,7 +903,7 @@ class DarwinManifest(ViewerManifest): print "Can't symlink %s -> %s: %s" % (src, dst, err) # Dullahan helper apps go inside AlchemyPlugin.app - if self.prefix(src="", dst="AlchemyPlugin.app/Contents/Frameworks"): + with self.prefix(src="", dst="AlchemyPlugin.app/Contents/Frameworks"): helperappfile = 'DullahanHelper.app' self.path2basename(relbinpkgdir, helperappfile) @@ -842,27 +924,20 @@ class DarwinManifest(ViewerManifest): '@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework', '@executable_path/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework', helperexecutablepath]) - self.end_prefix() - # plugins - if self.prefix(src="", dst="llplugin"): + with self.prefix(src="", dst="llplugin"): self.path2basename("../media_plugins/quicktime/" + self.args['configuration'], "media_plugin_quicktime.dylib") self.path2basename("../media_plugins/cef/" + self.args['configuration'], "media_plugin_cef.dylib") - self.end_prefix("llplugin") - # command line arguments for connecting to the proper grid self.put_in_file(self.flags_list(), 'arguments.txt') - self.end_prefix("Resources") - # CEF framework goes inside Second Life.app/Contents/Frameworks - if self.prefix(src="", dst="Frameworks"): + with self.prefix(src="", dst="Frameworks"): frameworkfile="Chromium Embedded Framework.framework" self.path2basename(relpkgdir, frameworkfile) - self.end_prefix("Frameworks") # This code constructs a relative path from the # target framework folder back to the location of the symlink. @@ -905,8 +980,6 @@ class DarwinManifest(ViewerManifest): print "Can't symlink %s -> %s: %s" % (origin, target, err) raise - self.end_prefix("Contents") - # 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. @@ -1067,124 +1140,50 @@ class LinuxManifest(ViewerManifest): relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") - if 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",self.viewer_branding_id()) - if 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.end_prefix("etc") self.path("install.sh") - self.end_prefix("linux_tools") # Create an appropriate gridargs.dat for this package, denoting required grid. # self.put_in_file(self.flags_list(), 'gridargs.dat') - if self.prefix(src="", dst="bin"): + with self.prefix(dst="bin"): self.path("%s-bin"%self.viewer_branding_id(),"do-not-directly-run-%s-bin"%self.viewer_branding_id()) self.path2basename("../llplugin/slplugin", "SLPlugin") - self.end_prefix("bin") - if self.prefix("res-sdl"): + with self.prefix("res-sdl"): self.path("*") # recurse - self.end_prefix("res-sdl") # Get the icons based on the channel type icon_path = self.icon_path() print "DEBUG: icon_path '%s'" % icon_path - if self.prefix(src=icon_path, dst="") : + with self.prefix(src=icon_path) : self.path("viewer.png","viewer_icon.png") - if self.prefix(src="",dst="res-sdl") : + with self.prefix(dst="res-sdl") : self.path("viewer_256.BMP","viewer_icon.BMP") - self.end_prefix("res-sdl") - self.end_prefix(icon_path) # plugins - if self.prefix(src="../plugins", dst="bin/llplugin"): + with self.prefix(src="../plugins", dst="bin/llplugin"): self.path2basename("filepicker", "libbasic_plugin_filepicker.so") self.path("gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") self.path("cef/libmedia_plugin_cef.so", "libmedia_plugin_cef.so") self.path2basename("libvlc", "libmedia_plugin_libvlc.so") - self.end_prefix() - # CEF files - if self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="bin"): - self.path("chrome-sandbox") - self.path("dullahan_host") - self.path("natives_blob.bin") - self.path("snapshot_blob.bin") - self.path("v8_context_snapshot.bin") - self.end_prefix() + with self.prefix(src=os.path.join(pkgdir, 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): + self.path( "plugins.dat" ) + self.path( "*/*.so" ) - if self.prefix(src=os.path.join(pkgdir, 'resources'), dst="bin"): - self.path("cef.pak") - self.path("cef_extensions.pak") - self.path("cef_100_percent.pak") - self.path("cef_200_percent.pak") - self.path("devtools_resources.pak") - self.path("icudtl.dat") - self.end_prefix() - - if self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('bin', 'locales')): - self.path("am.pak") - self.path("ar.pak") - self.path("bg.pak") - 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.pak") - self.path("es-419.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") - self.end_prefix() + with self.prefix(src=os.path.join(pkgdir, 'lib' ), dst="lib"): + self.path( "libvlc*.so*" ) # llcommon if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): @@ -1193,45 +1192,53 @@ class LinuxManifest(ViewerManifest): self.path("featuretable_linux.txt") def package_finish(self): - self.strip_binaries() - # Fix access permissions - self.run_command(""" - find %(dst)s -type d | xargs --no-run-if-empty chmod 755; - find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755; - find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555; - find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644; - find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444; - true""" % {'dst':self.get_dst_prefix() }) + installer_name = self.installer_base_name() - def strip_binaries(self): - print "* Going strip-crazy on the packaged binaries" - # makes some small assumptions about our packaged dir structure - self.run_command(r"find %(d)r/lib %(d)r/lib32 %(d)r/lib64 -type f \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) - self.run_command(r"find %(d)r/bin -executable -type f \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) + self.strip_binaries() + + # Fix access permissions + self.run_command(['find', self.get_dst_prefix(), + '-type', 'd', '-exec', 'chmod', '755', '{}', ';']) + for old, new in ('0700', '0755'), ('0500', '0555'), ('0600', '0644'), ('0400', '0444'): + self.run_command(['find', self.get_dst_prefix(), + '-type', 'f', '-perm', old, + '-exec', 'chmod', new, '{}', ';']) + self.package_file = installer_name + '.tar.xz' def create_archive(self): installer_name = self.installer_base_name() # temporarily move directory tree so that it has the right # name in the tarfile - self.run_command("mv %(dst)s %(inst)s" % { - 'dst': self.get_dst_prefix(), - 'inst': self.build_path_of(installer_name)}) + realname = self.get_dst_prefix() + tempname = self.build_path_of(installer_name) + self.run_command(["mv", realname, tempname]) try: - # --numeric-owner hides the username of the builder for - # security etc. - self.run_command('tar -C %(dir)s --numeric-owner -cJf ' - '%(inst_path)s.tar.xz %(inst_name)s' % { - 'dir': self.get_build_prefix(), - 'inst_name': installer_name, - 'inst_path':self.build_path_of(installer_name)}) + # only create tarball if it's a release build. + if True:# self.args['buildtype'].lower() == 'release': + # --numeric-owner hides the username of the builder for + # security etc. + self.run_command(['tar', '-C', self.get_build_prefix(), + '--numeric-owner', '-cJf', + tempname + '.tar.xz', installer_name]) + else: + print "Skipping %s.tar.xz for non-Release build (%s)" % \ + (installer_name, self.args['buildtype']) finally: - self.run_command("mv %(inst)s %(dst)s" % { - 'dst': self.get_dst_prefix(), - 'inst': self.build_path_of(installer_name)}) - self.package_file = installer_name + '.tar.xz' + self.run_command(["mv", tempname, realname]) + def strip_binaries(self): + if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer(): + print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" + # makes some small assumptions about our packaged dir structure + self.run_command( + ["find"] + + [os.path.join(self.get_dst_prefix(), dir) for dir in ('bin', 'lib')] + + ['-type', 'f', '!', '-name', '*.py', + '!', '-name', 'update_install', '-exec', 'strip', '-S', '{}', ';']) class Linux_i686_Manifest(LinuxManifest): + address_size = 32 + def construct(self): super(Linux_i686_Manifest, self).construct() @@ -1251,22 +1258,22 @@ class Linux_i686_Manifest(LinuxManifest): self.path("libtcmalloc_minimal.so.0") self.path("libtcmalloc_minimal.so.0.2.2") - self.end_prefix("lib") + self.end_prefix() # Vivox runtimes - if self.prefix(src=relpkgdir, dst="bin"): + with self.prefix(src=relpkgdir, dst="bin"): self.path("SLVoice") - self.end_prefix("bin") - if self.prefix(src=relpkgdir, dst="lib"): + with self.prefix(src=relpkgdir, dst="lib"): self.path("libortp.so") self.path("libsndfile.so.1") self.path("libvivoxoal.so.1") self.path("libvivoxsdk.so") self.path("libvivoxplatform.so") - self.end_prefix("lib") class Linux_x86_64_Manifest(LinuxManifest): + address_size = 64 + def construct(self): super(Linux_x86_64_Manifest, self).construct() @@ -1283,7 +1290,6 @@ class Linux_x86_64_Manifest(LinuxManifest): self.path("libhunspell-1.3.so*") self.path("libalut.so*") self.path("libopenal.so*") - self.path("libfreetype.so*") try: self.path("libtcmalloc.so*") #formerly called google perf tools @@ -1299,47 +1305,26 @@ class Linux_x86_64_Manifest(LinuxManifest): print "Skipping libfmod.so - not found" pass - self.end_prefix("lib64") - # Vivox runtimes - if self.prefix(src=relpkgdir, dst="bin"): + with self.prefix(src=relpkgdir, dst="bin"): self.path("SLVoice") - self.end_prefix("bin") - if self.prefix(src=relpkgdir, dst="lib32"): + with self.prefix(src=relpkgdir, dst="lib32"): self.path("libortp.so") self.path("libsndfile.so.1") self.path("libvivoxoal.so.1") self.path("libvivoxsdk.so") self.path("libvivoxplatform.so") - self.end_prefix("lib32") # plugin runtime - if self.prefix(src=relpkgdir, dst="lib64"): + with self.prefix(src=relpkgdir, dst="lib64"): self.path("libcef.so") - self.end_prefix("lib64") ################################################################ -def symlinkf(src, dst): - """ - Like ln -sf, but uses os.symlink() instead of running ln. - """ - try: - os.symlink(src, dst) - except OSError, err: - if err.errno != errno.EEXIST: - raise - # We could just blithely attempt to remove and recreate the target - # file, but that strategy doesn't work so well if we don't have - # permissions to remove it. Check to see if it's already the - # symlink we want, which is the usual reason for EEXIST. - if not (os.path.islink(dst) and os.readlink(dst) == src): - # Here either dst isn't a symlink or it's the wrong symlink. - # Remove and recreate. Caller will just have to deal with any - # exceptions at this stage. - os.remove(dst) - os.symlink(src, dst) - 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) From 0464e611c7c50fbf93a7702c5480761afea82f99 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 21 Apr 2019 20:39:54 -0400 Subject: [PATCH 36/91] Sync with future branch for urls --- indra/llui/llurlentry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 31d9377d5..d3e9ffee4 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -338,7 +338,7 @@ std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() : LLUrlEntryBase() { - mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?\\S*)?)?", + mPattern = boost::regex("(https?://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?\\S*)?)?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -425,7 +425,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const LLUrlEntrySLURL::LLUrlEntrySLURL() { // see http://slurl.com/about.php for details on the SLURL format - mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?\\S*)?)?", + mPattern = boost::regex("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?\\S*)?)?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slurl.xml"; mTooltip = LLTrans::getString("TooltipSLURL"); From 190e3e8c2f2fbe3755a6536029e6c0a2d4f25bb8 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 21 Apr 2019 22:06:33 -0400 Subject: [PATCH 37/91] Fix link replacement being enforced improperly, thanks Beware! --- indra/llui/lltexteditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 14f451d35..6bdef7142 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4274,7 +4274,7 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool return; std::string text = prepend_newline && !mWText.empty() ? ('\n' + new_text) : new_text; - appendTextImpl(text, style); + appendTextImpl(text, style, force_replace_links); if (!allow_undo) { @@ -4340,7 +4340,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s auto url = match.getUrl(); const auto& label = match.getLabel(); - if (force_replace_links || replace_links || url == label) + if (force_replace_links || url == label) { // add icon before url if need /* Singu TODO: Icons next to links? From 14774b03ad6cd36ce6d317636faa61fb4e3b6ade Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 22 Apr 2019 16:16:43 -0400 Subject: [PATCH 38/91] Squash Nai's bug and still fix the issue? --- indra/newview/llinventorypanel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index b7a2c95b2..4cb2bf65f 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -565,7 +565,7 @@ void LLInventoryPanel::modelChanged(U32 mask) LLFolderViewFolder* parent = view_item->getParentFolder(); if(parent) { - parent->dirtyFilter(); + parent->requestSort(); } } } @@ -634,7 +634,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { // Add the UI element for this item. buildNewViews(item_id); - if (auto parent = getItemByID(model_item->getParentUUID())) parent->dirtyFilter(); + if (auto parent = getFolderByID(model_item->getParentUUID())) parent->requestSort(); // Select any newly created object that has the auto rename at top of folder root set. if(mFolderRoot.get()->getRoot()->needsAutoRename()) { @@ -687,7 +687,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { updateFolderLabel(viewmodel_folder->getUUID()); }*/ - old_parent->dirtyFilter(); + old_parent->requestSort(); } } } @@ -703,7 +703,7 @@ void LLInventoryPanel::modelChanged(U32 mask) view_item->destroyView(); if(parent) { - parent->dirtyFilter(); + parent->requestSort(); /*auto* viewmodel_folder = parent->getListener(); if(viewmodel_folder) { From f2b773dac7f1b25ab68fbacf8754a256336ee451 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Wed, 24 Apr 2019 17:34:45 -0400 Subject: [PATCH 39/91] Fix linux build, thanks Router --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 0fb46724d..645dfce37 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3012,7 +3012,7 @@ options -G - 'Unix Makefiles' + Unix Makefiles default @@ -3036,7 +3036,7 @@ options -G - 'Unix Makefiles' + Unix Makefiles name @@ -3068,7 +3068,7 @@ options -G - 'Unix Makefiles' + Unix Makefiles -DWORD_SIZE:STRING=64 @@ -3093,7 +3093,7 @@ options -G - 'Unix Makefiles' + Unix Makefiles -DWORD_SIZE:STRING=64 From 2d06d455cd98afd06cc50974c25e44a424202ad4 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 25 Apr 2019 01:48:56 -0400 Subject: [PATCH 40/91] ProductEngines are Lindens, I guess. --- indra/newview/lfsimfeaturehandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/lfsimfeaturehandler.cpp b/indra/newview/lfsimfeaturehandler.cpp index dbdf2e5de..e9fa957b0 100644 --- a/indra/newview/lfsimfeaturehandler.cpp +++ b/indra/newview/lfsimfeaturehandler.cpp @@ -34,7 +34,7 @@ LFSimFeatureHandler::LFSimFeatureHandler() { if (!gHippoGridManager->getCurrentGrid()->isSecondLife()) // Remove this line if we ever handle SecondLife sim features gAgent.addRegionChangedCallback(boost::bind(&LFSimFeatureHandler::handleRegionChange, this)); - LLMuteList::instance().mGodLastNames.insert("Linden"); + LLMuteList::instance().mGodLastNames = { "Linden", "ProductEngine" }; } ExportPolicy LFSimFeatureHandler::exportPolicy() const @@ -138,7 +138,7 @@ void LFSimFeatureHandler::setSupportedFeatures() } else { - mute_list.mGodLastNames.insert("Linden"); + mute_list.mGodLastNames = { "Linden", "ProductEngine" }; } } } From 15a8e1f08f910688e31948bdca7ae53ce30ec3d4 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 25 Apr 2019 15:40:51 -0400 Subject: [PATCH 41/91] Don't limit estate managers on non-SL grids, let the grid do that. --- indra/newview/llfloaterregioninfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 64c6cc50e..119910950 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -1599,7 +1599,7 @@ void LLPanelEstateInfo::onClickAddEstateManager() { LLCtrlListInterface *list = childGetListInterface("estate_manager_name_list"); if (!list) return; - if (list->getItemCount() >= ESTATE_MAX_MANAGERS) + if (gHippoGridManager->getConnectedGrid()->isSecondLife() && list->getItemCount() >= ESTATE_MAX_MANAGERS) { // Tell user they can't add more managers LLSD args; args["MAX_MANAGER"] = llformat("%d",ESTATE_MAX_MANAGERS); From 005ec30d7bc1bddde391e899844da5a092877bd7 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 25 Apr 2019 17:07:51 -0400 Subject: [PATCH 42/91] Max estate managers is 15 now --- indra/llmessage/llregionflags.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h index fb2c9185b..70194342a 100644 --- a/indra/llmessage/llregionflags.h +++ b/indra/llmessage/llregionflags.h @@ -182,7 +182,7 @@ const U32 ESTATE_ACCESS_MANAGER_ADD = 1 << 8; const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9; const U32 ESTATE_ACCESS_NO_REPLY = 1 << 10; -const S32 ESTATE_MAX_MANAGERS = 10; +const S32 ESTATE_MAX_MANAGERS = 15; const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access, banned const S32 ESTATE_MAX_GROUP_IDS = (S32) ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET; From d33ae35fb3d654ee2e1fa1317f6f096391c8c4c1 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 27 Apr 2019 03:01:49 -0400 Subject: [PATCH 43/91] OK in preferenes should save per account settings if possible --- indra/newview/llfloaterpreference.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 95f92c1c6..bfdaf6abb 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -441,6 +441,8 @@ void LLFloaterPreference::onBtnOK() close(false); gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); + if (gAgentID.notNull()) + gSavedPerAccountSettings.saveToFile( gSavedSettings.getString("PerAccountSettingsFile"), TRUE ); } else { From b8c9d9c0f34dd1cf5a215d3ccd140087cf485c43 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 27 Apr 2019 03:02:47 -0400 Subject: [PATCH 44/91] Clean up LLScrollListCtrl and add ability to getSortColumns() --- indra/llui/llscrolllistctrl.cpp | 21 +++++++++------------ indra/llui/llscrolllistctrl.h | 5 +++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 7af6313af..2d84791b1 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -62,7 +62,7 @@ std::vector LLScrollListCtrl::sMenus = {}; // List menus that recur, // local structures & classes. struct SortScrollListItem { - SortScrollListItem(const std::vector >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal) + SortScrollListItem(const std::vector& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal) : mSortOrders(sort_orders) , mSortSignal(sort_signal) {} @@ -102,7 +102,7 @@ struct SortScrollListItem } - typedef std::vector > sort_order_t; + typedef std::vector sort_order_t; const LLScrollListCtrl::sort_signal_t* mSortSignal; const sort_order_t& mSortOrders; }; @@ -2294,7 +2294,7 @@ struct SameSortColumn SameSortColumn(S32 column) : mColumn(column) {} S32 mColumn; - bool operator()(std::pair sort_column) { return sort_column.first == mColumn; } + bool operator()(LLScrollListCtrl::sort_column_t sort_column) { return sort_column.first == mColumn; } }; BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) @@ -2392,7 +2392,7 @@ void LLScrollListCtrl::updateSort() const // for one-shot sorts, does not save sort column/order void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending) { - std::vector > sort_column; + std::vector sort_column; sort_column.push_back(std::make_pair(column, ascending)); // do stable sort to preserve any previous sorts @@ -2733,15 +2733,15 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac std::string value = row_child->getTextContents(); row["columns"][column_idx]["value"] = value; - std::string columnname(""); + std::string columnname; if (row_child->getAttributeString("name", columnname)) row["columns"][column_idx]["column"] = columnname; - std::string font(""); + std::string font; if (row_child->getAttributeString("font", font)) row["columns"][column_idx]["font"] = font; - std::string font_style(""); + std::string font_style; if (row_child->getAttributeString("font-style", font_style)) row["columns"][column_idx]["font-style"] = font_style; @@ -2771,12 +2771,9 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac void LLScrollListCtrl::copy() { std::string buffer; - - std::vector items = getAllSelected(); - std::vector::iterator itor; - for (itor = items.begin(); itor != items.end(); ++itor) + for (auto item : getAllSelected()) { - buffer += (*itor)->getContentsCSV() + "\n"; + buffer += item->getContentsCSV() + '\n'; } gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length()); } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 5c914ee17..3a2adf5c6 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -320,6 +320,8 @@ public: void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width); S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; } + typedef std::pair sort_column_t; + const std::vector& getSortColumns() const { return mSortColumns; } std::string getSortColumnName(); BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } BOOL hasSortOrder() const; @@ -354,7 +356,7 @@ public: void setNeedsSortColumn(S32 col) { if(!isSorted())return; - for(std::vector >::iterator it=mSortColumns.begin();it!=mSortColumns.end();++it) + for(auto it=mSortColumns.begin();it!=mSortColumns.end();++it) { if((*it).first == col) { @@ -482,7 +484,6 @@ private: typedef std::vector ordered_columns_t; ordered_columns_t mColumnsIndexed; - typedef std::pair sort_column_t; std::vector mSortColumns; sort_signal_t* mSortCallback; From 40faef0bf87f764acdad126fd895b14cec8d7eff Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 27 Apr 2019 03:04:11 -0400 Subject: [PATCH 45/91] If a setting is LLSD, display it in pretty print xml in debug This should probably get its own TextEditor later on, it'd look better. --- indra/newview/llfloatersettingsdebug.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 46f45b1e2..aac8e2223 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -39,6 +39,7 @@ #include "llfloater.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" +#include "llsdserialize.h" #include "llspinctrl.h" #include "lltexteditor.h" #include "lluictrlfactory.h" @@ -215,6 +216,19 @@ void LLFloaterSettingsDebug::onCommitSettings() col4U.mV[VALPHA] = getChild("val_spinner_4")->getValue().asInteger(); mCurrentControlVariable->set(col4U.getValue()); break; + case TYPE_LLSD: + { + auto val = getChild("val_text")->getValue().asString(); + LLSD sd; + if (!val.empty()) + { + std::istringstream str(val); + if (LLSDSerialize::fromXML(sd, str) == LLSDParser::PARSE_FAILURE) + break; + } + mCurrentControlVariable->set(sd); + break; + } default: break; } @@ -522,6 +536,14 @@ void LLFloaterSettingsDebug::updateControl() break; } + case TYPE_LLSD: + getChildView("val_text")->setVisible(true); + { + std::ostringstream str; + LLSDSerialize::toPrettyXML(sd, str); + getChild("val_text")->setValue(str.str()); + } + break; default: mComment->setText(std::string("unknown")); break; From 331023e87862231680f0b5bf0c22ba932e49ad9a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 03:39:13 -0400 Subject: [PATCH 46/91] Strings and LLSD settings now get edited with a text editor --- .../skins/default/xui/en-us/floater_settings_debug.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/newview/skins/default/xui/en-us/floater_settings_debug.xml b/indra/newview/skins/default/xui/en-us/floater_settings_debug.xml index 042081971..df43050a6 100644 --- a/indra/newview/skins/default/xui/en-us/floater_settings_debug.xml +++ b/indra/newview/skins/default/xui/en-us/floater_settings_debug.xml @@ -20,10 +20,7 @@ - - - - @@ -43,7 +40,10 @@ max_val="10000000" name="val_spinner_4" visible="false" width="120"> - From c6b17c9005d617d7cc2eb8962153da5dc6f8cc41 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 06:17:57 -0400 Subject: [PATCH 47/91] Probably fix Torric's issue of notes being overwritten --- indra/newview/llpanelavatar.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 16b224bd9..98a3621df 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -1501,10 +1501,13 @@ void LLPanelAvatar::processProperties(void* data, EAvatarProcessorType type) const LLAvatarNotes* pAvatarNotes = static_cast( data ); if (pAvatarNotes && (mAvatarID == pAvatarNotes->target_id) && (pAvatarNotes->target_id != LLUUID::null)) { - auto notes = getChildView("notes edit"); - notes->setEnabled(true); - notes->setValue(pAvatarNotes->notes); - mHaveNotes = true; + if (!mHaveNotes) // Only update the UI if we don't already have the notes, we could be editing them now! + { + auto notes = getChildView("notes edit"); + notes->setEnabled(true); + notes->setValue(pAvatarNotes->notes); + mHaveNotes = true; + } mLastNotes = pAvatarNotes->notes; } } From b34021abd5f21d7347321b50aa23a87b002645a7 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 06:22:42 -0400 Subject: [PATCH 48/91] Fix Beware's notecard embed crash by immediately reflowing upon remove We'll want to revisit this later and more optimally fix it. --- indra/llui/lltexteditor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 6bdef7142..f366cb2df 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4739,7 +4739,11 @@ S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) createDefaultSegment(); mTextIsUpToDate = FALSE; - needsReflow(/*pos*/); + + /*needsReflow(pos);*/ + // Singu Note: This kinda sucks for performance of delete, fix this later (with LLTextBase merge?) + updateLineStartList(); + mReflowNeeded = false; return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length } From 64561fa57b236729b1f67b487e8edfa60cbc390a Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 06:30:20 -0400 Subject: [PATCH 49/91] [Radar] Feature Request: Make sort order persist --- indra/newview/app_settings/settings.xml | 14 ++++++++++++++ indra/newview/llfloateravatarlist.cpp | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index e0be85ca3..822842c0a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8048,6 +8048,20 @@ This should be as low as possible, but too low may break functionality Value 0 + RadarSortOrder + + Comment + Sort order for the radar, map of column names to ascending boolean + Persist + 1 + Type + LLSD + Value + + distance + 1 + + FloaterLandRect5 Comment diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index bb614ec31..158d69b37 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -257,6 +257,14 @@ LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar")), LLFloaterAvatarList::~LLFloaterAvatarList() { mCleanup = true; + LLSD sort; + for (const auto& col : mAvatarList->getSortColumns()) + { + sort.append(mAvatarList->getColumn(col.first)->mName); + sort.append(col.second); + } + + gSavedSettings.setLLSD("RadarSortOrder", sort); mAvatars.clear(); } @@ -425,7 +433,13 @@ BOOL LLFloaterAvatarList::postBuild() // Get a pointer to the scroll list from the interface mAvatarList = getChild("avatar_list"); - mAvatarList->sortByColumn("distance", true); + const LLSD sort = gSavedSettings.getLLSD("RadarSortOrder"); + for (auto it = sort.beginArray(), end = sort.endArray(); it < end; ++it) + { + const auto& column_name = (*it); + const auto& ascending = (*++it); + mAvatarList->sortByColumn(column_name.asStringRef(), ascending.asBoolean()); + } mAvatarList->setCommitOnSelectionChange(true); mAvatarList->setCommitCallback(boost::bind(&LLFloaterAvatarList::onSelectName,this)); mAvatarList->setDoubleClickCallback(boost::bind(&LLFloaterAvatarList::onClickFocus,this)); From 56378772cc0b7a1109dd56dcba1c9f3d9f8821a4 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 08:51:22 -0400 Subject: [PATCH 50/91] Add operator == for LLSD to llsdutil.h --- indra/llcommon/llsdutil.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 1d68009ca..e9c366212 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -144,6 +144,11 @@ LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, /// is_approx_equal_fraction(). LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1); +inline bool operator==(const LLSD& lhs, const LLSD& rhs) +{ + return llsd_equals(lhs, rhs, 8); +} + // Simple function to copy data out of input & output iterators if // there is no need for casting. template LLSD llsd_copy_array(Input iter, Input end) From 0cc146a170b5a3506830292ba795b341402a5503 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 09:01:38 -0400 Subject: [PATCH 51/91] Change LLInstanceTracker to use a boost unordered map Also remove unused functions and an unused typedef with it --- indra/llcommon/llinstancetracker.h | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 52db12314..50bf367d1 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -28,7 +28,7 @@ #ifndef LL_LLINSTANCETRACKER_H #define LL_LLINSTANCETRACKER_H -#include +#include #include #include "llstringtable.h" @@ -89,8 +89,7 @@ template class LLInstanceTracker : public LLInstanceTrackerBase { typedef LLInstanceTracker MyT; - typedef typename std::map InstanceMap; - typedef typename InstanceMap::iterator::difference_type difference_type; + typedef typename boost::unordered_map InstanceMap; struct StaticData: public StaticBase { InstanceMap sMap; @@ -121,10 +120,6 @@ public: void increment() { mIterator++; } void decrement() { --mIterator; } - difference_type distance_to(instance_iter const& other) const - { - return std::distance(mIterator, other.mIterator); - } bool equal(instance_iter const& other) const { return mIterator == other.mIterator; @@ -166,10 +161,6 @@ public: void increment() { mIterator++; } void decrement() { --mIterator; } - difference_type distance_to(instance_iter const& other) const - { - return std::distance(mIterator, other.mIterator); - } bool equal(key_iter const& other) const { return mIterator == other.mIterator; From 8653ee79eaaca47508681f5b97b6edaaf9ead82f Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 28 Apr 2019 09:02:43 -0400 Subject: [PATCH 52/91] Update Floater Inv Panel to be more modern --- indra/newview/lffloaterinvpanel.cpp | 20 ++++++++++---------- indra/newview/lffloaterinvpanel.h | 7 ++++--- indra/newview/llinventorybridge.cpp | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index 2578c90a2..b09885890 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -26,19 +26,19 @@ #include "lluictrlfactory.h" -LFFloaterInvPanel::LFFloaterInvPanel(const LLUUID& cat_id, LLInventoryModel* model, const std::string& name) -: LLInstanceTracker(cat_id) +LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, LLInventoryModel* model) +: LLInstanceTracker(cat) { mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2)); LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml"); LLPanel* panel = getChild("placeholder_panel"); - mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, LLSD().with("id", cat_id), panel->getRect(), model, true); + mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, cat, panel->getRect(), model ? model : &gInventory, true); mPanel->postBuild(); mPanel->setFollows(FOLLOWS_ALL); mPanel->setEnabled(true); addChild(mPanel); removeChild(panel); - setTitle(name); + setTitle(name.empty() ? mPanel->getRootFolder()->getLabel() : name); } LFFloaterInvPanel::~LFFloaterInvPanel() @@ -47,10 +47,10 @@ LFFloaterInvPanel::~LFFloaterInvPanel() } // static -void LFFloaterInvPanel::show(const LLUUID& cat_id, LLInventoryModel* model, const std::string& name) +void LFFloaterInvPanel::show(const LLSD& cat, const std::string& name, LLInventoryModel* model) { - LFFloaterInvPanel* floater = LFFloaterInvPanel::getInstance(cat_id); - if (!floater) floater = new LFFloaterInvPanel(cat_id, model, name); + LFFloaterInvPanel* floater = LFFloaterInvPanel::getInstance(cat); + if (!floater) floater = new LFFloaterInvPanel(cat, name, model); floater->open(); } @@ -63,10 +63,10 @@ void LFFloaterInvPanel::closeAll() { cache.push_back(&*i); } - // Now close all panels, without using instance_iter iterators. - for (std::vector::iterator i = cache.begin(); i != cache.end(); ++i) + // Now close all, without using instance_iter iterators. + for (auto& floater : cache) { - (*i)->close(); + floater->close(); } } diff --git a/indra/newview/lffloaterinvpanel.h b/indra/newview/lffloaterinvpanel.h index 3c9104db1..51dac3369 100644 --- a/indra/newview/lffloaterinvpanel.h +++ b/indra/newview/lffloaterinvpanel.h @@ -23,15 +23,16 @@ #include "llfloater.h" #include "llinstancetracker.h" +#include "llsdutil.h" -class LFFloaterInvPanel : public LLFloater, public LLInstanceTracker +class LFFloaterInvPanel : public LLFloater, public LLInstanceTracker { - LFFloaterInvPanel(const LLUUID& cat_id, class LLInventoryModel* model, const std::string& name); + LFFloaterInvPanel(const LLSD& cat, const std::string& name = LLStringUtil::null, class LLInventoryModel* model = nullptr); ~LFFloaterInvPanel(); public: - static void show(const LLUUID& cat_id, LLInventoryModel* model, const std::string& name); // Show the floater for cat_id (create with other params if necessary) + static void show(const LLSD& cat, const std::string& name = LLStringUtil::null, LLInventoryModel* model = nullptr); // Show the floater for cat (create with other params if necessary) static void closeAll(); // Called when not allowed to have inventory open /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 5326a64d3..2f527e134 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2961,7 +2961,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryModel* model = getInventoryModel(); LLViewerInventoryCategory* cat = getCategory(); if (!model || !cat) return; - LFFloaterInvPanel::show(mUUID, model, cat->getName()); + LFFloaterInvPanel::show(LLSD().with("id", mUUID), cat->getName(), model); return; } else if ("paste" == action) From 22f314d1589c87a8915814cefc016b328174fc6f Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 29 Apr 2019 00:15:43 -0400 Subject: [PATCH 53/91] Dynamically create Rect Controls for storing Inv in New Window Pos/Size --- indra/newview/lffloaterinvpanel.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index b09885890..1a7f3e13f 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -22,6 +22,8 @@ #include "lffloaterinvpanel.h" +#include + #include "llinventorypanel.h" #include "lluictrlfactory.h" @@ -39,6 +41,19 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L addChild(mPanel); removeChild(panel); setTitle(name.empty() ? mPanel->getRootFolder()->getLabel() : name); + + // Figure out a unique name for our rect control + const auto rect_control = llformat("FloaterInv%sRect", + cat.has("name") ? boost::algorithm::erase_all_copy(cat["name"].asStringRef(), " ").data() : + cat.has("id") ? cat["id"].asStringRef().data() : + cat["type"].asStringRef().data()); + + if (gSavedSettings.controlExists(rect_control)) // Set our initial rect to the stored control + setRect(gSavedSettings.getRect(rect_control)); + else // Or create the rect control if it doesn't exist + gSavedSettings.declareRect(rect_control, getRect(), "Rectangle for " + getTitle() + " window"); + + setRectControl(rect_control); } LFFloaterInvPanel::~LFFloaterInvPanel() From 140f5d3b0ab69cccd0735f5a6c7f751648959268 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 4 May 2019 03:24:28 -0400 Subject: [PATCH 54/91] Make Outbox and Favorites like other new inventory windows Yay search~ --- indra/newview/app_settings/settings.xml | 32 --------- indra/newview/lltoolbar.cpp | 4 ++ indra/newview/llviewermenu.cpp | 72 +++++++++++++++++++ .../default/xui/de/floater_inventory_favs.xml | 2 - .../default/xui/de/floater_my_outfits.xml | 2 - .../xui/en-us/floater_inventory_favs.xml | 4 -- .../default/xui/en-us/floater_my_outfits.xml | 4 -- .../skins/default/xui/en-us/menu_viewer.xml | 6 +- .../skins/default/xui/en-us/panel_toolbar.xml | 4 +- .../default/xui/es/floater_inventory_favs.xml | 4 -- .../default/xui/es/floater_my_outfits.xml | 4 -- 11 files changed, 82 insertions(+), 56 deletions(-) delete mode 100644 indra/newview/skins/default/xui/de/floater_inventory_favs.xml delete mode 100644 indra/newview/skins/default/xui/de/floater_my_outfits.xml delete mode 100644 indra/newview/skins/default/xui/en-us/floater_inventory_favs.xml delete mode 100644 indra/newview/skins/default/xui/en-us/floater_my_outfits.xml delete mode 100644 indra/newview/skins/default/xui/es/floater_inventory_favs.xml delete mode 100644 indra/newview/skins/default/xui/es/floater_my_outfits.xml diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 822842c0a..229855df7 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7493,22 +7493,6 @@ This should be as low as possible, but too low may break functionality 0 - FloaterFavoritesRect - - Comment - Rectangle for favorites window - Persist - 1 - Type - Rect - Value - - 0 - 400 - 250 - 0 - - FloaterFindRect2 Comment @@ -8174,22 +8158,6 @@ This should be as low as possible, but too low may break functionality 0 - FloaterMyOutfitsRect - - Comment - Rectangle for My Outfits window - Persist - 1 - Type - Rect - Value - - 0 - 400 - 250 - 0 - - FloaterObjectIMInfo Comment diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp index d6e748d72..46862bfbd 100644 --- a/indra/newview/lltoolbar.cpp +++ b/indra/newview/lltoolbar.cpp @@ -101,6 +101,7 @@ F32 LLToolBar::sInventoryAutoOpenTime = 1.f; // Functions // void show_floater(const std::string& floater_name); +void show_inv_floater(const LLSD& userdata, const std::string& field); LLToolBar::LLToolBar() : LLLayoutPanel() @@ -108,6 +109,9 @@ LLToolBar::LLToolBar() setIsChrome(TRUE); setFocusRoot(TRUE); mCommitCallbackRegistrar.add("ShowFloater", boost::bind(show_floater, _2)); + mCommitCallbackRegistrar.add("ShowInvFloater.ID", boost::bind(show_inv_floater, _2, "id")); + mCommitCallbackRegistrar.add("ShowInvFloater.Name", boost::bind(show_inv_floater, _2, "name")); + mCommitCallbackRegistrar.add("ShowInvFloater.Type", boost::bind(show_inv_floater, _2, "type")); } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 81eacc728..e2b7e52cf 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -48,6 +48,7 @@ #include "statemachine/aifilepicker.h" // newview includes +#include "lffloaterinvpanel.h" #include "lfsimfeaturehandler.h" #include "llagent.h" #include "llagentcamera.h" @@ -8935,6 +8936,70 @@ class SinguUrlAction : public view_listener_t } }; +void show_inv_floater(const LLSD& userdata, const std::string& field) +{ + LFFloaterInvPanel::show(LLSD().with(field, userdata)); +} + +static void visible_inv_floater(const LLSD& userdata, const std::string& field) +{ + gMenuHolder->findControl(userdata["control"].asString())->setValue(!!LFFloaterInvPanel::getInstance(LLSD().with(field, userdata["data"]))); +} + +class ShowInvFloaterID : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + show_inv_floater(userdata, "id"); + return true; + } +}; + +class VisibleInvFloaterID : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + visible_inv_floater(userdata, "id"); + return true; + } +}; + +class ShowInvFloaterName : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + show_inv_floater(userdata, "name"); + return true; + } +}; + +class VisibleInvFloaterName : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + visible_inv_floater(userdata, "name"); + return true; + } +}; + +class ShowInvFloaterType : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + show_inv_floater(userdata, "type"); + return true; + } +}; + +class VisibleInvFloaterType : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + visible_inv_floater(userdata, "type"); + return true; + } +}; + class VisibleSecondLife : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -9667,6 +9732,13 @@ void initialize_menus() addMenu(new SinguUrlAction(), "URLAction"); addMenu(new LLSyncAnimations(), "Tools.ResyncAnimations"); + addMenu(new ShowInvFloaterID, "ShowInvFloater.ID"); + addMenu(new ShowInvFloaterName, "ShowInvFloater.Name"); + addMenu(new ShowInvFloaterType, "ShowInvFloater.Type"); + addMenu(new VisibleInvFloaterID, "InvFloaterVisible.ID"); + addMenu(new VisibleInvFloaterName, "InvFloaterVisible.Name"); + addMenu(new VisibleInvFloaterType, "InvFloaterVisible.Type"); + // [RLVa:KB] - Checked: 2010-01-18 (RLVa-1.1.0m) | Added: RLVa-1.1.0m | OK if (rlv_handler_t::isEnabled()) { diff --git a/indra/newview/skins/default/xui/de/floater_inventory_favs.xml b/indra/newview/skins/default/xui/de/floater_inventory_favs.xml deleted file mode 100644 index b34911dd4..000000000 --- a/indra/newview/skins/default/xui/de/floater_inventory_favs.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/indra/newview/skins/default/xui/de/floater_my_outfits.xml b/indra/newview/skins/default/xui/de/floater_my_outfits.xml deleted file mode 100644 index cf8404461..000000000 --- a/indra/newview/skins/default/xui/de/floater_my_outfits.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/indra/newview/skins/default/xui/en-us/floater_inventory_favs.xml b/indra/newview/skins/default/xui/en-us/floater_inventory_favs.xml deleted file mode 100644 index 3de83ba05..000000000 --- a/indra/newview/skins/default/xui/en-us/floater_inventory_favs.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/indra/newview/skins/default/xui/en-us/floater_my_outfits.xml b/indra/newview/skins/default/xui/en-us/floater_my_outfits.xml deleted file mode 100644 index fc0751ff2..000000000 --- a/indra/newview/skins/default/xui/en-us/floater_my_outfits.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/indra/newview/skins/default/xui/en-us/menu_viewer.xml index 1e9ad54d2..138d195f3 100644 --- a/indra/newview/skins/default/xui/en-us/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en-us/menu_viewer.xml @@ -618,10 +618,12 @@ - + + - + + diff --git a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml index 458335fe5..2703a4cd6 100644 --- a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml +++ b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml @@ -399,12 +399,12 @@ diff --git a/indra/newview/skins/default/xui/es/floater_inventory_favs.xml b/indra/newview/skins/default/xui/es/floater_inventory_favs.xml deleted file mode 100644 index 49ec800f0..000000000 --- a/indra/newview/skins/default/xui/es/floater_inventory_favs.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/indra/newview/skins/default/xui/es/floater_my_outfits.xml b/indra/newview/skins/default/xui/es/floater_my_outfits.xml deleted file mode 100644 index 9f7195449..000000000 --- a/indra/newview/skins/default/xui/es/floater_my_outfits.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - From f0ef3a4569532ad82c753d0df68f2435f2436844 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 4 May 2019 03:29:48 -0400 Subject: [PATCH 55/91] Fix defaulted inventory folder floater title --- indra/newview/lffloaterinvpanel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index 1a7f3e13f..8a1b739ce 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -40,7 +40,8 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L mPanel->setEnabled(true); addChild(mPanel); removeChild(panel); - setTitle(name.empty() ? mPanel->getRootFolder()->getLabel() : name); + const auto& title = name.empty() ? mPanel->getRootFolder()->getName() : name; + setTitle(title); // Figure out a unique name for our rect control const auto rect_control = llformat("FloaterInv%sRect", @@ -51,7 +52,7 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L if (gSavedSettings.controlExists(rect_control)) // Set our initial rect to the stored control setRect(gSavedSettings.getRect(rect_control)); else // Or create the rect control if it doesn't exist - gSavedSettings.declareRect(rect_control, getRect(), "Rectangle for " + getTitle() + " window"); + gSavedSettings.declareRect(rect_control, getRect(), "Rectangle for " + title + " window"); setRectControl(rect_control); } From 6dadfaffac1288893c4b9341d0e5abfa78448189 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 4 May 2019 03:33:51 -0400 Subject: [PATCH 56/91] Improve rect control name of inventory folder floater This makes it more reliably usable and more understandable to the user. The downside of this is that folders with the same name will have the same position, but I don't think that really matters. --- indra/newview/lffloaterinvpanel.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index 8a1b739ce..1f2d46946 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -44,10 +44,7 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L setTitle(title); // Figure out a unique name for our rect control - const auto rect_control = llformat("FloaterInv%sRect", - cat.has("name") ? boost::algorithm::erase_all_copy(cat["name"].asStringRef(), " ").data() : - cat.has("id") ? cat["id"].asStringRef().data() : - cat["type"].asStringRef().data()); + const auto rect_control = llformat("FloaterInv%sRect", boost::algorithm::erase_all_copy(title, " ").data()); if (gSavedSettings.controlExists(rect_control)) // Set our initial rect to the stored control setRect(gSavedSettings.getRect(rect_control)); From 3d21dc309c1c9de90eb2dc2a33d2833c12fbad97 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 4 May 2019 03:40:08 -0400 Subject: [PATCH 57/91] Views fromXML can have sound_flags now --- indra/llui/llview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index c983d3a84..78602df94 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -2831,6 +2831,10 @@ void LLView::initFromXML(LLXMLNodePtr node, LLView* parent) node->getAttributeBOOL("mouse_opaque", mMouseOpaque); node->getAttributeS32("default_tab_group", mDefaultTabGroup); + if (node->hasAttribute("sound_flags")) + { + node->getAttributeU8("sound_flags", mSoundFlags); + } reshape(view_rect.getWidth(), view_rect.getHeight()); } From 2b47e62dd86099c51911afb8805a7d94fb6ae180 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 4 May 2019 03:51:12 -0400 Subject: [PATCH 58/91] Remove dumb dynamic cast children loop in toolbar init :%s/\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 7773f6b4f7a72d605060f9c55a153d9f8cf0790e Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 4 May 2019 07:12:40 -0400 Subject: [PATCH 59/91] Restore the old toggle behavior of the special inventory floaters --- indra/newview/lffloaterinvpanel.h | 7 +++++++ indra/newview/llviewermenu.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/indra/newview/lffloaterinvpanel.h b/indra/newview/lffloaterinvpanel.h index 51dac3369..724fed98d 100644 --- a/indra/newview/lffloaterinvpanel.h +++ b/indra/newview/lffloaterinvpanel.h @@ -33,6 +33,13 @@ class LFFloaterInvPanel : public LLFloater, public LLInstanceTrackerclose(); + else + show(cat); + } static void closeAll(); // Called when not allowed to have inventory open /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index e2b7e52cf..8361f1863 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -8938,7 +8938,7 @@ class SinguUrlAction : public view_listener_t void show_inv_floater(const LLSD& userdata, const std::string& field) { - LFFloaterInvPanel::show(LLSD().with(field, userdata)); + LFFloaterInvPanel::toggle(LLSD().with(field, userdata)); } static void visible_inv_floater(const LLSD& userdata, const std::string& field) From 623b993fa9c3fa49aa265cdb7cc45f4ec89cc2d1 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 5 May 2019 18:24:27 -0400 Subject: [PATCH 60/91] Fix the inventory floater resize bug Thanks for catching this, Deltek. --- indra/newview/lffloaterinvpanel.cpp | 30 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index 1f2d46946..daa9e9831 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -31,17 +31,9 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, LLInventoryModel* model) : LLInstanceTracker(cat) { - mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2)); - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml"); - LLPanel* panel = getChild("placeholder_panel"); - mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, cat, panel->getRect(), model ? model : &gInventory, true); - mPanel->postBuild(); - mPanel->setFollows(FOLLOWS_ALL); - mPanel->setEnabled(true); - addChild(mPanel); - removeChild(panel); - const auto& title = name.empty() ? mPanel->getRootFolder()->getName() : name; - setTitle(title); + // Setup the floater first + mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, cat, LLRect(), model ? model : &gInventory, true); + const auto& title = name.empty() ? gInventory.getCategory(mPanel->getRootFolderID())->getName() : name; // Figure out a unique name for our rect control const auto rect_control = llformat("FloaterInv%sRect", boost::algorithm::erase_all_copy(title, " ").data()); @@ -52,6 +44,22 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L gSavedSettings.declareRect(rect_control, getRect(), "Rectangle for " + title + " window"); setRectControl(rect_control); + + // Load from XUI + mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2)); + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml"); + + // Now set the title + setTitle(title); + + // Now take care of the children + LLPanel* panel = getChild("placeholder_panel"); + mPanel->setRect(panel->getRect()); + mPanel->postBuild(); + mPanel->setFollows(FOLLOWS_ALL); + mPanel->setEnabled(true); + addChild(mPanel); + removeChild(panel); } LFFloaterInvPanel::~LFFloaterInvPanel() From 1193f9bb656aaf6faf23abf2a2f78f99de77734d Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 5 May 2019 20:09:31 -0400 Subject: [PATCH 61/91] Fix Torric's no copy texture crash Thanks for the research and textures, Cheesy --- indra/newview/llselectmgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index fe25b88fb..a60f31f9a 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1599,7 +1599,7 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) struct f : public LLSelectedTEFunctor { - LLViewerInventoryItem* mItem; + LLPointer mItem; LLUUID mImageID; f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mImageID(id) {} bool apply(LLViewerObject* objectp, S32 te) override From 535a01ea306e4656645270cb8d932eba17e85c56 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Thu, 2 May 2019 09:08:06 -0500 Subject: [PATCH 62/91] SLVoice update Windows and Mac --- autobuild.xml | 24 +++++++++++++++++++----- indra/cmake/Copy3rdPartyLibs.cmake | 13 +++++++++++-- indra/newview/CMakeLists.txt | 17 ++++++++++++----- indra/newview/viewer_manifest.py | 12 +++++++++--- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 645dfce37..8df1b9a15 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2458,11 +2458,11 @@ archive hash - 68a8fab5ad3a180487598d3a3e0d57b1 + b1b0134bc55f55a7c1a04ee78d70c64d hash_algorithm md5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Darwin/installer/slvoice-4.6.0017.21209.298329-darwin-298329.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12438/73598/slvoice-4.9.0002.27586.511884-darwin64-511884.tar.bz2 name darwin @@ -2498,18 +2498,32 @@ archive hash - 5a78539626b5f23522d0b466247f48b4 + ec50c31efce74bdedee470b5388aeeec hash_algorithm md5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/21422/157291/slvoice-4.9.0002.30313.517593-windows-517593.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12434/73576/slvoice-4.9.0002.27586.511884-windows-511884.tar.bz2 name windows + windows64 + + archive + + hash + 4bf45d641bf5b2bd6b2cc39edcb01a6e + hash_algorithm + md5 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12433/73570/slvoice-4.9.0002.27586.511884-windows64-511884.tar.bz2 + + name + windows64 + version - 4.6.0017.22050 + 4.9.0002.27586.511884 tut diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index f541578be..fada12e96 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -22,10 +22,19 @@ if(WINDOWS) set(vivox_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}") set(vivox_files SLVoice.exe - vivoxsdk.dll - ortp.dll vivoxplatform.dll ) + if (ADDRESS_SIZE EQUAL 64) + list(APPEND vivox_files + vivoxsdk_x64.dll + ortp_x64.dll + ) + else (ADDRESS_SIZE EQUAL 64) + list(APPEND vivox_files + vivoxsdk.dll + ortp.dll + ) + endif (ADDRESS_SIZE EQUAL 64) #******************************* # Misc shared libs diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 423c1e686..9ef039900 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1508,12 +1508,7 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/SLVoice.exe - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxsdk.dll - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ortp.dll - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libsndfile-1.dll - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/zlib1.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxplatform.dll - ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxoal.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ca-bundle.crt ${GOOGLE_PERF_TOOLS_SOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/licenses-win32.txt @@ -1529,6 +1524,18 @@ if (WINDOWS) windows-crash-logger ) + if (ADDRESS_SIZE EQUAL 64) + list(APPEND COPY_INPUT_DEPENDENCIES + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxsdk_x64.dll + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ortp_x64.dll + ) + else (ADDRESS_SIZE EQUAL 64) + list(APPEND COPY_INPUT_DEPENDENCIES + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxsdk.dll + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ortp.dll + ) + endif (ADDRESS_SIZE EQUAL 64) + if (FMODSTUDIO) if (WORD_SIZE EQUAL 64) list(APPEND COPY_INPUT_DEPENDENCIES diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 356016d67..d2c34bae0 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -455,7 +455,7 @@ class WindowsManifest(ViewerManifest): "SLplugin.exe") # Get shared libs from the shared libs staging directory - with self.prefix(src=os.path.join(os.pardir, + 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. @@ -493,8 +493,14 @@ class WindowsManifest(ViewerManifest): # Vivox runtimes self.path("SLVoice.exe") - self.path("vivoxsdk.dll") - self.path("ortp.dll") + if (self.address_size == 64): + self.path("vivoxsdk_x64.dll") + self.path("ortp_x64.dll") + else: + self.path("vivoxsdk.dll") + self.path("ortp.dll") + self.path("libsndfile-1.dll") + self.path("vivoxoal.dll") # Security if(self.address_size == 64): From 2f9a43effdef221e00249cd1f8dc07bfce4868fa Mon Sep 17 00:00:00 2001 From: Router Gray Date: Thu, 2 May 2019 09:19:04 -0500 Subject: [PATCH 63/91] Sync with Alchemy to fix and streamline Windows plugins packaging --- indra/newview/viewer_manifest.py | 160 ++++++++++++++++--------------- 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index d2c34bae0..7053782ef 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -532,22 +532,26 @@ class WindowsManifest(ViewerManifest): self.path(src="licenses-win32.txt", dst="licenses.txt") self.path("featuretable.txt") - # Plugins - FilePicker - with self.prefix(src='../plugins/filepicker/%s' % self.args['configuration'], dst="llplugin"): - self.path("basic_plugin_filepicker.dll") + # Plugins + with self.prefix(dst="llplugin"): + with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'plugins')): - # Media plugins - LibVLC - with self.prefix(src='../plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): - self.path("media_plugin_libvlc.dll") + # Plugins - FilePicker + with self.prefix(src=os.path.join('filepicker', self.args['configuration'])): + self.path("basic_plugin_filepicker.dll") - # Media plugins - CEF - with self.prefix(src='../plugins/cef/%s' % self.args['configuration'], dst="llplugin"): - 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") - # 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"): + # Media plugins - CEF + with self.prefix(src=os.path.join('cef', self.args['configuration'])): + self.path("media_plugin_cef.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") @@ -559,73 +563,73 @@ class WindowsManifest(ViewerManifest): self.path("snapshot_blob.bin") self.path("v8_context_snapshot.bin") - with self.prefix(src=os.path.join(pkgdir, 'bin', config, 'swiftshader'), dst=os.path.join("llplugin", 'swiftshader')): - self.path("libEGL.dll") - self.path("libGLESv2.dll") + with self.prefix(src=os.path.join(pkgdir, 'bin', config, 'swiftshader'), dst='swiftshader'): + self.path("libEGL.dll") + self.path("libGLESv2.dll") - # CEF files common to all configurations - with self.prefix(src=os.path.join(pkgdir, 'resources'), dst="llplugin"): - self.path("cef.pak") - self.path("cef_100_percent.pak") - self.path("cef_200_percent.pak") - self.path("cef_extensions.pak") - self.path("devtools_resources.pak") - self.path("icudtl.dat") + # CEF files common to all configurations + with self.prefix(src=os.path.join(pkgdir, 'resources')): + self.path("cef.pak") + self.path("cef_100_percent.pak") + self.path("cef_200_percent.pak") + self.path("cef_extensions.pak") + self.path("devtools_resources.pak") + self.path("icudtl.dat") - with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('llplugin', 'locales')): - self.path("am.pak") - self.path("ar.pak") - self.path("bg.pak") - self.path("bn.pak") - self.path("ca.pak") - self.path("cs.pak") - self.path("da.pak") - self.path("de.pak") - self.path("el.pak") - self.path("en-GB.pak") - self.path("en-US.pak") - self.path("es-419.pak") - self.path("es.pak") - self.path("et.pak") - self.path("fa.pak") - self.path("fi.pak") - self.path("fil.pak") - self.path("fr.pak") - self.path("gu.pak") - self.path("he.pak") - self.path("hi.pak") - self.path("hr.pak") - self.path("hu.pak") - self.path("id.pak") - self.path("it.pak") - self.path("ja.pak") - self.path("kn.pak") - self.path("ko.pak") - self.path("lt.pak") - self.path("lv.pak") - self.path("ml.pak") - self.path("mr.pak") - self.path("ms.pak") - self.path("nb.pak") - self.path("nl.pak") - self.path("pl.pak") - self.path("pt-BR.pak") - self.path("pt-PT.pak") - self.path("ro.pak") - self.path("ru.pak") - self.path("sk.pak") - self.path("sl.pak") - self.path("sr.pak") - self.path("sv.pak") - self.path("sw.pak") - self.path("ta.pak") - self.path("te.pak") - self.path("th.pak") - self.path("tr.pak") - self.path("uk.pak") - self.path("vi.pak") - self.path("zh-CN.pak") - self.path("zh-TW.pak") + with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst='locales'): + self.path("am.pak") + self.path("ar.pak") + self.path("bg.pak") + self.path("bn.pak") + self.path("ca.pak") + self.path("cs.pak") + self.path("da.pak") + self.path("de.pak") + self.path("el.pak") + self.path("en-GB.pak") + self.path("en-US.pak") + self.path("es-419.pak") + self.path("es.pak") + self.path("et.pak") + self.path("fa.pak") + self.path("fi.pak") + self.path("fil.pak") + self.path("fr.pak") + self.path("gu.pak") + self.path("he.pak") + self.path("hi.pak") + self.path("hr.pak") + self.path("hu.pak") + self.path("id.pak") + self.path("it.pak") + self.path("ja.pak") + self.path("kn.pak") + self.path("ko.pak") + self.path("lt.pak") + self.path("lv.pak") + self.path("ml.pak") + self.path("mr.pak") + self.path("ms.pak") + self.path("nb.pak") + self.path("nl.pak") + self.path("pl.pak") + self.path("pt-BR.pak") + self.path("pt-PT.pak") + self.path("ro.pak") + self.path("ru.pak") + self.path("sk.pak") + self.path("sl.pak") + self.path("sr.pak") + self.path("sv.pak") + self.path("sw.pak") + self.path("ta.pak") + self.path("te.pak") + self.path("th.pak") + self.path("tr.pak") + self.path("uk.pak") + self.path("vi.pak") + self.path("zh-CN.pak") + self.path("zh-TW.pak") with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): self.path("libvlc.dll") From 9bcd3298bad6d007b7f529276a38bd631ce940ba Mon Sep 17 00:00:00 2001 From: Router Gray Date: Sat, 4 May 2019 07:28:50 -0500 Subject: [PATCH 64/91] Use dictionaries prebuilt instead of in-tree; adds several languages. --- indra/cmake/Hunspell.cmake | 1 + indra/newview/viewer_manifest.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/cmake/Hunspell.cmake b/indra/cmake/Hunspell.cmake index 25b06092b..83f9bc2fd 100644 --- a/indra/cmake/Hunspell.cmake +++ b/indra/cmake/Hunspell.cmake @@ -12,4 +12,5 @@ else (STANDALONE) set(HUNSPELL_LIBRARY libhunspell) endif (LINUX OR DARWIN) set(HUNSPELL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/hunspell) + use_prebuilt_binary(dictionaries) endif (STANDALONE) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 7053782ef..4a8698189 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -95,7 +95,8 @@ class ViewerManifest(LLManifest): # ... and the included spell checking dictionaries pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') - self.path("dictionaries") + with self.prefix(src=pkgdir): + self.path("dictionaries") # include the extracted packages information (see BuildPackagesInfo.cmake) self.path(src=os.path.join(self.args['build'],"packages-info.txt"), dst="packages-info.txt") From 2075d92369ed5eac73ff7dad6a22d10d2ae0ed1e Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 7 May 2019 00:33:39 -0400 Subject: [PATCH 65/91] Fix crash when SinguReplaceLinks is false reported by Beware --- indra/llui/lltexteditor.cpp | 2 +- indra/llui/llurlentry.cpp | 31 ++++++++++++++++++++----------- indra/llui/llurlentry.h | 2 +- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index f366cb2df..843fff605 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4317,7 +4317,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s if (always_underline) link_style->mUnderline = true; appendAndHighlightText(link, part, link_style, !always_underline/*match.underlineOnHoverOnly()*/); }; - const auto&& cb = force_replace_links ? boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3) : LLUrlLabelCallback::slot_function_type(); + const auto&& cb = force_replace_links ? boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3) : LLUrlLabelCallback(); while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, cb)) { start = match.getStart(); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index d3e9ffee4..81be0e2de 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -673,9 +673,12 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } else { - auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); - mAvatarNameCacheConnections.emplace(agent_id, connection); - addObserver(agent_id_string, url, cb); + if (cb) + { + auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections.emplace(agent_id, connection); + addObserver(agent_id_string, url, cb); + } return LLTrans::getString("LoadingData"); } } @@ -785,9 +788,12 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } else { - auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); - mAvatarNameCacheConnections.emplace(agent_id, connection); - addObserver(agent_id_string, url, cb); + if (cb) + { + auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections.emplace(agent_id, connection); + addObserver(agent_id_string, url, cb); + } return LLTrans::getString("LoadingData"); } } @@ -921,10 +927,13 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa } else { - gCacheName->getGroup(group_id, - boost::bind(&LLUrlEntryGroup::onGroupNameReceived, - this, _1, _2, _3)); - addObserver(group_id_string, url, cb); + if (cb) + { + gCacheName->getGroup(group_id, + boost::bind(&LLUrlEntryGroup::onGroupNameReceived, + this, _1, _2, _3)); + addObserver(group_id_string, url, cb); + } return LLTrans::getString("LoadingData"); } } @@ -1028,7 +1037,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id // Add an observer to call LLUrlLabelCallback when we have parcel name. - addObserver(parcel_id_string, url, cb); + if (cb) addObserver(parcel_id_string, url, cb); LLUUID parcel_id(parcel_id_string); diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index d526ea957..01f1fb1ae 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -42,7 +42,7 @@ class LLAvatarName; typedef boost::signals2::signal LLUrlLabelSignal; -typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback; +typedef LLUrlLabelSignal::slot_function_type LLUrlLabelCallback; /// /// LLUrlEntryBase is the base class of all Url types registered in the From 93af7491ee7ba3ba47987fca46a427243522d2f3 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Tue, 7 May 2019 01:07:48 -0400 Subject: [PATCH 66/91] Attempt at fixing beware's selection lost bug once and for all --- indra/llui/lltexteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 843fff605..33d710cb0 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4627,7 +4627,7 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h } endOfDoc(); } - else if( selection_start != selection_end ) + else if (was_selecting || selection_start != selection_end) { mSelectionStart = selection_start; mSelectionEnd = selection_end; From e9c99089e120925847aa437bfeb4f746249d0a54 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 10 May 2019 14:04:32 -0400 Subject: [PATCH 67/91] No viewer_manifest warnings, please. --- indra/newview/viewer_manifest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 4a8698189..ffc4e35f3 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -500,8 +500,8 @@ class WindowsManifest(ViewerManifest): else: self.path("vivoxsdk.dll") self.path("ortp.dll") - self.path("libsndfile-1.dll") - self.path("vivoxoal.dll") + #self.path("libsndfile-1.dll") + #self.path("vivoxoal.dll") # Security if(self.address_size == 64): From 2a40ca629543d118513e8ea00ef0f4efe0d06124 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 10 May 2019 16:45:20 -0400 Subject: [PATCH 68/91] LLTextEditor's setText and setWText shouldn't always enforce linking --- indra/llui/lltexteditor.cpp | 8 ++++---- indra/llui/lltexteditor.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 33d710cb0..03bafce7d 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -741,7 +741,7 @@ LLMenuGL* LLTextEditor::createUrlContextMenu(S32 x, S32 y, const std::string &in return menu; } -void LLTextEditor::setText(const LLStringExplicit &utf8str) +void LLTextEditor::setText(const LLStringExplicit &utf8str, bool force_replace_links) { // clear out the existing text and segments mWText.clear(); @@ -756,7 +756,7 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str) //LLStringUtil::removeCRLF(text); // appendText modifies mCursorPos... - appendText(utf8str, false, false); + appendText(utf8str, false, false, nullptr, force_replace_links); // ...so move cursor to top after appending text setCursorPos(0); @@ -766,9 +766,9 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str) mTextIsUpToDate = true; } -void LLTextEditor::setWText(const LLWString& text) +void LLTextEditor::setWText(const LLWString& text, bool force_replace_links) { - setText(wstring_to_utf8str(text)); + setText(wstring_to_utf8str(text), force_replace_links); } // virtual diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 0d92803b4..c8a67fdbd 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -276,8 +276,8 @@ public: LLMenuGL* createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url // Non-undoable - void setText(const LLStringExplicit &utf8str); - void setWText(const LLWString &wtext); + void setText(const LLStringExplicit &utf8str, bool force_replace_links = true); + void setWText(const LLWString &wtext, bool force_replace_links = true); // Returns byte length limit S32 getMaxLength() const { return mMaxTextByteLength; } From 4bb63a98270439ebd6765d8fa321672179102627 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 10 May 2019 18:47:06 -0400 Subject: [PATCH 69/91] Fix SinguReplaceLinks not being respected on various user driven UI Corrects display for Readonly Notecards, Picks, Group profiles, First/ Second Life, Classifieds, Events, Landmarks, About Land, and Region/Estate Thanks for giving me the heads-up, Beware --- indra/newview/llfloaterland.cpp | 4 ++-- indra/newview/llfloaterregioninfo.cpp | 4 ++-- indra/newview/llpanelavatar.cpp | 4 ++-- indra/newview/llpanelclassified.cpp | 2 +- indra/newview/llpanelevent.cpp | 2 +- indra/newview/llpanelgroupgeneral.cpp | 2 +- indra/newview/llpanelgroupnotices.cpp | 7 +------ indra/newview/llpanelgroupvoting.cpp | 4 ++-- indra/newview/llpanelpick.cpp | 2 +- indra/newview/llpanelplace.cpp | 4 ++-- indra/newview/llpreviewnotecard.cpp | 2 +- 11 files changed, 16 insertions(+), 21 deletions(-) diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 9f5aa1b5f..064fee823 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -715,7 +715,7 @@ void LLPanelLandGeneral::refresh() ); mEditName->setText( parcel->getName() ); - mEditDesc->setText( parcel->getDesc() ); + mEditDesc->setText( parcel->getDesc(), false ); BOOL for_sale = parcel->getForSale(); @@ -3070,7 +3070,7 @@ void LLPanelLandCovenant::updateCovenantText(const std::string &string) { LLViewerTextEditor* editor = self->getChild("covenant_editor"); editor->setHandleEditKeysDirectly(TRUE); - editor->setText(string); + editor->setText(string, false); } } diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 119910950..9b24c620f 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -2776,7 +2776,7 @@ void LLPanelEstateCovenant::updateCovenantText(const std::string& string, const LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant(); if( panelp ) { - panelp->mEditor->setText(string); + panelp->mEditor->setText(string, false); panelp->setCovenantID(asset_id); } } @@ -2823,7 +2823,7 @@ void LLPanelEstateCovenant::setOwnerName(const std::string& name) void LLPanelEstateCovenant::setCovenantTextEditor(const std::string& text) { - mEditor->setText(text); + mEditor->setText(text, false); } // key = "estateupdateinfo" diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 98a3621df..5871d47bd 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -290,7 +290,7 @@ void LLPanelAvatarFirstLife::processProperties(void* data, EAvatarProcessorType if (pAvatarData && (mAvatarID == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null)) { // Teens don't get these - getChildView("about")->setValue(pAvatarData->fl_about_text); + getChild("about")->setText(pAvatarData->fl_about_text, false); getChild("img")->setImageAssetID(pAvatarData->fl_image_id); } } @@ -1493,7 +1493,7 @@ void LLPanelAvatar::processProperties(void* data, EAvatarProcessorType type) timeStructToFormattedString(&t, gSavedSettings.getString("ShortDateFormat"), born_on); }*/ setOnlineStatus(pAvatarData->flags & AVATAR_ONLINE ? ONLINE_STATUS_YES : ONLINE_STATUS_NO); - childSetValue("about", pAvatarData->about_text); + getChild("about")->setText(pAvatarData->about_text, false); } } else if (type == APT_NOTES) diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 8aec5a381..786088cc0 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -354,7 +354,7 @@ void LLPanelClassifiedInfo::processProperties(void* data, EAvatarProcessorType t // Update UI controls mNameEditor->setText(c_info->name); - mDescEditor->setText(c_info->description); + mDescEditor->setText(c_info->description, false); mSnapshotCtrl->setImageAssetID(c_info->snapshot_id); mLocationEditor->setText(location_text); mLocationChanged = false; diff --git a/indra/newview/llpanelevent.cpp b/indra/newview/llpanelevent.cpp index 21efbafd9..66644c803 100644 --- a/indra/newview/llpanelevent.cpp +++ b/indra/newview/llpanelevent.cpp @@ -151,7 +151,7 @@ void LLPanelEvent::processEventInfoReply(LLMessageSystem *msg, void **) self->mTBName->setText(self->mEventInfo.mName); self->mTBCategory->setText(self->mEventInfo.mCategoryStr); self->mTBDate->setText(self->mEventInfo.mTimeStr); - self->mTBDesc->setText(self->mEventInfo.mDesc); + self->mTBDesc->setText(self->mEventInfo.mDesc, false); self->mTBDuration->setText(llformat("%d:%.2d", self->mEventInfo.mDuration / 60, self->mEventInfo.mDuration % 60)); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index c09f96dcf..192177cc1 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -761,7 +761,7 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) if (mEditCharter) { - mEditCharter->setText(gdatap->mCharter); + mEditCharter->setText(gdatap->mCharter, false); mEditCharter->resetDirty(); } } diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 0498e7e49..862a89dd4 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -529,12 +529,7 @@ void LLPanelGroupNotices::showNotice(const std::string& subject, arrangeNoticeView(VIEW_PAST_NOTICE); if(mViewSubject) mViewSubject->setText(subject); - if (mViewMessage) { - // We need to prune the highlights, and clear() is not doing it... - mViewMessage->removeTextFromEnd(mViewMessage->getMaxLength()); - // Now we append the new text (setText() won't highlight URLs) - mViewMessage->appendColoredText(message, false, false, mViewMessage->getReadOnlyFgColor()); - } + if (mViewMessage) mViewMessage->setText(message, false); if (mInventoryOffer) { diff --git a/indra/newview/llpanelgroupvoting.cpp b/indra/newview/llpanelgroupvoting.cpp index 09a28efea..b58f15721 100644 --- a/indra/newview/llpanelgroupvoting.cpp +++ b/indra/newview/llpanelgroupvoting.cpp @@ -307,7 +307,7 @@ void LLPanelGroupVoting::impl::setEnableVoteProposal() if ( proposal_cell ) { //proposal text - mProposalText->setText(proposal_cell->getValue().asString()); + mProposalText->setText(proposal_cell->getValue().asString(), false); } else { // Something's wrong... should have some text @@ -556,7 +556,7 @@ void LLPanelGroupVoting::impl::setEnableHistoryItem() const LLScrollListCell *cell = item->getColumn(5); if (cell) { - mVoteHistoryText->setText(cell->getValue().asString()); + mVoteHistoryText->setText(cell->getValue().asString(), false); } else { // Something's wrong... diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 899210edb..ba1384797 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -199,7 +199,7 @@ void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) // Update UI controls mNameEditor->setText(pick_info->name); - mDescEditor->setText(pick_info->desc); + mDescEditor->setText(pick_info->desc, false); mSnapshotCtrl->setImageAssetID(pick_info->snapshot_id); mLocationEditor->setText(location_text); } diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp index 9dc433463..00f7bc4d1 100644 --- a/indra/newview/llpanelplace.cpp +++ b/indra/newview/llpanelplace.cpp @@ -125,7 +125,7 @@ BOOL LLPanelPlace::postBuild() void LLPanelPlace::displayItemInfo(const LLInventoryItem* pItem) { mNameEditor->setText(pItem->getName()); - mDescEditor->setText(pItem->getDescription()); + mDescEditor->setText(pItem->getDescription(), false); } // Use this for search directory clicks, because we are totally @@ -248,7 +248,7 @@ void LLPanelPlace::processParcelInfo(const LLParcelData& parcel_data) if( !parcel_data.desc.empty() && mDescEditor && mDescEditor->getText().empty()) { - mDescEditor->setText(parcel_data.desc); + mDescEditor->setText(parcel_data.desc, false); } std::string info_text; diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 1bfa37c84..2f7415bdd 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -405,7 +405,7 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, else { // Version 0 (just text, doesn't include version number) - previewEditor->setText(LLStringExplicit(buffer)); + previewEditor->setText(LLStringExplicit(buffer), false); } previewEditor->makePristine(); From 6a7695a8f0e8877fd57d22626b885466e844cdb1 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 11 May 2019 22:40:17 -0400 Subject: [PATCH 70/91] Refactor appendAndHighlightTextImpl's Highlight impl and fix selection --- indra/llui/lltexteditor.cpp | 42 +++++++++++++------------------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 03bafce7d..f58c334e5 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4552,39 +4552,27 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h // This is where we appendHighlightedText // If LindenUserDir is empty then we didn't login yet. // In that case we can't instantiate LLTextParser, which is initialized per user. - if (mParseHighlights && !gDirUtilp->getLindenUserDir(true).empty()) + LLTextParser* highlight = mParseHighlights && stylep && !gDirUtilp->getLindenUserDir(true).empty() ? LLTextParser::getInstance() : nullptr; + if (highlight) { - LLTextParser* highlight = LLTextParser::getInstance(); - - if (highlight && stylep) + const LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), (LLTextParser::EHighlightPosition)highlight_part); + auto cur_length = getLength(); + for (auto i = pieces.beginArray(), end = pieces.endArray(); i < end; ++i) { - LLStyleSP highlight_params(new LLStyle(*stylep)); - LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params->getColor(), (LLTextParser::EHighlightPosition)highlight_part); - for (S32 i=0;isetColor(lcolor); + const auto& piece = *i; + LLWString wide_text = utf8str_to_wstring(piece["text"].asString()); - LLWString wide_text; - wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); - - S32 cur_length = getLength(); - insertStringNoUndo(cur_length, wide_text); - LLStyleSP sp(new LLStyle(*highlight_params)); - LLTextSegmentPtr segmentp; - segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_text.size()); - if (underline_on_hover) segmentp->setUnderlineOnHover(true); - mSegments.push_back(segmentp); - } - return; + insertStringNoUndo(cur_length, wide_text); + LLStyleSP sp(new LLStyle(*stylep)); + sp->setColor(piece["color"]); + LLTextSegmentPtr segmentp = new LLTextSegment(sp, cur_length, cur_length += wide_text.size()); + if (underline_on_hover) segmentp->setUnderlineOnHover(true); + mSegments.push_back(segmentp); } } - //else + else { - LLWString wide_text; - wide_text = utf8str_to_wstring(new_text); + LLWString wide_text = utf8str_to_wstring(new_text); auto length = getLength(); auto insert_len = length + insertStringNoUndo(length, utf8str_to_wstring(new_text)); From bf176ffd4dccb48e799163f8fad9f6a3f78e003c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 11 May 2019 22:49:56 -0400 Subject: [PATCH 71/91] Prevent the texteditor from parsing highlights when it shouldn't --- indra/llui/lltexteditor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index f58c334e5..b9a3b73ca 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -293,6 +293,8 @@ LLTextEditor::LLTextEditor( mMouseDownY(0), mLastSelectionX(-1), mLastSelectionY(-1), + mParseHTML(parse_html), + mParseHighlights(false), mLastContextMenuX(-1), mLastContextMenuY(-1), mReflowNeeded(FALSE), @@ -343,7 +345,6 @@ LLTextEditor::LLTextEditor( mBorder = new LLViewBorder(std::string("text ed border"), LLRect(0, getRect().getHeight(), getRect().getWidth(), 0), LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, UI_TEXTEDITOR_BORDER); addChild(mBorder); - mParseHTML = parse_html; appendText(default_text, FALSE, FALSE); resetDirty(); // Update saved text state From 55c0ba5b060569f71a58e417cb463220e2177048 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 05:57:15 -0500 Subject: [PATCH 72/91] Silence compiler warning destructor on non-final w/virtual functions & non-virtual destructor --- indra/libhacd/hacdMicroAllocator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/libhacd/hacdMicroAllocator.cpp b/indra/libhacd/hacdMicroAllocator.cpp index 47562f6a4..664b5e8c6 100644 --- a/indra/libhacd/hacdMicroAllocator.cpp +++ b/indra/libhacd/hacdMicroAllocator.cpp @@ -436,7 +436,7 @@ public: } - ~MyMicroAllocator(void) + virtual ~MyMicroAllocator() { if ( mMicroChunks ) { @@ -746,7 +746,7 @@ public: mMicro = createMicroAllocator(this,defaultChunkSize); } - ~MyHeapManager(void) + virtual ~MyHeapManager() { releaseMicroAllocator(mMicro); } From 58d94e988365803543154bc78ca64acb53be0767 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 06:17:39 -0500 Subject: [PATCH 73/91] Change some instances of {0} to {} to pacify compilers. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 2 +- indra/llaudio/llstreamingaudio_fmodstudio.cpp | 4 ++-- indra/newview/llviewertexture.cpp | 2 +- indra/newview/rlvhandler.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 182e4a942..823f51cbf 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -902,7 +902,7 @@ bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename) } FMOD_MODE base_mode = FMOD_LOOP_NORMAL; - FMOD_CREATESOUNDEXINFO exinfo = {0}; + FMOD_CREATESOUNDEXINFO exinfo = { }; exinfo.cbsize = sizeof(exinfo); exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading. // Load up the wav file into an fmod sample diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index 470eb8828..d7c6b48b7 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -143,7 +143,7 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : Check_FMOD_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup"); - FMOD_DSP_DESCRIPTION dspdesc = {0}; + FMOD_DSP_DESCRIPTION dspdesc = { }; dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name)); dspdesc.numoutputbuffers = 1; @@ -632,7 +632,7 @@ FMOD_RESULT LLAudioStreamManagerFMODSTUDIO::getOpenState(FMOD_OPENSTATE& state, void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) { Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); - FMOD_ADVANCEDSETTINGS settings = {0}; + FMOD_ADVANCEDSETTINGS settings = { }; settings.cbSize=sizeof(settings); settings.defaultDecodeBufferSize = decodebuffertime;//ms Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings"); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 3e5c98e7f..97e6a8f55 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -3073,7 +3073,7 @@ LLDate LLViewerFetchedTexture::getUploadTime() { if (mComment.find('z') != mComment.end()) { - struct tm t = {0}; + struct tm t = {}; sscanf(mComment['z'].c_str(), "%4d%2d%2d%2d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec); std::string iso_date = llformat("%d-%d-%dT%d:%d:%dZ", t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index c89c1782d..09b7ab37c 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -2237,7 +2237,7 @@ ERlvCmdRet RlvHandler::onGetInvWorn(const RlvCommand& rlvCmd, std::string& strRe RlvWearableItemCollector f(pFolder, RlvForceWear::ACTION_WEAR_REPLACE, RlvForceWear::FLAG_MATCHALL); gInventory.collectDescendentsIf(pFolder->getUUID(), folders, items, FALSE, f, true); - rlv_wear_info wi = {0}; + rlv_wear_info wi = {}; // Add all the folders to a lookup map std::map mapFolders; From 3951f6f5166329b3e64118c6747a6bf2e590dcd1 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 06:18:52 -0500 Subject: [PATCH 74/91] Fix no newline at EOF. --- indra/llrender/llgltexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index 590fe9d17..3e8638595 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -393,4 +393,4 @@ void LLGLTexture::setTexelsPerImage() S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT); S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT); mTexelsPerImage = (F32)fullwidth * fullheight; -} \ No newline at end of file +} From 40ad9fad32efdaeaef5cb500338466c872a21e52 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 06:20:14 -0500 Subject: [PATCH 75/91] Fix fonts search path on not-Windows. --- indra/newview/llviewerwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 101200c8d..d505395c2 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1644,9 +1644,9 @@ static const std::string font_dir() { return gDirUtilp->getExecutableDir() #if LL_DARWIN - + "../Resources/" + + "/../Resources" #elif !defined(LL_WINDOWS) - + "../" + + "/.." #endif ; } From bf9e900731650078f05ce515443787d6a71cd2ea Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 06:21:25 -0500 Subject: [PATCH 76/91] Fix warning about variable promotion. --- indra/newview/llpaneleditwearable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index e68b9ae26..d5f2c7a29 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -137,7 +137,7 @@ public: const std::string &title, U8 num_color_swatches, // number of 'color_swatches' U8 num_texture_pickers, // number of 'texture_pickers' - U8 num_subparts, ... ); // number of subparts followed by a list of ETextureIndex and ESubparts + unsigned int num_subparts, ... ); // number of subparts followed by a list of ETextureIndex and ESubparts const LLWearableType::EType mWearableType; @@ -252,7 +252,7 @@ LLEditWearableDictionary::WearableEntry::WearableEntry(LLWearableType::EType typ const std::string &title, U8 num_color_swatches, U8 num_texture_pickers, - U8 num_subparts, ... ) : + unsigned int num_subparts, ... ) : LLDictionaryEntry(title), mWearableType(type) { From 4329d46ac4a774026c6d4db1fe3b925865f8e7e0 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 06:34:03 -0500 Subject: [PATCH 77/91] Properly comment out code so compilers quit whining about 'unreachable'. --- indra/newview/llfloaterbulkpermission.cpp | 3 ++- indra/newview/llpreviewgesture.cpp | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp index 7fb149ee5..0fcdc7071 100644 --- a/indra/newview/llfloaterbulkpermission.cpp +++ b/indra/newview/llfloaterbulkpermission.cpp @@ -342,12 +342,13 @@ void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInve //status_text.setArg("[STATUS]", getString("status_ok_text")); status_text.setArg("[STATUS]", ""); } +#if 0 else { //status_text.setArg("[STATUS]", getString("status_bad_text")); status_text.setArg("[STATUS]", ""); } - +#endif list->addSimpleElement(status_text.getString()); //TODO if we are an object inside an object we should check a recuse flag and if set diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index eb0bece56..aac1fa4d7 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -1050,12 +1050,9 @@ void LLPreviewGesture::saveIfNeeded() LLDataPackerAsciiBuffer dp(buffer, max_size); - BOOL ok = gesture->serialize(dp); - - // - //if (dp.getCurrentSize() > 1000) - if(0) - // + bool ok = gesture->serialize(dp); +#if 0 // + if (dp.getCurrentSize() > 1000) { LLNotificationsUtil::add("GestureSaveFailedTooManySteps"); @@ -1063,6 +1060,8 @@ void LLPreviewGesture::saveIfNeeded() gesture = nullptr; } else if (!ok) +#endif // + if (!ok) { LLNotificationsUtil::add("GestureSaveFailedTryAgain"); delete gesture; From df13aa408b06ed7e42a1fd8c5adfba86ddca9a68 Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 10:09:00 -0500 Subject: [PATCH 78/91] Add missing operand in inventory actions. --- indra/newview/llinventorybridge.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 2f527e134..61b16c729 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3142,7 +3142,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) } return; } - else if ("marketplace_copy_id") + else if ("marketplace_copy_id" == action) { auto id = LLMarketplaceData::instance().getListingID(mUUID); gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(std::to_string(id))); From 562982cc30984ea24db34f6691397f69455fd67f Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 10:55:33 -0500 Subject: [PATCH 79/91] Null check, sooner is better, and clang is happy. Thanks Liru. --- indra/newview/llface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 50eb3a7d8..8cde987a9 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -977,12 +977,12 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po { const LLMatrix4a& vol_mat = getWorldMatrix(); const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset); - const LLVector4a& normal = vf.mNormals[0]; - const LLVector4a& tangent = vf.mTangents[0]; - if (!&tangent) + if (!vf.mTangents) { return; } + const LLVector4a& normal = vf.mNormals[0]; + const LLVector4a& tangent = vf.mTangents[0]; LLVector4a binormal; binormal.setCross3(normal, tangent); From f6d85732210dc0fb7ceb59112fc9e2e322aa90ee Mon Sep 17 00:00:00 2001 From: Router Gray Date: Fri, 24 May 2019 10:57:33 -0500 Subject: [PATCH 80/91] "Liru: All getters should be const, ideally." Clang agrees. The values being compared are const, the result should be too. --- indra/newview/lltextureview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 47e473643..c2f924bc0 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -829,7 +829,7 @@ LLTextureView::~LLTextureView() typedef std::pair decode_pair_t; struct compare_decode_pair { - bool operator()(const decode_pair_t& a, const decode_pair_t& b) + bool operator()(const decode_pair_t& a, const decode_pair_t& b) const { return a.first > b.first; } From 2e5141bd25d7a6e41f28275c52e51c224c61d276 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 27 May 2019 06:25:29 -0400 Subject: [PATCH 81/91] Possible fix for Torric's gesture toggle crash --- indra/newview/llgesturemgr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index ae5bded83..d55c22b5e 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -1207,6 +1207,8 @@ void LLGestureMgr::stopGesture(LLMultiGesture* gesture) { gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP); } + gesture->mRequestedAnimIDs.clear(); + for (const auto& anim_id : gesture->mPlayingAnimIDs) { if (gesture->mLocal) @@ -1214,6 +1216,7 @@ void LLGestureMgr::stopGesture(LLMultiGesture* gesture) else gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP); } + gesture->mPlayingAnimIDs.clear(); mPlaying.erase(std::remove(mPlaying.begin(), mPlaying.end(), gesture), mPlaying.end()); From 1cd7706461b73711b78edd61119b9f3ac19469ea Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 31 May 2019 18:22:56 -0400 Subject: [PATCH 82/91] Actually updaate the binary we put into the installer --- indra/newview/viewer_manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index ffc4e35f3..f01357bee 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -441,7 +441,7 @@ class WindowsManifest(ViewerManifest): if True: #self.is_packaging_viewer(): # Find singularity-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. - self.path(src='%s/%s-bin.exe' % (self.args['configuration'],self.viewer_branding_id()), dst=self.final_exe()) + self.path(src='%s\\%s-bin.exe' % (self.args['configuration'],self.viewer_branding_id()), dst=self.final_exe()) with self.prefix(src=os.path.join(pkgdir, "redist")): # include the compiled launcher scripts so that it gets included in the file_list From 68fc758e943b1c25d983a5d1c70f6c740d4785bf Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 31 May 2019 19:35:22 -0400 Subject: [PATCH 83/91] Do not make installer package unless binary is updated (or touched) This is bad for production, adding a touch binary line to the prebuild step should fix it though --- indra/newview/viewer_manifest.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f01357bee..0ca559b29 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -722,6 +722,13 @@ class WindowsManifest(ViewerManifest): installer_file = self.installer_base_name() + '_Setup.exe' substitution_strings['installer_file'] = installer_file + # Packaging the installer takes forever, dodge it if we can. + binary_mod = os.path.getmtime(os.path.join(self.args['configuration'], self.final_exe())) + installer_mod = os.path.getmtime(os.path.join(self.args['configuration'], installer_file)) + if binary_mod <= installer_mod: + print("Binary is unchanged since last package, touch the binary to trigger repackage.") + exit(); + version_vars = """ !define INSTEXE "%(final_exe)s" !define VERSION "%(version_short)s" From c2f56f18811283dbe700b287eb1c5a22ea3ceab6 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 31 May 2019 22:57:50 -0400 Subject: [PATCH 84/91] Woops, now inventory floaters remember their position per folder for real! --- indra/newview/lffloaterinvpanel.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index daa9e9831..be530d1e2 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -38,12 +38,9 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L // Figure out a unique name for our rect control const auto rect_control = llformat("FloaterInv%sRect", boost::algorithm::erase_all_copy(title, " ").data()); - if (gSavedSettings.controlExists(rect_control)) // Set our initial rect to the stored control - setRect(gSavedSettings.getRect(rect_control)); - else // Or create the rect control if it doesn't exist - gSavedSettings.declareRect(rect_control, getRect(), "Rectangle for " + title + " window"); - - setRectControl(rect_control); + bool existed = gSavedSettings.controlExists(rect_control); + if (existed) // Set our initial rect to the stored control + setRectControl(rect_control); // Load from XUI mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2)); @@ -52,6 +49,18 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L // Now set the title setTitle(title); + // If we haven't existed before, create and set our rect control now + if (!existed) + { + S32 left, top; + gFloaterView->getNewFloaterPosition(&left, &top); + LLRect rect = getRect(); + rect.translate(left - rect.mLeft, top - rect.mTop); + setRect(rect); + gSavedSettings.declareRect(rect_control, rect, "Rectangle for " + title + " window"); + setRectControl(rect_control); + } + // Now take care of the children LLPanel* panel = getChild("placeholder_panel"); mPanel->setRect(panel->getRect()); From 82386fe2ef35c75fffa4535d216453fe9705e72d Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 10 Jun 2019 09:15:35 -0400 Subject: [PATCH 85/91] Negative TeleportHistoryMaxEntries means unlimited now --- indra/newview/llfloaterteleporthistory.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/indra/newview/llfloaterteleporthistory.cpp b/indra/newview/llfloaterteleporthistory.cpp index ceac57f0f..ec7cb9ce4 100644 --- a/indra/newview/llfloaterteleporthistory.cpp +++ b/indra/newview/llfloaterteleporthistory.cpp @@ -157,7 +157,12 @@ void LLFloaterTeleportHistory::addEntry(std::string parcelName) // add the new list entry on top of the list, deselect all and disable the buttons const S32 max_entries = gSavedSettings.getS32("TeleportHistoryMaxEntries"); S32 num_entries = mPlacesList->getItemCount(); - while(num_entries >= max_entries) + if (max_entries <= 0) + { + if (!max_entries) + mPlaces->clearRows(); + } + else while(num_entries >= max_entries) { mPlacesList->deleteItems(LLSD(mID - num_entries--)); } @@ -212,7 +217,7 @@ void LLFloaterTeleportHistory::loadFile(const std::string &file_name) else { const S32 max_entries = gSavedSettings.getS32("TeleportHistoryMaxEntries"); - const S32 num_entries = llmin(max_entries,(const S32)data.size()); + const S32 num_entries = llmin(max_entries < 0 ? S32_MAX : max_entries,(const S32)data.size()); pScrollList->clear(); for(S32 i = 0; i < num_entries; i++) //Lower entry = newer { From 7dea17cdc7368d8c005cd80a7402cbd8b5d9136d Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 13 Jun 2019 18:27:43 -0400 Subject: [PATCH 86/91] Fix Xitano's bug by fixing my viewer_manifest bootstrap paradox --- indra/newview/viewer_manifest.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0ca559b29..6c8c8a888 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -723,11 +723,13 @@ class WindowsManifest(ViewerManifest): substitution_strings['installer_file'] = installer_file # Packaging the installer takes forever, dodge it if we can. - binary_mod = os.path.getmtime(os.path.join(self.args['configuration'], self.final_exe())) - installer_mod = os.path.getmtime(os.path.join(self.args['configuration'], installer_file)) - if binary_mod <= installer_mod: - print("Binary is unchanged since last package, touch the binary to trigger repackage.") - exit(); + installer_path = os.path.join(self.args['configuration'], installer_file); + if os.path.isfile(installer_path): + binary_mod = os.path.getmtime(os.path.join(self.args['configuration'], self.final_exe())) + installer_mod = os.path.getmtime(installer_path) + if binary_mod <= installer_mod: + print("Binary is unchanged since last package, touch the binary or delete installer to trigger repackage.") + exit(); version_vars = """ !define INSTEXE "%(final_exe)s" From 128d30bd4b6f56672c7e3710003eac3c525337aa Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 16 Jun 2019 03:57:04 -0400 Subject: [PATCH 87/91] Fix Compile --- indra/newview/llfloaterteleporthistory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llfloaterteleporthistory.cpp b/indra/newview/llfloaterteleporthistory.cpp index ec7cb9ce4..f3369782d 100644 --- a/indra/newview/llfloaterteleporthistory.cpp +++ b/indra/newview/llfloaterteleporthistory.cpp @@ -160,7 +160,7 @@ void LLFloaterTeleportHistory::addEntry(std::string parcelName) if (max_entries <= 0) { if (!max_entries) - mPlaces->clearRows(); + mPlacesList->clearRows(); } else while(num_entries >= max_entries) { From 8fef329437c4e68ad0f2bbc9bdaceca9a5d75f19 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sat, 29 Jun 2019 21:23:07 -0400 Subject: [PATCH 88/91] Fix UB, thanks Router and Clang for the heads up --- indra/llui/lltexteditor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index b9a3b73ca..b0572a070 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4566,7 +4566,9 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h insertStringNoUndo(cur_length, wide_text); LLStyleSP sp(new LLStyle(*stylep)); sp->setColor(piece["color"]); - LLTextSegmentPtr segmentp = new LLTextSegment(sp, cur_length, cur_length += wide_text.size()); + auto wide_size = wide_text.size(); + LLTextSegmentPtr segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_size); + cur_length += wide_size; if (underline_on_hover) segmentp->setUnderlineOnHover(true); mSegments.push_back(segmentp); } From aec42782927706a325fab798b5631510d87543da Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 30 Jun 2019 17:03:45 -0400 Subject: [PATCH 89/91] Unbreak the goat! --- indra/llmessage/llmessagetemplate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/llmessagetemplate.h b/indra/llmessage/llmessagetemplate.h index 01c7310a5..d0d8aeebb 100644 --- a/indra/llmessage/llmessagetemplate.h +++ b/indra/llmessage/llmessagetemplate.h @@ -131,7 +131,7 @@ public: void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1); public: - typedef LLIndexedVector msg_blk_data_map_t; + typedef boost::unordered_map msg_blk_data_map_t; msg_blk_data_map_t mMemberBlocks; char *mName; S32 mTotalSize; From ce42937e3fab55db15c15274f018c9ca9928bb99 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 1 Jul 2019 20:29:08 -0400 Subject: [PATCH 90/91] Goats are as delicate as the UDP pipeline --- indra/llmessage/llmessagetemplate.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/llmessagetemplate.h b/indra/llmessage/llmessagetemplate.h index d0d8aeebb..29b2be3b7 100644 --- a/indra/llmessage/llmessagetemplate.h +++ b/indra/llmessage/llmessagetemplate.h @@ -32,6 +32,8 @@ #include "llstl.h" #include "llindexedvector.h" +#include + extern U32 sMsgDataAllocSize; extern U32 sMsgdataAllocCount; class LLMsgVarData @@ -131,7 +133,7 @@ public: void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1); public: - typedef boost::unordered_map msg_blk_data_map_t; + typedef std::unordered_map msg_blk_data_map_t; msg_blk_data_map_t mMemberBlocks; char *mName; S32 mTotalSize; From 0197a47007c24c7c31eb2ba1a60fd52abba69f24 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 1 Jul 2019 20:31:09 -0400 Subject: [PATCH 91/91] Support modern event slurls --- indra/newview/lleventnotifier.cpp | 46 +++++++++++++++++++++++++++++++ indra/newview/llfloaterevent.cpp | 24 ---------------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp index f4b583a53..f48b99411 100644 --- a/indra/newview/lleventnotifier.cpp +++ b/indra/newview/lleventnotifier.cpp @@ -43,8 +43,54 @@ #include "llfloaterworldmap.h" #include "llagent.h" #include "llappviewer.h" // for gPacificDaylightTime +#include "llcommandhandler.h" // secondlife:///app/... support #include "llviewercontrol.h" +class LLEventHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLEventHandler() : LLCommandHandler("event", UNTRUSTED_THROTTLE) { } + bool handle(const LLSD& params, const LLSD& query_map, + LLMediaCtrl* web) + { + if (params.size() < 2) + { + return false; + } + std::string event_command = params[1].asString(); + S32 event_id = params[0].asInteger(); + if (event_command == "about" || event_command == "details") + { + LLFloaterEventInfo::show(event_id); + return true; + } + else if(event_command == "notify") + { + // we're adding or removing a notification, so grab the date, name and notification bool + if (params.size() < 3) + { + return false; + } + if(params[2].asString() == "enable") + { + gEventNotifier.add(event_id); + // tell the server to modify the database as this was a slurl event notification command + gEventNotifier.serverPushRequest(event_id, true); + } + else + { + gEventNotifier.remove(event_id); + } + return true; + } + + return false; + } +}; +LLEventHandler gEventHandler; + + LLEventNotifier gEventNotifier; LLEventNotifier::LLEventNotifier() diff --git a/indra/newview/llfloaterevent.cpp b/indra/newview/llfloaterevent.cpp index b526767f8..ec863020c 100644 --- a/indra/newview/llfloaterevent.cpp +++ b/indra/newview/llfloaterevent.cpp @@ -36,7 +36,6 @@ #include "llfloaterevent.h" // viewer project includes -#include "llcommandhandler.h" #include "llpanelevent.h" // linden library includes @@ -52,29 +51,6 @@ LLMap< U32, LLFloaterEventInfo* > gEventInfoInstances; -class LLEventHandler : public LLCommandHandler -{ -public: - // requires trusted browser to trigger - LLEventHandler() : LLCommandHandler("event", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - if (tokens.size() < 2) - { - return false; - } - U32 event_id = tokens[0].asInteger(); - if (tokens[1].asString() == "about") - { - LLFloaterEventInfo::show(event_id); - return true; - } - return false; - } -}; -LLEventHandler gEventHandler; - LLFloaterEventInfo::LLFloaterEventInfo(const std::string& name, const U32 event_id) : LLFloater(name), mEventID( event_id )
  • ...