Merge branch 'animesh' of https://github.com/Lirusaito/SingularityViewer.git into animesh
This commit is contained in:
303
autobuild.xml
303
autobuild.xml
@@ -585,6 +585,40 @@
|
|||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
</map>
|
</map>
|
||||||
|
<key>discord-rpc</key>
|
||||||
|
<map>
|
||||||
|
<key>canonical_repo</key>
|
||||||
|
<string>https://bitbucket.org/alchemyviewer/3p-discord-rpc</string>
|
||||||
|
<key>copyright</key>
|
||||||
|
<string>Copyright 2017 Discord, Inc.</string>
|
||||||
|
<key>description</key>
|
||||||
|
<string>Discord Remote Procedure Call library</string>
|
||||||
|
<key>license</key>
|
||||||
|
<string>MIT</string>
|
||||||
|
<key>license_file</key>
|
||||||
|
<string>LICENSES/discord-rpc.txt</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>discord-rpc</string>
|
||||||
|
<key>platforms</key>
|
||||||
|
<map>
|
||||||
|
<key>windows64</key>
|
||||||
|
<map>
|
||||||
|
<key>archive</key>
|
||||||
|
<map>
|
||||||
|
<key>hash</key>
|
||||||
|
<string>76bd78e14034e9c518c1a201074d568a</string>
|
||||||
|
<key>hash_algorithm</key>
|
||||||
|
<string>md5</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>http://depot.alchemyviewer.org/pub/packages/windows64/msvc-1916/discord_rpc-3.4.0.190570542-windows64-190570542.tar.bz2</string>
|
||||||
|
</map>
|
||||||
|
<key>name</key>
|
||||||
|
<string>windows64</string>
|
||||||
|
</map>
|
||||||
|
</map>
|
||||||
|
<key>version</key>
|
||||||
|
<string>3.4.0.190570542</string>
|
||||||
|
</map>
|
||||||
<key>dullahan</key>
|
<key>dullahan</key>
|
||||||
<map>
|
<map>
|
||||||
<key>copyright</key>
|
<key>copyright</key>
|
||||||
@@ -630,7 +664,7 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>2ecc71350b30a1057091b9cd7af18b1c</string>
|
<string>c3e093d40e93ff242e9b9c953ae7316c</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/windows/lib-vc142/dullahan-1.1.930-3.3282.1733.g9091548-windows-201802202358.tar.bz2</string>
|
<string>https://depot.alchemyviewer.org/pub/windows/lib-vc142/dullahan-1.1.930-3.3282.1733.g9091548-windows-201802202358.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
@@ -768,7 +802,7 @@
|
|||||||
<key>fmodstudio</key>
|
<key>fmodstudio</key>
|
||||||
<map>
|
<map>
|
||||||
<key>copyright</key>
|
<key>copyright</key>
|
||||||
<string>FMOD Studio, copyright © Firelight Technologies Pty, Ltd., 1994-2015.</string>
|
<string>FMOD Studio, copyright (c) Firelight Technologies Pty, Ltd., 2012-2017.</string>
|
||||||
<key>description</key>
|
<key>description</key>
|
||||||
<string>FMOD Studio audio system library</string>
|
<string>FMOD Studio audio system library</string>
|
||||||
<key>license</key>
|
<key>license</key>
|
||||||
@@ -784,11 +818,11 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>a2badcf46277f7547bab0963fe262696</string>
|
<string>a3ccf7916d1e0b6f01370978b661c139</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/fmodstudio-1.08.01-darwin-201604080928.tar.bz2</string>
|
<string>https://depot.alchemyviewer.org/pub/darwin/lib/fmodstudio-1.10.02-darwin-201712262058.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>darwin</string>
|
<string>darwin</string>
|
||||||
@@ -812,11 +846,11 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>be09dc1f144c936790236b94190815b4</string>
|
<string>54dbd41322a08a1fc333ca6d96af5502</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/linux64/lib/fmodstudio-1.08.04-linux64-201605301500.tar.bz2</string>
|
<string>https://depot.alchemyviewer.org/pub/linux64/lib/fmodstudio-1.10.00-linux64-201709282320.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>linux64</string>
|
<string>linux64</string>
|
||||||
@@ -840,18 +874,18 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>c2c45c2ebd9fd930098aa2a3b85bbe5f</string>
|
<string>779ae33c6b84656295bd6cf50a69c830</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/windows64/lib/fmodstudio-1.10.02-windows64-201712251435.tar.bz2</string>
|
<string>http://depot.alchemyviewer.org/pub/packages/windows64/msvc/fmodstudio-1.10.10.190360453-windows64-190360453.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows64</string>
|
<string>windows64</string>
|
||||||
</map>
|
</map>
|
||||||
</map>
|
</map>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1.10.02</string>
|
<string>1.10.10.190360453</string>
|
||||||
</map>
|
</map>
|
||||||
<key>fontconfig</key>
|
<key>fontconfig</key>
|
||||||
<map>
|
<map>
|
||||||
@@ -1128,13 +1162,13 @@
|
|||||||
<key>glod</key>
|
<key>glod</key>
|
||||||
<map>
|
<map>
|
||||||
<key>copyright</key>
|
<key>copyright</key>
|
||||||
<string>Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia. All rights reserved.</string>
|
<string>Copyright 2003 Jonathan Cohen, Nat Duca, David Luebke, Brenden Schubert - Johns Hopkins University and University of Virginia</string>
|
||||||
<key>description</key>
|
<key>description</key>
|
||||||
<string>Geometric Level of Detail library</string>
|
<string>Geometric Level of Detail library</string>
|
||||||
<key>license</key>
|
<key>license</key>
|
||||||
<string>GLOD Open-Source License 1.0</string>
|
<string>GLOD Open-Source License Version 1.0</string>
|
||||||
<key>license_file</key>
|
<key>license_file</key>
|
||||||
<string>LICENSES/glod.txt</string>
|
<string>LICENSES/GLOD.txt</string>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>glod</string>
|
<string>glod</string>
|
||||||
<key>platforms</key>
|
<key>platforms</key>
|
||||||
@@ -1144,23 +1178,37 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>6c6b3b06a4305c2da0aad1de132e6a07</string>
|
<string>71e678d70e276fc42a56926fc28a7abd</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/glod-1.0pre4-darwin-201511222034.tar.bz2</string>
|
<string>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</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>darwin</string>
|
<string>darwin</string>
|
||||||
</map>
|
</map>
|
||||||
|
<key>darwin64</key>
|
||||||
|
<map>
|
||||||
|
<key>archive</key>
|
||||||
|
<map>
|
||||||
|
<key>hash</key>
|
||||||
|
<string>017ef34ddf14293099a90c6eaa3615ca</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1626/3627/glod-1.0pre3.501614-darwin64-501614.tar.bz2</string>
|
||||||
|
</map>
|
||||||
|
<key>name</key>
|
||||||
|
<string>darwin64</string>
|
||||||
|
</map>
|
||||||
<key>linux</key>
|
<key>linux</key>
|
||||||
<map>
|
<map>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>176736c52b3cde6ca8e7d9e173d91731</string>
|
<string>58113bcbbacbaeb2d278f745867ae6f0</string>
|
||||||
|
<key>hash_algorithm</key>
|
||||||
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-glod/rev/268002/arch/Linux/installer/glod-1.0pre4-linux-20121212.tar.bz2</string>
|
<string>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</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>linux</string>
|
<string>linux</string>
|
||||||
@@ -1170,11 +1218,9 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>f7172928ba2201d4b9aeb2c70d7a55b9</string>
|
<string>9aef5cd576ace19568da01d9bc3db29c</string>
|
||||||
<key>hash_algorithm</key>
|
|
||||||
<string>md5</string>
|
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>http://depot.alchemyviewer.org/pub/linux64/lib-trusty/glod-1.0pre4-linux64-201603240031.tar.bz2</string>
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1625/3628/glod-1.0pre3.501614-linux64-501614.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>linux64</string>
|
<string>linux64</string>
|
||||||
@@ -1184,11 +1230,11 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>37035aad7d066dfe6f26dc009aa9dcea</string>
|
<string>573e68f46f825a1c040daa4994ee2a61</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/windows/lib-vc141/glod-1.0pre4-windows-201703090622.tar.bz2</string>
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1627/3633/glod-1.0pre3.501614-windows-501614.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows</string>
|
<string>windows</string>
|
||||||
@@ -1198,18 +1244,18 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>c5c04c164ff057a43928de2f2b83470e</string>
|
<string>c122ba644e8ec146b4742428b799d749</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/windows64/lib-vc142/glod-1.0pre4-windows64-201712251340.tar.bz2</string>
|
<string>http://depot.alchemyviewer.org/pub/packages/windows64/msvc-1916/glod-1.0pre3.190390340-windows64-190390340.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows64</string>
|
<string>windows64</string>
|
||||||
</map>
|
</map>
|
||||||
</map>
|
</map>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1.0pre4</string>
|
<string>1.0pre3.190390340</string>
|
||||||
</map>
|
</map>
|
||||||
<key>google_breakpad</key>
|
<key>google_breakpad</key>
|
||||||
<map>
|
<map>
|
||||||
@@ -1218,7 +1264,7 @@
|
|||||||
<key>description</key>
|
<key>description</key>
|
||||||
<string>Breakpad is a crossplatform library for capturing crash callstacks and runtime data.</string>
|
<string>Breakpad is a crossplatform library for capturing crash callstacks and runtime data.</string>
|
||||||
<key>license</key>
|
<key>license</key>
|
||||||
<string>BSD</string>
|
<string>bsd</string>
|
||||||
<key>license_file</key>
|
<key>license_file</key>
|
||||||
<string>LICENSES/google_breakpad.txt</string>
|
<string>LICENSES/google_breakpad.txt</string>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
@@ -1239,6 +1285,18 @@
|
|||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>darwin</string>
|
<string>darwin</string>
|
||||||
</map>
|
</map>
|
||||||
|
<key>darwin64</key>
|
||||||
|
<map>
|
||||||
|
<key>archive</key>
|
||||||
|
<map>
|
||||||
|
<key>hash</key>
|
||||||
|
<string>2d43c6a149cd9c89ba19e884579b1e25</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1836/4096/google_breakpad-1413.501824-darwin64-501824.tar.bz2</string>
|
||||||
|
</map>
|
||||||
|
<key>name</key>
|
||||||
|
<string>darwin64</string>
|
||||||
|
</map>
|
||||||
<key>linux</key>
|
<key>linux</key>
|
||||||
<map>
|
<map>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
@@ -2400,11 +2458,11 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>68a8fab5ad3a180487598d3a3e0d57b1</string>
|
<string>b1b0134bc55f55a7c1a04ee78d70c64d</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>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</string>
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12438/73598/slvoice-4.9.0002.27586.511884-darwin64-511884.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>darwin</string>
|
<string>darwin</string>
|
||||||
@@ -2440,18 +2498,32 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>5a78539626b5f23522d0b466247f48b4</string>
|
<string>ec50c31efce74bdedee470b5388aeeec</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>md5</string>
|
<string>md5</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/21422/157291/slvoice-4.9.0002.30313.517593-windows-517593.tar.bz2</string>
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12434/73576/slvoice-4.9.0002.27586.511884-windows-511884.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows</string>
|
<string>windows</string>
|
||||||
</map>
|
</map>
|
||||||
|
<key>windows64</key>
|
||||||
|
<map>
|
||||||
|
<key>archive</key>
|
||||||
|
<map>
|
||||||
|
<key>hash</key>
|
||||||
|
<string>4bf45d641bf5b2bd6b2cc39edcb01a6e</string>
|
||||||
|
<key>hash_algorithm</key>
|
||||||
|
<string>md5</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12433/73570/slvoice-4.9.0002.27586.511884-windows64-511884.tar.bz2</string>
|
||||||
|
</map>
|
||||||
|
<key>name</key>
|
||||||
|
<string>windows64</string>
|
||||||
|
</map>
|
||||||
</map>
|
</map>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>4.6.0017.22050</string>
|
<string>4.9.0002.27586.511884</string>
|
||||||
</map>
|
</map>
|
||||||
<key>tut</key>
|
<key>tut</key>
|
||||||
<map>
|
<map>
|
||||||
@@ -2571,30 +2643,50 @@
|
|||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>0.8.4</string>
|
<string>0.8.4</string>
|
||||||
</map>
|
</map>
|
||||||
<key>vcredist</key>
|
<key>vlc-bin</key>
|
||||||
<map>
|
<map>
|
||||||
<key>copyright</key>
|
<key>copyright</key>
|
||||||
<string>Copyright (c) Microsoft Corporation. All rights reserved.</string>
|
<string>Copyright (C) 1998-2016 VLC authors and VideoLAN</string>
|
||||||
<key>description</key>
|
|
||||||
<string>The Visual Studio C and C++ runtime redistributable</string>
|
|
||||||
<key>license</key>
|
<key>license</key>
|
||||||
<string>Proprietary</string>
|
<string>GPL2</string>
|
||||||
<key>license_file</key>
|
<key>license_file</key>
|
||||||
<string>LICENSES/vcredist.txt</string>
|
<string>LICENSES/vlc.txt</string>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>vcredist</string>
|
<string>vlc-bin</string>
|
||||||
<key>platforms</key>
|
<key>platforms</key>
|
||||||
<map>
|
<map>
|
||||||
|
<key>darwin64</key>
|
||||||
|
<map>
|
||||||
|
<key>archive</key>
|
||||||
|
<map>
|
||||||
|
<key>hash</key>
|
||||||
|
<string>e5635e173c75dc0675b48ab5f5e4868b</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12143/71451/vlc_bin-2.2.8.511703-darwin64-511703.tar.bz2</string>
|
||||||
|
</map>
|
||||||
|
<key>name</key>
|
||||||
|
<string>darwin64</string>
|
||||||
|
</map>
|
||||||
|
<key>linux</key>
|
||||||
|
<map>
|
||||||
|
<key>archive</key>
|
||||||
|
<map>
|
||||||
|
<key>hash</key>
|
||||||
|
<string>2f410640df3f9812d1abff02a414cfa8</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>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</string>
|
||||||
|
</map>
|
||||||
|
<key>name</key>
|
||||||
|
<string>linux</string>
|
||||||
|
</map>
|
||||||
<key>windows</key>
|
<key>windows</key>
|
||||||
<map>
|
<map>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>f193ec9e594d857d22c067c6e52622a9</string>
|
<string>add560654a53cb1c554044a4fac3c718</string>
|
||||||
<key>hash_algorithm</key>
|
|
||||||
<string>md5</string>
|
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/windows/lib-vc141/vcredist-14.10.25017.0-windows-201705270440.tar.bz2</string>
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12144/71458/vlc_bin-2.2.8.511703-windows-511703.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows</string>
|
<string>windows</string>
|
||||||
@@ -2604,18 +2696,16 @@
|
|||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>e895f4b5d24b372a360c3a8fe3bc0398</string>
|
<string>94bf04b49acc1e1bf2c06e2232f8a083</string>
|
||||||
<key>hash_algorithm</key>
|
|
||||||
<string>md5</string>
|
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://depot.alchemyviewer.org/pub/windows64/lib-vc142/vcredist-14.12.25810.0-windows64-201712251445.tar.bz2</string>
|
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12145/71463/vlc_bin-2.2.8.511703-windows64-511703.tar.bz2</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows64</string>
|
<string>windows64</string>
|
||||||
</map>
|
</map>
|
||||||
</map>
|
</map>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>14.12.25810.0</string>
|
<string>2.2.8.511703</string>
|
||||||
</map>
|
</map>
|
||||||
<key>xmlrpc-epi</key>
|
<key>xmlrpc-epi</key>
|
||||||
<map>
|
<map>
|
||||||
@@ -2821,7 +2911,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo</string>
|
<string>-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo</string>
|
||||||
<string>-DWORD_SIZE:STRING=32</string>
|
<string>-DWORD_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
|
||||||
<string>-DROOT_PROJECT_NAME:STRING=Singularity</string>
|
<string>-DROOT_PROJECT_NAME:STRING=Singularity</string>
|
||||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
||||||
</array>
|
</array>
|
||||||
@@ -2842,7 +2932,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-DCMAKE_BUILD_TYPE:STRING=Release</string>
|
<string>-DCMAKE_BUILD_TYPE:STRING=Release</string>
|
||||||
<string>-DWORD_SIZE:STRING=32</string>
|
<string>-DWORD_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
|
||||||
<string>-DROOT_PROJECT_NAME:STRING=Singularity</string>
|
<string>-DROOT_PROJECT_NAME:STRING=Singularity</string>
|
||||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
||||||
</array>
|
</array>
|
||||||
@@ -2936,7 +3026,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-G</string>
|
<string>-G</string>
|
||||||
<string>'Unix Makefiles'</string>
|
<string>Unix Makefiles</string>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
<key>default</key>
|
<key>default</key>
|
||||||
@@ -2960,7 +3050,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-G</string>
|
<string>-G</string>
|
||||||
<string>'Unix Makefiles'</string>
|
<string>Unix Makefiles</string>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
@@ -2992,7 +3082,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-G</string>
|
<string>-G</string>
|
||||||
<string>'Unix Makefiles'</string>
|
<string>Unix Makefiles</string>
|
||||||
<string>-DWORD_SIZE:STRING=64</string>
|
<string>-DWORD_SIZE:STRING=64</string>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
@@ -3017,7 +3107,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-G</string>
|
<string>-G</string>
|
||||||
<string>'Unix Makefiles'</string>
|
<string>Unix Makefiles</string>
|
||||||
<string>-DWORD_SIZE:STRING=64</string>
|
<string>-DWORD_SIZE:STRING=64</string>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
@@ -3031,7 +3121,7 @@
|
|||||||
<key>windows</key>
|
<key>windows</key>
|
||||||
<map>
|
<map>
|
||||||
<key>build_directory</key>
|
<key>build_directory</key>
|
||||||
<string>build-vc141</string>
|
<string>build-vc$AUTOBUILD_WIN_VSVER-$AUTOBUILD_ADDRSIZE</string>
|
||||||
<key>configurations</key>
|
<key>configurations</key>
|
||||||
<map>
|
<map>
|
||||||
<key>RelWithDebInfo</key>
|
<key>RelWithDebInfo</key>
|
||||||
@@ -3063,9 +3153,11 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-G</string>
|
<string>-G</string>
|
||||||
<string>"Visual Studio 15"</string>
|
<string>${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN}</string>
|
||||||
<string>-DCMAKE_SYSTEM_VERSION="10.0.16299.0"</string>
|
<string>-A</string>
|
||||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
<string>${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
|
||||||
|
<string>-T</string>
|
||||||
|
<string>host=${AUTOBUILD_WIN_VSHOST|NOTWIN}</string>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
<key>default</key>
|
<key>default</key>
|
||||||
@@ -3086,7 +3178,7 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>/p:Configuration=Release</string>
|
<string>/p:Configuration=Release</string>
|
||||||
<string>/p:Platform=Win32</string>
|
<string>/p:Platform=${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
|
||||||
<string>/t:Build</string>
|
<string>/t:Build</string>
|
||||||
<string>/p:useenv=true</string>
|
<string>/p:useenv=true</string>
|
||||||
<string>/verbosity:normal</string>
|
<string>/verbosity:normal</string>
|
||||||
@@ -3102,96 +3194,11 @@
|
|||||||
<key>options</key>
|
<key>options</key>
|
||||||
<array>
|
<array>
|
||||||
<string>-G</string>
|
<string>-G</string>
|
||||||
<string>"Visual Studio 15"</string>
|
<string>${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN}</string>
|
||||||
<string>-DCMAKE_SYSTEM_VERSION="10.0.16299.0"</string>
|
<string>-A</string>
|
||||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
<string>${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}</string>
|
||||||
</array>
|
<string>-T</string>
|
||||||
</map>
|
<string>host=${AUTOBUILD_WIN_VSHOST|NOTWIN}</string>
|
||||||
<key>name</key>
|
|
||||||
<string>Release</string>
|
|
||||||
</map>
|
|
||||||
</map>
|
|
||||||
<key>name</key>
|
|
||||||
<string>windows</string>
|
|
||||||
</map>
|
|
||||||
<key>windows64</key>
|
|
||||||
<map>
|
|
||||||
<key>build_directory</key>
|
|
||||||
<string>build-vc141-x86_64</string>
|
|
||||||
<key>configurations</key>
|
|
||||||
<map>
|
|
||||||
<key>RelWithDebInfo</key>
|
|
||||||
<map>
|
|
||||||
<key>build</key>
|
|
||||||
<map>
|
|
||||||
<key>arguments</key>
|
|
||||||
<array>
|
|
||||||
<string>Singularity.sln</string>
|
|
||||||
</array>
|
|
||||||
<key>command</key>
|
|
||||||
<string>msbuild.exe</string>
|
|
||||||
<key>options</key>
|
|
||||||
<array>
|
|
||||||
<string>/p:Configuration=RelWithDebInfo</string>
|
|
||||||
<string>/p:Platform=x64</string>
|
|
||||||
<string>/t:Build</string>
|
|
||||||
<string>/p:useenv=true</string>
|
|
||||||
<string>/verbosity:normal</string>
|
|
||||||
<string>/m</string>
|
|
||||||
</array>
|
|
||||||
</map>
|
|
||||||
<key>configure</key>
|
|
||||||
<map>
|
|
||||||
<key>arguments</key>
|
|
||||||
<array>
|
|
||||||
<string>..\indra</string>
|
|
||||||
</array>
|
|
||||||
<key>options</key>
|
|
||||||
<array>
|
|
||||||
<string>-G</string>
|
|
||||||
<string>"Visual Studio 15 Win64"</string>
|
|
||||||
<string>-DCMAKE_SYSTEM_VERSION="10.0.16299.0"</string>
|
|
||||||
<string>-DWORD_SIZE:STRING=64</string>
|
|
||||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
|
||||||
</array>
|
|
||||||
</map>
|
|
||||||
<key>default</key>
|
|
||||||
<string>True</string>
|
|
||||||
<key>name</key>
|
|
||||||
<string>RelWithDebInfo</string>
|
|
||||||
</map>
|
|
||||||
<key>Release</key>
|
|
||||||
<map>
|
|
||||||
<key>build</key>
|
|
||||||
<map>
|
|
||||||
<key>arguments</key>
|
|
||||||
<array>
|
|
||||||
<string>Singularity.sln</string>
|
|
||||||
</array>
|
|
||||||
<key>command</key>
|
|
||||||
<string>msbuild.exe</string>
|
|
||||||
<key>options</key>
|
|
||||||
<array>
|
|
||||||
<string>/p:Configuration=Release</string>
|
|
||||||
<string>/p:Platform=x64</string>
|
|
||||||
<string>/t:Build</string>
|
|
||||||
<string>/p:useenv=true</string>
|
|
||||||
<string>/verbosity:normal</string>
|
|
||||||
<string>/m</string>
|
|
||||||
</array>
|
|
||||||
</map>
|
|
||||||
<key>configure</key>
|
|
||||||
<map>
|
|
||||||
<key>arguments</key>
|
|
||||||
<array>
|
|
||||||
<string>..\indra</string>
|
|
||||||
</array>
|
|
||||||
<key>options</key>
|
|
||||||
<array>
|
|
||||||
<string>-G</string>
|
|
||||||
<string>"Visual Studio 15 Win64"</string>
|
|
||||||
<string>-DCMAKE_SYSTEM_VERSION="10.0.16299.0"</string>
|
|
||||||
<string>-DWORD_SIZE:STRING=64</string>
|
|
||||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
if (WINDOWS)
|
if (WINDOWS)
|
||||||
message(WARNING, ${CMAKE_CURRENT_BINARY_DIR}/newview/viewerRes.rc.in)
|
#message(WARNING, ${CMAKE_CURRENT_BINARY_DIR}/newview/viewerRes.rc.in)
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_SOURCE_DIR}/newview/res/viewerRes.rc.in
|
${CMAKE_SOURCE_DIR}/newview/res/viewerRes.rc.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/viewerRes.rc
|
${CMAKE_CURRENT_BINARY_DIR}/viewerRes.rc
|
||||||
|
|||||||
@@ -3,9 +3,18 @@
|
|||||||
include(Python)
|
include(Python)
|
||||||
include(Variables)
|
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
|
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
|
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/../autobuild.xml
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py
|
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}/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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n
|
|||||||
|
|
||||||
set(VIEWER_CHANNEL_VERSION_DEFINES
|
set(VIEWER_CHANNEL_VERSION_DEFINES
|
||||||
"LL_VIEWER_CHANNEL=\"${VIEWER_CHANNEL}\""
|
"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_MAJOR=${VIEWER_VERSION_MAJOR}"
|
||||||
"LL_VIEWER_VERSION_MINOR=${VIEWER_VERSION_MINOR}"
|
"LL_VIEWER_VERSION_MINOR=${VIEWER_VERSION_MINOR}"
|
||||||
"LL_VIEWER_VERSION_PATCH=${VIEWER_VERSION_PATCH}"
|
"LL_VIEWER_VERSION_PATCH=${VIEWER_VERSION_PATCH}"
|
||||||
|
|||||||
@@ -18,16 +18,15 @@ set(cmake_SOURCE_FILES
|
|||||||
CARes.cmake
|
CARes.cmake
|
||||||
CEFPlugin.cmake
|
CEFPlugin.cmake
|
||||||
CMakeCopyIfDifferent.cmake
|
CMakeCopyIfDifferent.cmake
|
||||||
CURL.cmake
|
|
||||||
Colladadom.cmake
|
Colladadom.cmake
|
||||||
ConfigurePkgConfig.cmake
|
ConfigurePkgConfig.cmake
|
||||||
|
CURL.cmake
|
||||||
Copy3rdPartyLibs.cmake
|
Copy3rdPartyLibs.cmake
|
||||||
Cwdebug.cmake
|
Cwdebug.cmake
|
||||||
DBusGlib.cmake
|
DBusGlib.cmake
|
||||||
DeploySharedLibs.cmake
|
DeploySharedLibs.cmake
|
||||||
EXPAT.cmake
|
EXPAT.cmake
|
||||||
ExamplePlugin.cmake
|
ExamplePlugin.cmake
|
||||||
FMODSTUDIO.cmake
|
|
||||||
FindAPR.cmake
|
FindAPR.cmake
|
||||||
FindAutobuild.cmake
|
FindAutobuild.cmake
|
||||||
FindCARes.cmake
|
FindCARes.cmake
|
||||||
@@ -39,8 +38,10 @@ set(cmake_SOURCE_FILES
|
|||||||
FindNDOF.cmake
|
FindNDOF.cmake
|
||||||
FindOpenJPEG.cmake
|
FindOpenJPEG.cmake
|
||||||
FindTut.cmake
|
FindTut.cmake
|
||||||
|
FindVCRedist.cmake
|
||||||
FindURIPARSER.cmake
|
FindURIPARSER.cmake
|
||||||
FindXmlRpcEpi.cmake
|
FindXmlRpcEpi.cmake
|
||||||
|
FMODSTUDIO.cmake
|
||||||
FreeType.cmake
|
FreeType.cmake
|
||||||
GLOD.cmake
|
GLOD.cmake
|
||||||
GStreamer010Plugin.cmake
|
GStreamer010Plugin.cmake
|
||||||
@@ -85,8 +86,6 @@ set(cmake_SOURCE_FILES
|
|||||||
Prebuilt.cmake
|
Prebuilt.cmake
|
||||||
PulseAudio.cmake
|
PulseAudio.cmake
|
||||||
Python.cmake
|
Python.cmake
|
||||||
QuickTimePlugin.cmake
|
|
||||||
RunBuildTest.cmake
|
|
||||||
StateMachine.cmake
|
StateMachine.cmake
|
||||||
TemplateCheck.cmake
|
TemplateCheck.cmake
|
||||||
Tut.cmake
|
Tut.cmake
|
||||||
@@ -96,6 +95,7 @@ set(cmake_SOURCE_FILES
|
|||||||
Variables.cmake
|
Variables.cmake
|
||||||
ViewerMiscLibs.cmake
|
ViewerMiscLibs.cmake
|
||||||
WinManifest.cmake
|
WinManifest.cmake
|
||||||
|
LibVLCPlugin.cmake
|
||||||
XmlRpcEpi.cmake
|
XmlRpcEpi.cmake
|
||||||
ZLIB.cmake
|
ZLIB.cmake
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
include(CMakeCopyIfDifferent)
|
include(CMakeCopyIfDifferent)
|
||||||
include(Linking)
|
include(Linking)
|
||||||
|
include(Variables)
|
||||||
|
include(LLCommon)
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# set up platform specific lists of files that need to be copied
|
# set up platform specific lists of files that need to be copied
|
||||||
@@ -20,10 +22,19 @@ if(WINDOWS)
|
|||||||
set(vivox_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
|
set(vivox_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
|
||||||
set(vivox_files
|
set(vivox_files
|
||||||
SLVoice.exe
|
SLVoice.exe
|
||||||
vivoxsdk.dll
|
|
||||||
ortp.dll
|
|
||||||
vivoxplatform.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
|
# Misc shared libs
|
||||||
@@ -46,7 +57,7 @@ if(WINDOWS)
|
|||||||
libhunspell.dll
|
libhunspell.dll
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WORD_SIZE STREQUAL 64)
|
if(ADDRESS_SIZE EQUAL 64)
|
||||||
list(APPEND debug_files
|
list(APPEND debug_files
|
||||||
libcrypto-1_1-x64.dll
|
libcrypto-1_1-x64.dll
|
||||||
libssl-1_1-x64.dll
|
libssl-1_1-x64.dll
|
||||||
@@ -55,7 +66,7 @@ if(WINDOWS)
|
|||||||
libcrypto-1_1-x64.dll
|
libcrypto-1_1-x64.dll
|
||||||
libssl-1_1-x64.dll
|
libssl-1_1-x64.dll
|
||||||
)
|
)
|
||||||
else(WORD_SIZE STREQUAL 64)
|
else(ADDRESS_SIZE EQUAL 64)
|
||||||
list(APPEND debug_files
|
list(APPEND debug_files
|
||||||
libcrypto-1_1.dll
|
libcrypto-1_1.dll
|
||||||
libssl-1_1.dll
|
libssl-1_1.dll
|
||||||
@@ -64,7 +75,7 @@ if(WINDOWS)
|
|||||||
libcrypto-1_1.dll
|
libcrypto-1_1.dll
|
||||||
libssl-1_1.dll
|
libssl-1_1.dll
|
||||||
)
|
)
|
||||||
endif(WORD_SIZE STREQUAL 64)
|
endif(ADDRESS_SIZE EQUAL 64)
|
||||||
|
|
||||||
if(NOT DISABLE_TCMALLOC)
|
if(NOT DISABLE_TCMALLOC)
|
||||||
set(debug_files ${debug_files} libtcmalloc_minimal-debug.dll)
|
set(debug_files ${debug_files} libtcmalloc_minimal-debug.dll)
|
||||||
@@ -72,14 +83,15 @@ if(WINDOWS)
|
|||||||
endif(NOT DISABLE_TCMALLOC)
|
endif(NOT DISABLE_TCMALLOC)
|
||||||
|
|
||||||
if (FMODSTUDIO)
|
if (FMODSTUDIO)
|
||||||
if(WORD_SIZE STREQUAL 64)
|
if(ADDRESS_SIZE STREQUAL 64)
|
||||||
set(debug_files ${debug_files} fmodL64.dll)
|
set(debug_files ${debug_files} fmodL64.dll)
|
||||||
set(release_files ${release_files} fmod64.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(debug_files ${debug_files} fmodL.dll)
|
||||||
set(release_files ${release_files} fmod.dll)
|
set(release_files ${release_files} fmod.dll)
|
||||||
endif(WORD_SIZE STREQUAL 64)
|
endif(ADDRESS_SIZE STREQUAL 64)
|
||||||
endif (FMODSTUDIO)
|
endif (FMODSTUDIO)
|
||||||
|
|
||||||
elseif(DARWIN)
|
elseif(DARWIN)
|
||||||
set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug/Resources")
|
set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug/Resources")
|
||||||
set(SHARED_LIB_STAGING_DIR_RELWITHDEBINFO "${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/Resources")
|
set(SHARED_LIB_STAGING_DIR_RELWITHDEBINFO "${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/Resources")
|
||||||
@@ -113,8 +125,8 @@ elseif(DARWIN)
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (FMODSTUDIO)
|
if (FMODSTUDIO)
|
||||||
set(debug_files ${debug_files} libfmodL.dylib)
|
list(APPEND debug_files libfmodL.dylib)
|
||||||
set(release_files ${release_files} libfmod.dylib)
|
list(APPEND release_files libfmod.dylib)
|
||||||
endif (FMODSTUDIO)
|
endif (FMODSTUDIO)
|
||||||
|
|
||||||
elseif(LINUX)
|
elseif(LINUX)
|
||||||
@@ -132,7 +144,6 @@ elseif(LINUX)
|
|||||||
libvivoxplatform.so
|
libvivoxplatform.so
|
||||||
libvivoxsdk.so
|
libvivoxsdk.so
|
||||||
SLVoice
|
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
|
# *TODO - update this to use LIBS_PREBUILT_DIR and LL_ARCH_DIR variables
|
||||||
# or ARCH_PREBUILT_DIRS
|
# or ARCH_PREBUILT_DIRS
|
||||||
@@ -221,13 +232,13 @@ set(third_party_targets ${third_party_targets} ${out_targets})
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
copy_if_different(
|
#copy_if_different(
|
||||||
${debug_src_dir}
|
# ${debug_src_dir}
|
||||||
"${SHARED_LIB_STAGING_DIR_DEBUG}"
|
# "${SHARED_LIB_STAGING_DIR_DEBUG}"
|
||||||
out_targets
|
# out_targets
|
||||||
${debug_files}
|
# ${debug_files}
|
||||||
)
|
# )
|
||||||
set(third_party_targets ${third_party_targets} ${out_targets})
|
#set(third_party_targets ${third_party_targets} ${out_targets})
|
||||||
|
|
||||||
copy_if_different(
|
copy_if_different(
|
||||||
${release_src_dir}
|
${release_src_dir}
|
||||||
|
|||||||
34
indra/cmake/FindVCRedist.cmake
Normal file
34
indra/cmake/FindVCRedist.cmake
Normal file
@@ -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)
|
||||||
@@ -12,4 +12,5 @@ else (STANDALONE)
|
|||||||
set(HUNSPELL_LIBRARY libhunspell)
|
set(HUNSPELL_LIBRARY libhunspell)
|
||||||
endif (LINUX OR DARWIN)
|
endif (LINUX OR DARWIN)
|
||||||
set(HUNSPELL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/hunspell)
|
set(HUNSPELL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/hunspell)
|
||||||
|
use_prebuilt_binary(dictionaries)
|
||||||
endif (STANDALONE)
|
endif (STANDALONE)
|
||||||
|
|||||||
@@ -105,17 +105,6 @@ MACRO(ADD_BUILD_TEST_INTERNAL name parent libraries source_files)
|
|||||||
IF (NOT "$ENV{LD_LIBRARY_PATH}" STREQUAL "")
|
IF (NOT "$ENV{LD_LIBRARY_PATH}" STREQUAL "")
|
||||||
SET(LD_LIBRARY_PATH "$ENV{LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}")
|
SET(LD_LIBRARY_PATH "$ENV{LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}")
|
||||||
ENDIF (NOT "$ENV{LD_LIBRARY_PATH}" STREQUAL "")
|
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})
|
ADD_CUSTOM_TARGET(${name}_test_ok ALL DEPENDS ${TEST_OUTPUT})
|
||||||
IF (${parent})
|
IF (${parent})
|
||||||
|
|||||||
@@ -1,32 +1,40 @@
|
|||||||
# ll_deploy_sharedlibs_command
|
# ll_deploy_sharedlibs_command
|
||||||
# target_exe: the cmake target of the executable for which the shared libs will be deployed.
|
# target_exe: the cmake target of the executable for which the shared libs will be deployed.
|
||||||
macro(ll_deploy_sharedlibs_command target_exe)
|
macro(ll_deploy_sharedlibs_command target_exe)
|
||||||
set(OUTPUT_PATH $<TARGET_FILE_DIR:${target_exe}>)
|
set(TARGET_LOCATION $<TARGET_FILE:${target_exe}>)
|
||||||
|
get_filename_component(OUTPUT_PATH ${TARGET_LOCATION} PATH)
|
||||||
|
|
||||||
if(DARWIN)
|
# It's not clear that this does anything useful for us on Darwin. It has
|
||||||
SET_TEST_PATH(SEARCH_DIRS)
|
# been broken for some time now; the BIN_NAME was being constructed as a
|
||||||
get_target_property(IS_BUNDLE ${target_exe} MACOSX_BUNDLE)
|
# ridiculous nonexistent path with duplicated segments. Fixing that only
|
||||||
if(IS_BUNDLE)
|
# produces ominous spammy warnings: at the time the command below is run, we
|
||||||
set(OUTPUT_PATH ${OUTPUT_PATH}/../Resources)
|
# have not yet populated the nested mac-crash-logger.app/Contents/Resources
|
||||||
endif(IS_BUNDLE)
|
# with the .dylibs with which it was linked. Moreover, the form of the
|
||||||
elseif(WINDOWS)
|
# embedded @executable_path/../Resources/mumble.dylib pathname confuses the
|
||||||
SET_TEST_PATH(SEARCH_DIRS)
|
# GetPrerequisites.cmake tool invoked by DeploySharedLibs.cmake. It seems
|
||||||
LIST(APPEND SEARCH_DIRS "$ENV{SystemRoot}/system32")
|
# clear that we have long since accomplished by other means what this was
|
||||||
elseif(LINUX)
|
# originally supposed to do. Skipping it only eliminates an annoying
|
||||||
SET_TEST_PATH(SEARCH_DIRS)
|
# non-fatal error.
|
||||||
set(OUTPUT_PATH ${OUTPUT_PATH}/lib)
|
if(NOT DARWIN)
|
||||||
endif(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(
|
add_custom_command(
|
||||||
TARGET ${target_exe} POST_BUILD
|
TARGET ${target_exe} POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
ARGS
|
ARGS
|
||||||
"-DBIN_NAME=\"$<TARGET_FILE:${target_exe}>\""
|
"-DBIN_NAME=\"${TARGET_LOCATION}\""
|
||||||
"-DSEARCH_DIRS=\"${SEARCH_DIRS}\""
|
"-DSEARCH_DIRS=\"${SEARCH_DIRS}\""
|
||||||
"-DDST_PATH=\"${OUTPUT_PATH}\""
|
"-DDST_PATH=\"${OUTPUT_PATH}\""
|
||||||
"-P"
|
"-P"
|
||||||
"${CMAKE_SOURCE_DIR}/cmake/DeploySharedLibs.cmake"
|
"${CMAKE_SOURCE_DIR}/cmake/DeploySharedLibs.cmake"
|
||||||
)
|
)
|
||||||
|
endif(NOT DARWIN)
|
||||||
|
|
||||||
endmacro(ll_deploy_sharedlibs_command)
|
endmacro(ll_deploy_sharedlibs_command)
|
||||||
|
|
||||||
|
|||||||
30
indra/cmake/LibVLCPlugin.cmake
Normal file
30
indra/cmake/LibVLCPlugin.cmake
Normal file
@@ -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)
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
|
|
||||||
|
include(Variables)
|
||||||
include(Prebuilt)
|
include(Prebuilt)
|
||||||
|
|
||||||
if (NOT (STANDALONE OR DARWIN))
|
if (NOT (STANDALONE OR DARWIN))
|
||||||
@@ -8,3 +10,5 @@ if (NOT (STANDALONE OR DARWIN))
|
|||||||
${LIBS_PREBUILT_LEGACY_DIR}/include
|
${LIBS_PREBUILT_LEGACY_DIR}/include
|
||||||
)
|
)
|
||||||
endif (NOT (STANDALONE OR DARWIN))
|
endif (NOT (STANDALONE OR DARWIN))
|
||||||
|
|
||||||
|
include(FindOpenGL)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ if(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED)
|
|||||||
set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES")
|
set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES")
|
||||||
|
|
||||||
include(FindAutobuild)
|
include(FindAutobuild)
|
||||||
include(Variables)
|
|
||||||
if(INSTALL_PROPRIETARY)
|
if(INSTALL_PROPRIETARY)
|
||||||
include(FindSCP)
|
include(FindSCP)
|
||||||
endif(INSTALL_PROPRIETARY)
|
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(${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/${_binary}_installed OR NOT ${${_binary}_installed} EQUAL 0)
|
||||||
if(DEBUG_PREBUILT)
|
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}
|
--install-dir=${AUTOBUILD_INSTALL_DIR}
|
||||||
${_binary} ")
|
${_binary} ")
|
||||||
endif(DEBUG_PREBUILT)
|
endif(DEBUG_PREBUILT)
|
||||||
execute_process(COMMAND "${AUTOBUILD_EXECUTABLE}"
|
execute_process(COMMAND "${AUTOBUILD_EXECUTABLE}"
|
||||||
install
|
install
|
||||||
--install-dir=${AUTOBUILD_INSTALL_DIR}
|
--install-dir=${AUTOBUILD_INSTALL_DIR}
|
||||||
-p ${AUTOBUILD_PLATFORM_NAME}
|
|
||||||
${_binary}
|
${_binary}
|
||||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
RESULT_VARIABLE ${_binary}_installed
|
RESULT_VARIABLE ${_binary}_installed
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|
||||||
@@ -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)
|
|
||||||
@@ -24,14 +24,6 @@ set(VIEWER_PREFIX)
|
|||||||
set(INTEGRATION_TESTS_PREFIX)
|
set(INTEGRATION_TESTS_PREFIX)
|
||||||
option(LL_TESTS "Build and run unit and integration tests (disable for build timing runs to reduce variation" OFF)
|
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(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_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)
|
option(USE_LTO "Enable Whole Program Optimization and related folding and binary reduction routines" OFF)
|
||||||
@@ -40,6 +32,7 @@ option(UNATTENDED "Disable use of uneeded tooling for automated builds" OFF)
|
|||||||
# Media Plugins
|
# Media Plugins
|
||||||
option(ENABLE_MEDIA_PLUGINS "Turn off building media plugins if they are imported by third-party library mechanism" ON)
|
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" ON)
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
set(LIBVLCPLUGIN OFF)
|
set(LIBVLCPLUGIN OFF)
|
||||||
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
@@ -47,8 +40,9 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Dar
|
|||||||
# Mallocs
|
# Mallocs
|
||||||
set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds automatically disable TCMalloc)")
|
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.")
|
set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.")
|
||||||
|
|
||||||
# Audio Engines
|
# 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
|
# Window implementation
|
||||||
option(LLWINDOW_SDL2 "Use SDL2 for window and input handling" OFF)
|
option(LLWINDOW_SDL2 "Use SDL2 for window and input handling" OFF)
|
||||||
@@ -88,61 +82,56 @@ if (NOT CMAKE_BUILD_TYPE)
|
|||||||
"Build type. One of: Debug Release RelWithDebInfo" FORCE)
|
"Build type. One of: Debug Release RelWithDebInfo" FORCE)
|
||||||
endif (NOT CMAKE_BUILD_TYPE)
|
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")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||||
set(WINDOWS ON BOOL FORCE)
|
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 ${ARCH}_win32)
|
||||||
set(LL_ARCH_DIR ${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")
|
endif (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
set(LINUX ON BOOL FORCE)
|
set(LINUX ON BOOL FORCE)
|
||||||
|
|
||||||
# If someone has specified a word size, use that to determine the
|
if (ADDRESS_SIZE EQUAL 32)
|
||||||
# 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)
|
|
||||||
set(DEB_ARCHITECTURE i386)
|
set(DEB_ARCHITECTURE i386)
|
||||||
set(FIND_LIBRARY_USE_LIB64_PATHS OFF)
|
set(FIND_LIBRARY_USE_LIB64_PATHS OFF)
|
||||||
set(CMAKE_SYSTEM_LIBRARY_PATH /usr/lib32 ${CMAKE_SYSTEM_LIBRARY_PATH})
|
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(DEB_ARCHITECTURE amd64)
|
||||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
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
|
execute_process(COMMAND dpkg-architecture -a${DEB_ARCHITECTURE} -qDEB_HOST_MULTIARCH
|
||||||
RESULT_VARIABLE DPKG_RESULT
|
RESULT_VARIABLE DPKG_RESULT
|
||||||
@@ -155,7 +144,6 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||||||
endif (DPKG_RESULT EQUAL 0)
|
endif (DPKG_RESULT EQUAL 0)
|
||||||
|
|
||||||
include(ConfigurePkgConfig)
|
include(ConfigurePkgConfig)
|
||||||
endif (NOT STANDALONE AND MULTIARCH_HACK)
|
|
||||||
|
|
||||||
set(LL_ARCH ${ARCH}_linux)
|
set(LL_ARCH ${ARCH}_linux)
|
||||||
set(LL_ARCH_DIR ${ARCH}-linux)
|
set(LL_ARCH_DIR ${ARCH}-linux)
|
||||||
@@ -164,49 +152,42 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
set(DARWIN 1)
|
set(DARWIN 1)
|
||||||
|
|
||||||
if (XCODE_VERSION LESS 8.0.0)
|
# Architecture
|
||||||
message( FATAL_ERROR "Xcode 8.0.0 or greater is required." )
|
set(CMAKE_OSX_SYSROOT macosx10.14)
|
||||||
endif (XCODE_VERSION LESS 8.0.0)
|
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||||
message( "Building with " ${CMAKE_OSX_SYSROOT} )
|
set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS "x86_64")
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12)
|
|
||||||
|
|
||||||
|
# Build Options
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0")
|
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_DEBUG_INFORMATION_FORMAT[variant=Debug] "dwarf")
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO)
|
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_FAST_MATH NO)
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
# Deployment
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13)
|
||||||
if (USE_AVX2)
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_X86_VECTOR_INSTRUCTIONS avx2)
|
# Linking
|
||||||
elseif (USE_AVX)
|
set(CMAKE_XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING YES)
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_X86_VECTOR_INSTRUCTIONS avx)
|
|
||||||
else ()
|
# 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)
|
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")
|
# Apple Clang - Custom Compiler Flags
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf-with-dsym)
|
set(CMAKE_XCODE_ATTRIBUTE_WARNING_CFLAGS "-Wall -Wextra -Wno-reorder -Wno-sign-compare -Wno-ignored-qualifiers -Wno-unused-local-typedef -Wno-unused-parameter")
|
||||||
else (${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf)
|
|
||||||
endif (${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
|
||||||
|
|
||||||
set(ADDRESS_SIZE 64)
|
# Apple Clang - Language - C++
|
||||||
if (NOT CMAKE_OSX_ARCHITECTURES)
|
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD c++14)
|
||||||
if (ADDRESS_SIZE EQUAL 64)
|
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||||
set(CMAKE_OSX_ARCHITECTURES x86_64)
|
|
||||||
else (ADDRESS_SIZE EQUAL 64)
|
# Apple Clang - Warning Policies
|
||||||
set(CMAKE_OSX_ARCHITECTURES i386)
|
set(CMAKE_XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS YES)
|
||||||
endif (ADDRESS_SIZE EQUAL 64)
|
|
||||||
endif (NOT CMAKE_OSX_ARCHITECTURES)
|
|
||||||
|
|
||||||
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 ${ARCH}_darwin)
|
||||||
set(LL_ARCH_DIR universal-darwin)
|
set(LL_ARCH_DIR universal-darwin)
|
||||||
set(AUTOBUILD_PLATFORM_NAME "darwin" CACHE STRING "Autobuild Platform Name")
|
|
||||||
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
|
||||||
# Default deploy grid
|
# Default deploy grid
|
||||||
@@ -223,11 +204,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" "")
|
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 (NOT VIEWER_CHANNEL_GRK)
|
||||||
if (VIEWER_CHANNEL_BASE MATCHES "Test")
|
if (VIEWER_CHANNEL_BASE MATCHES "Test")
|
||||||
set(VIEWER_CHANNEL_GRK "τ")
|
set(VIEWER_CHANNEL_GRK "\\u03C4") # "τ"
|
||||||
elseif (VIEWER_CHANNEL_BASE MATCHES "Alpha")
|
elseif (VIEWER_CHANNEL_BASE MATCHES "Alpha")
|
||||||
set(VIEWER_CHANNEL_GRK "α")
|
set(VIEWER_CHANNEL_GRK "\\u03B1") # "α"
|
||||||
elseif (VIEWER_CHANNEL_BASE MATCHES "Beta")
|
elseif (VIEWER_CHANNEL_BASE MATCHES "Beta")
|
||||||
set(VIEWER_CHANNEL_GRK "β")
|
set(VIEWER_CHANNEL_GRK "\\u03B2") # "β"
|
||||||
endif ()
|
endif ()
|
||||||
endif (NOT VIEWER_CHANNEL_GRK)
|
endif (NOT VIEWER_CHANNEL_GRK)
|
||||||
|
|
||||||
@@ -245,13 +226,21 @@ set(VIEWER_CHANNEL_NOSPACE ${VIEWER_CHANNEL_ONEWORD} CACHE STRING "Prefix used f
|
|||||||
|
|
||||||
set(VIEWER_BRANDING_ID "singularity" CACHE STRING "Viewer branding id")
|
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)
|
option(ENABLE_SIGNING "Enable signing the viewer" OFF)
|
||||||
set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if necessary.")
|
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)
|
source_group("CMake Rules" FILES CMakeLists.txt)
|
||||||
|
|
||||||
mark_as_advanced(AUTOBUILD_PLATFORM_NAME)
|
|
||||||
|
|
||||||
endif(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED)
|
endif(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED)
|
||||||
|
|||||||
340
indra/cmake/run_build_test.py
Normal file
340
indra/cmake/run_build_test.py
Normal file
@@ -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 <eyeroll/>. 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 <p> ... </p> just like <tr> or <td>, but that
|
||||||
|
# introduces an unnecessary extra level of nesting. Just skip.
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# For any tag we don't recognize (notably <th>), 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 <p> in handle_starttag(), we
|
||||||
|
# must refrain from popping it for </p> 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 <td> entries of the form:
|
||||||
|
# <p>0x00000000<br />STATUS_SUCCESS</p>
|
||||||
|
# The <br/> 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 <br/> or any other
|
||||||
|
# singleton tag.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_data(self, data):
|
||||||
|
# Outside the <table> 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 <table> 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 <tr><th>...</th></tr> 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 <br/>
|
||||||
|
# or <p> ... </p> 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)
|
||||||
@@ -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$
|
|
||||||
"""
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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")
|
|
||||||
@@ -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()
|
|
||||||
@@ -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
|
|
||||||
@@ -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)
|
|
||||||
@@ -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
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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('/'))
|
|
||||||
|
|
||||||
@@ -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)
|
|
||||||
@@ -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, '<?xml version="1.0" ?><llsd><map>'
|
|
||||||
'<key>a</key><integer>2</integer></map></llsd>')
|
|
||||||
|
|
||||||
def test_request_llsd_mod_body_changes_llsd(self):
|
|
||||||
req = siesta.Request.blank('/')
|
|
||||||
req.llsd = {'a': 2}
|
|
||||||
req.body = '<?xml version="1.0" ?><llsd><integer>1337</integer></llsd>'
|
|
||||||
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()
|
|
||||||
@@ -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: '<LI><A HREF="X"> X</A>'. 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, '<li><a href="', '"> ')
|
|
||||||
# apache dir
|
|
||||||
assets += try_match (lline, 'alt="[dir]"> <a href="', '/">')
|
|
||||||
# apache file
|
|
||||||
assets += try_match (lline, 'alt="[ ]"> <a href="', '">')
|
|
||||||
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)
|
|
||||||
@@ -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 </struct> 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("""<?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall>
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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")] )
|
|
||||||
@@ -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()
|
|
||||||
@@ -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()
|
|
||||||
@@ -33,21 +33,26 @@ import filecmp
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import getopt
|
import getopt
|
||||||
import glob
|
import glob
|
||||||
|
import itertools
|
||||||
|
import operator
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
import errno
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
class ManifestError(RuntimeError):
|
class ManifestError(RuntimeError):
|
||||||
"""Use an exception more specific than generic Python 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):
|
class MissingError(ManifestError):
|
||||||
"""You specified a file that doesn't exist"""
|
"""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):
|
def path_ancestors(path):
|
||||||
drive, path = os.path.splitdrive(os.path.normpath(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'
|
CHANNEL_VENDOR_BASE = 'Singularity'
|
||||||
RELEASE_CHANNEL = CHANNEL_VENDOR_BASE + ' Release'
|
RELEASE_CHANNEL = CHANNEL_VENDOR_BASE + ' Release'
|
||||||
|
|
||||||
ARGUMENTS=[
|
BASE_ARGUMENTS=[
|
||||||
dict(name='actions',
|
dict(name='actions',
|
||||||
description="""This argument specifies the actions that are to be taken when the
|
description="""This argument specifies the actions that are to be taken when the
|
||||||
script is run. The meaningful actions are currently:
|
script is run. The meaningful actions are currently:
|
||||||
@@ -106,9 +111,17 @@ ARGUMENTS=[
|
|||||||
Example use: %(name)s --arch=i686
|
Example use: %(name)s --arch=i686
|
||||||
On Linux this would try to use Linux_i686Manifest.""",
|
On Linux this would try to use Linux_i686Manifest.""",
|
||||||
default=""),
|
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='branding_id', description="Identifier for the branding set to use.", default='singularity'),
|
||||||
dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE),
|
dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE),
|
||||||
dict(name='buildtype', description='Build type (i.e. Debug, Release, RelWithDebInfo).', default=None),
|
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',
|
dict(name='configuration',
|
||||||
description="""The build configuration used.""",
|
description="""The build configuration used.""",
|
||||||
default="Release"),
|
default="Release"),
|
||||||
@@ -116,12 +129,6 @@ ARGUMENTS=[
|
|||||||
dict(name='grid',
|
dict(name='grid',
|
||||||
description="""Which grid the client will try to connect to.""",
|
description="""Which grid the client will try to connect to.""",
|
||||||
default=None),
|
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',
|
dict(name='installer_name',
|
||||||
description=""" The name of the file that the installer should be
|
description=""" The name of the file that the installer should be
|
||||||
packaged up into. Only used on Linux at the moment.""",
|
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
|
description="""The current platform, to be used for looking up which
|
||||||
manifest class to run.""",
|
manifest class to run.""",
|
||||||
default=get_default_platform),
|
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',
|
dict(name='source',
|
||||||
description='Source directory.',
|
description='Source directory.',
|
||||||
default=DEFAULT_SRCTREE),
|
default=DEFAULT_SRCTREE),
|
||||||
dict(name='standalone',
|
dict(name='standalone',
|
||||||
description='Set to ON if this is a standalone build.',
|
description='Set to ON if this is a standalone build.',
|
||||||
default="OFF"),
|
default="OFF"),
|
||||||
dict(name='artwork', description='Artwork directory.', default=DEFAULT_SRCTREE),
|
|
||||||
dict(name='touch',
|
dict(name='touch',
|
||||||
description="""File to touch when action is finished. Touch file will
|
description="""File to touch when action is finished. Touch file will
|
||||||
contain the name of the final package in a form suitable
|
contain the name of the final package in a form suitable
|
||||||
@@ -147,20 +158,15 @@ ARGUMENTS=[
|
|||||||
default=None),
|
default=None),
|
||||||
dict(name='versionfile',
|
dict(name='versionfile',
|
||||||
description="""The name of a file containing the full version number."""),
|
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]}
|
nd = {'name':sys.argv[0]}
|
||||||
print """Usage:
|
print """Usage:
|
||||||
%(name)s [options] [destdir]
|
%(name)s [options] [destdir]
|
||||||
Options:
|
Options:
|
||||||
""" % nd
|
""" % nd
|
||||||
for arg in ARGUMENTS:
|
for arg in arguments:
|
||||||
default = arg['default']
|
default = arg['default']
|
||||||
if hasattr(default, '__call__'):
|
if hasattr(default, '__call__'):
|
||||||
default = "(computed value) \"" + str(default(srctree)) + '"'
|
default = "(computed value) \"" + str(default(srctree)) + '"'
|
||||||
@@ -171,11 +177,15 @@ def usage(srctree=""):
|
|||||||
default,
|
default,
|
||||||
arg['description'] % nd)
|
arg['description'] % nd)
|
||||||
|
|
||||||
def main():
|
def main(extra=[]):
|
||||||
## import itertools
|
|
||||||
## print ' '.join((("'%s'" % item) if ' ' in item else item)
|
## print ' '.join((("'%s'" % item) if ' ' in item else item)
|
||||||
## for item in itertools.chain([sys.executable], sys.argv))
|
## 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')
|
option_names.append('help')
|
||||||
options, remainder = getopt.getopt(sys.argv[1:], "", option_names)
|
options, remainder = getopt.getopt(sys.argv[1:], "", option_names)
|
||||||
|
|
||||||
@@ -198,11 +208,11 @@ def main():
|
|||||||
# early out for help
|
# early out for help
|
||||||
if 'help' in args:
|
if 'help' in args:
|
||||||
# *TODO: it is a huge hack to pass around the srctree like this
|
# *TODO: it is a huge hack to pass around the srctree like this
|
||||||
usage(args['source'])
|
usage(arguments, srctree=args['source'])
|
||||||
return
|
return
|
||||||
|
|
||||||
# defaults
|
# defaults
|
||||||
for arg in ARGUMENTS:
|
for arg in arguments:
|
||||||
if arg['name'] not in args:
|
if arg['name'] not in args:
|
||||||
default = arg['default']
|
default = arg['default']
|
||||||
if hasattr(default, '__call__'):
|
if hasattr(default, '__call__'):
|
||||||
@@ -231,101 +241,68 @@ def main():
|
|||||||
print "Option:", opt, "=", args[opt]
|
print "Option:", opt, "=", args[opt]
|
||||||
|
|
||||||
# pass in sourceid as an argument now instead of an environment variable
|
# pass in sourceid as an argument now instead of an environment variable
|
||||||
try:
|
args['sourceid'] = os.environ.get("sourceid", "")
|
||||||
args['sourceid'] = os.environ["sourceid"]
|
|
||||||
except KeyError:
|
|
||||||
args['sourceid'] = ""
|
|
||||||
|
|
||||||
# Build base package.
|
# Build base package.
|
||||||
touch = args.get('touch')
|
touch = args.get('touch')
|
||||||
if touch:
|
if touch:
|
||||||
print 'Creating base package'
|
print '================ Creating base package'
|
||||||
args['package_id'] = "" # base package has no package ID
|
else:
|
||||||
|
print '================ Starting base copy'
|
||||||
wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)
|
wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)
|
||||||
wm.do(*args['actions'])
|
wm.do(*args['actions'])
|
||||||
# Store package file for later if making touched file.
|
# Store package file for later if making touched file.
|
||||||
base_package_file = ""
|
base_package_file = ""
|
||||||
if touch:
|
if touch:
|
||||||
print 'Created base package ', wm.package_file
|
print '================ Created base package ', wm.package_file
|
||||||
base_package_file = "" + wm.package_file
|
base_package_file = "" + wm.package_file
|
||||||
|
else:
|
||||||
|
print '================ Finished base copy'
|
||||||
|
|
||||||
# handle multiple packages if set
|
# handle multiple packages if set
|
||||||
try:
|
# ''.split() produces empty list
|
||||||
additional_packages = os.environ["additional_packages"]
|
additional_packages = os.environ.get("additional_packages", "").split()
|
||||||
except KeyError:
|
|
||||||
additional_packages = ""
|
|
||||||
if additional_packages:
|
if additional_packages:
|
||||||
# Determine destination prefix / suffix for additional packages.
|
# Determine destination prefix / suffix for additional packages.
|
||||||
base_dest_postfix = args['dest']
|
base_dest_parts = list(os.path.split(args['dest']))
|
||||||
base_dest_prefix = ""
|
base_dest_parts.insert(-1, "{}")
|
||||||
base_dest_parts = args['dest'].split(os.sep)
|
base_dest_template = os.path.join(*base_dest_parts)
|
||||||
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
|
|
||||||
# Determine touched prefix / suffix for additional packages.
|
# Determine touched prefix / suffix for additional packages.
|
||||||
base_touch_postfix = ""
|
|
||||||
base_touch_prefix = ""
|
|
||||||
if touch:
|
if touch:
|
||||||
base_touch_postfix = touch
|
base_touch_parts = list(os.path.split(touch))
|
||||||
base_touch_parts = touch.split('/')
|
# 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 "arwin" in args['platform']:
|
||||||
if len(base_touch_parts) > 1:
|
base_touch_parts.insert(-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
|
|
||||||
else:
|
else:
|
||||||
if len(base_touch_parts) > 2:
|
base_touch_parts.insert(-2, "{}")
|
||||||
base_touch_postfix = base_touch_parts[len(base_touch_parts) - 2] + '/' + base_touch_parts[len(base_touch_parts) - 1]
|
base_touch_template = os.path.join(*base_touch_parts)
|
||||||
base_touch_prefix = base_touch_parts[0]
|
for package_id in additional_packages:
|
||||||
i = 1
|
args['channel_suffix'] = os.environ.get(package_id + "_viewer_channel_suffix")
|
||||||
while i < len(base_touch_parts) - 2:
|
args['sourceid'] = os.environ.get(package_id + "_sourceid")
|
||||||
base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i]
|
args['dest'] = base_dest_template.format(package_id)
|
||||||
i = i + 1
|
if touch:
|
||||||
# Store base channel name.
|
print '================ Creating additional package for "', package_id, '" in ', args['dest']
|
||||||
base_channel_name = args['channel']
|
else:
|
||||||
# Build each additional package.
|
print '================ Starting additional copy for "', package_id, '" in ', args['dest']
|
||||||
package_id_list = additional_packages.split(" ")
|
|
||||||
args['channel'] = base_channel_name
|
|
||||||
for package_id in package_id_list:
|
|
||||||
try:
|
try:
|
||||||
if package_id + "_viewer_channel_suffix" in os.environ:
|
wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)
|
||||||
args['channel_suffix'] = os.environ[package_id + "_viewer_channel_suffix"]
|
wm.do(*args['actions'])
|
||||||
else:
|
except Exception as err:
|
||||||
args['channel_suffix'] = None
|
sys.exit(str(err))
|
||||||
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
|
|
||||||
if touch:
|
if touch:
|
||||||
print 'Creating additional package for "', package_id, '" in ', args['dest']
|
print '================ Created additional package ', wm.package_file, ' for ', package_id
|
||||||
wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args)
|
with open(base_touch_template.format(package_id), 'w') as fp:
|
||||||
wm.do(*args['actions'])
|
fp.write('set package_file=%s\n' % wm.package_file)
|
||||||
if touch:
|
else:
|
||||||
print 'Created additional package ', wm.package_file, ' for ', package_id
|
print '================ Finished additional copy "', package_id, '" in ', args['dest']
|
||||||
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()
|
|
||||||
|
|
||||||
# Write out the package file in this format, so that it can easily be called
|
# 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...
|
# and used in a .bat file - yeah, it sucks, but this is the simplest...
|
||||||
touch = args.get('touch')
|
|
||||||
if touch:
|
if touch:
|
||||||
fp = open(touch, 'w')
|
with open(touch, 'w') as fp:
|
||||||
fp.write('set package_file=%s\n' % base_package_file)
|
fp.write('set package_file=%s\n' % base_package_file)
|
||||||
fp.close()
|
|
||||||
print 'touched', touch
|
print 'touched', touch
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -371,22 +348,113 @@ class LLManifest(object):
|
|||||||
in the file list by path()."""
|
in the file list by path()."""
|
||||||
self.excludes.append(glob)
|
self.excludes.append(glob)
|
||||||
|
|
||||||
def prefix(self, src='', build=None, dst=None):
|
def prefix(self, src='', build='', dst='', src_dst=None):
|
||||||
""" Pushes a prefix onto the stack. Until end_prefix is
|
"""
|
||||||
called, all relevant method calls (esp. to path()) will prefix
|
Usage:
|
||||||
paths with the entire prefix stack. Source and destination
|
|
||||||
prefixes can be different, though if only one is provided they
|
with self.prefix(...args as described...):
|
||||||
are both equal. To specify a no-op, use an empty string, not
|
self.path(...)
|
||||||
None."""
|
|
||||||
if dst is None:
|
For the duration of the 'with' block, pushes a prefix onto the stack.
|
||||||
dst = src
|
Within that block, all relevant method calls (esp. to path()) will
|
||||||
if build is None:
|
prefix paths with the entire prefix stack. Source and destination
|
||||||
build = src
|
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.src_prefix.append(src)
|
||||||
self.artwork_prefix.append(src)
|
self.artwork_prefix.append(src)
|
||||||
self.build_prefix.append(build)
|
self.build_prefix.append(build)
|
||||||
self.dst_prefix.append(dst)
|
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):
|
def end_prefix(self, descr=None):
|
||||||
"""Pops a prefix off the stack. If given an argument, checks
|
"""Pops a prefix off the stack. If given an argument, checks
|
||||||
@@ -433,6 +501,19 @@ class LLManifest(object):
|
|||||||
relative to the destination directory."""
|
relative to the destination directory."""
|
||||||
return os.path.join(self.get_dst_prefix(), relpath)
|
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):
|
def ensure_src_dir(self, reldir):
|
||||||
"""Construct the path for a directory relative to the
|
"""Construct the path for a directory relative to the
|
||||||
source path, and ensures that it exists. Returns the
|
source path, and ensures that it exists. Returns the
|
||||||
@@ -450,29 +531,17 @@ class LLManifest(object):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
def run_command(self, command):
|
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
|
Runs an external command.
|
||||||
debugging/informational purposes, prints out the command's
|
Raises ManifestError exception if the command returns a nonzero status.
|
||||||
output as it is received."""
|
"""
|
||||||
print "Running command:", command
|
print "Running command:", command
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
child = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
try:
|
||||||
shell=True)
|
subprocess.check_call(command)
|
||||||
lines = []
|
except subprocess.CalledProcessError as err:
|
||||||
while True:
|
raise ManifestError( "Command %s returned non-zero status (%s)"
|
||||||
lines.append(child.stdout.readline())
|
% (command, err.returncode) )
|
||||||
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
|
|
||||||
|
|
||||||
def created_path(self, path):
|
def created_path(self, path):
|
||||||
""" Declare that you've created a path in order to
|
""" 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):
|
def put_in_file(self, contents, dst, src=None):
|
||||||
# write contents as dst
|
# write contents as dst
|
||||||
dst_path = self.dst_path_of(dst)
|
dst_path = self.dst_path_of(dst)
|
||||||
|
self.cmakedirs(os.path.dirname(dst_path))
|
||||||
f = open(dst_path, "wb")
|
f = open(dst_path, "wb")
|
||||||
try:
|
try:
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
@@ -546,9 +616,16 @@ class LLManifest(object):
|
|||||||
# *TODO is this gonna be useful?
|
# *TODO is this gonna be useful?
|
||||||
print "Cleaning up " + c
|
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):
|
def process_file(self, src, dst):
|
||||||
if self.includes(src, dst):
|
if self.includes(src, dst):
|
||||||
# print src, "=>", dst
|
|
||||||
for action in self.actions:
|
for action in self.actions:
|
||||||
methodname = action + "_action"
|
methodname = action + "_action"
|
||||||
method = getattr(self, methodname, None)
|
method = getattr(self, methodname, None)
|
||||||
@@ -573,10 +650,7 @@ class LLManifest(object):
|
|||||||
for name in names:
|
for name in names:
|
||||||
srcname = os.path.join(src, name)
|
srcname = os.path.join(src, name)
|
||||||
dstname = os.path.join(dst, name)
|
dstname = os.path.join(dst, name)
|
||||||
if os.path.isdir(srcname):
|
count += self.process_either(srcname, dstname)
|
||||||
count += self.process_directory(srcname, dstname)
|
|
||||||
else:
|
|
||||||
count += self.process_file(srcname, dstname)
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
def includes(self, src, dst):
|
def includes(self, src, dst):
|
||||||
@@ -616,16 +690,21 @@ class LLManifest(object):
|
|||||||
# Don't recopy file if it's up-to-date.
|
# Don't recopy file if it's up-to-date.
|
||||||
# If we seem to be not not overwriting files that have been
|
# If we seem to be not not overwriting files that have been
|
||||||
# updated, set the last arg to False, but it will take longer.
|
# 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):
|
if os.path.exists(dst) and filecmp.cmp(src, dst, True):
|
||||||
|
## print "{} (skipping, {} exists)".format(src, reldst)
|
||||||
return
|
return
|
||||||
# only copy if it's not excluded
|
# only copy if it's not excluded
|
||||||
if self.includes(src, dst):
|
if self.includes(src, dst):
|
||||||
try:
|
try:
|
||||||
os.unlink(dst)
|
os.unlink(dst)
|
||||||
except OSError, err:
|
except OSError as err:
|
||||||
if err.errno != errno.ENOENT:
|
if err.errno != errno.ENOENT:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
## print "{} => {}".format(src, reldst)
|
||||||
shutil.copy2(src, dst)
|
shutil.copy2(src, dst)
|
||||||
|
|
||||||
def ccopytree(self, src, dst):
|
def ccopytree(self, src, dst):
|
||||||
@@ -643,7 +722,7 @@ class LLManifest(object):
|
|||||||
dstname = os.path.join(dst, name)
|
dstname = os.path.join(dst, name)
|
||||||
try:
|
try:
|
||||||
self.ccopymumble(srcname, dstname)
|
self.ccopymumble(srcname, dstname)
|
||||||
except (IOError, os.error), why:
|
except (IOError, os.error) as why:
|
||||||
errors.append((srcname, dstname, why))
|
errors.append((srcname, dstname, why))
|
||||||
if errors:
|
if errors:
|
||||||
raise ManifestError, errors
|
raise ManifestError, errors
|
||||||
@@ -724,13 +803,13 @@ class LLManifest(object):
|
|||||||
return self.path(os.path.join(path, file), file)
|
return self.path(os.path.join(path, file), file)
|
||||||
|
|
||||||
def path(self, src, dst=None):
|
def path(self, src, dst=None):
|
||||||
sys.stdout.write("Processing %s => %s ... " % (src, dst))
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
if src == None:
|
if src == None:
|
||||||
raise ManifestError("No source file, dst is " + dst)
|
raise ManifestError("No source file, dst is " + dst)
|
||||||
if dst == None:
|
if dst == None:
|
||||||
dst = src
|
dst = src
|
||||||
dst = os.path.join(self.get_dst_prefix(), dst)
|
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):
|
def try_path(src):
|
||||||
# expand globs
|
# expand globs
|
||||||
@@ -743,29 +822,21 @@ class LLManifest(object):
|
|||||||
# if we're specifying a single path (not a glob),
|
# if we're specifying a single path (not a glob),
|
||||||
# we should error out if it doesn't exist
|
# we should error out if it doesn't exist
|
||||||
self.check_file_exists(src)
|
self.check_file_exists(src)
|
||||||
# if it's a directory, recurse through it
|
count += self.process_either(src, dst)
|
||||||
if os.path.isdir(src):
|
|
||||||
count += self.process_directory(src, dst)
|
|
||||||
else:
|
|
||||||
count += self.process_file(src, dst)
|
|
||||||
return count
|
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:
|
try:
|
||||||
count = try_path(os.path.join(pfx, src))
|
count = try_path(os.path.join(pfx, src))
|
||||||
except MissingError:
|
except MissingError:
|
||||||
# If src isn't a wildcard, and if that file doesn't exist in
|
tried.append(pfx)
|
||||||
# this pfx, try next pfx.
|
if not try_prefixes:
|
||||||
count = 0
|
# no more prefixes left to try
|
||||||
continue
|
print "unable to find '%s'; looked in:\n %s" % (src, '\n '.join(tried))
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
print "%d files" % count
|
print "%d files" % count
|
||||||
|
|
||||||
# Let caller check whether we processed as many files as expected. In
|
# Let caller check whether we processed as many files as expected. In
|
||||||
|
|||||||
@@ -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()
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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:
|
|
||||||
# <llsd>...
|
|
||||||
# <map>
|
|
||||||
# <key>user_notes</key>
|
|
||||||
# <array>
|
|
||||||
# <array> <!-- row 1 for VALUES -->
|
|
||||||
# <string>...</string>
|
|
||||||
# <string>...</string>
|
|
||||||
# <string>...</string>
|
|
||||||
# </array>
|
|
||||||
# ...
|
|
||||||
# </array>
|
|
||||||
# </map>
|
|
||||||
# ... </llsd>
|
|
||||||
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:]
|
|
||||||
@@ -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)
|
|
||||||
@@ -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 '<ds>' 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 <rrd>
|
|
||||||
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 <rrd><lastupdate>
|
|
||||||
self._rrd_capture = True
|
|
||||||
self._rrd_chars = ""
|
|
||||||
elif name == "step":
|
|
||||||
self._rrd_parse_state = 3 # In <rrd><step>
|
|
||||||
self._rrd_capture = True
|
|
||||||
self._rrd_chars = ""
|
|
||||||
elif name == "ds":
|
|
||||||
self._rrd_parse_state = 4 # In <rrd><ds>
|
|
||||||
self._rrd_ds_val = {}
|
|
||||||
self._rrd_chars = ""
|
|
||||||
elif name == "rra":
|
|
||||||
self._rrd_parse_state = 5 # In <rrd><rra>
|
|
||||||
elif self._rrd_level == 3:
|
|
||||||
if self._rrd_parse_state == 4:
|
|
||||||
if name == "name":
|
|
||||||
self._rrd_parse_state = 6 # In <rrd><ds><name>
|
|
||||||
self._rrd_capture = True
|
|
||||||
self._rrd_chars = ""
|
|
||||||
elif name == "type":
|
|
||||||
self._rrd_parse_state = 7 # In <rrd><ds><type>
|
|
||||||
self._rrd_capture = True
|
|
||||||
self._rrd_chars = ""
|
|
||||||
elif self._rrd_parse_state == 5:
|
|
||||||
if name == "database":
|
|
||||||
self._rrd_parse_state = 8 # In <rrd><rra><database>
|
|
||||||
elif self._rrd_level == 4:
|
|
||||||
if self._rrd_parse_state == 8:
|
|
||||||
if name == "row":
|
|
||||||
self._rrd_parse_state = 9 # In <rrd><rra><database><row>
|
|
||||||
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 <rrd><rra><database><row><v>
|
|
||||||
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 <rrd><rra><database><row>
|
|
||||||
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 <rrd><rra><database>
|
|
||||||
elif self._rrd_parse_state == 8:
|
|
||||||
if self._rrd_level == 3:
|
|
||||||
self._rrd_parse_state = 5 # In <rrd><rra>
|
|
||||||
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 <rrd><ds>
|
|
||||||
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 <rrd><ds>
|
|
||||||
elif self._rrd_parse_state == 5:
|
|
||||||
if self._rrd_level == 2:
|
|
||||||
self._rrd_parse_state = 1 # In <rrd>
|
|
||||||
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 <rrd>
|
|
||||||
elif self._rrd_parse_state == 3:
|
|
||||||
if self._rrd_level == 2:
|
|
||||||
self.rrd_step = long(self._rrd_chars)
|
|
||||||
self._rrd_parse_state = 1 # In <rrd>
|
|
||||||
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 <rrd>
|
|
||||||
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())
|
|
||||||
@@ -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())
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""\
|
|
||||||
@file simperf_proc_interface.py
|
|
||||||
@brief Utility to extract log messages from *.<pid>.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 *.<pid>.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 <type>_proc.<pid>.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 *.<pid>.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
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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 <ping@zesty.ca>'
|
|
||||||
__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')
|
|
||||||
@@ -436,7 +436,7 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~MyMicroAllocator(void)
|
virtual ~MyMicroAllocator()
|
||||||
{
|
{
|
||||||
if ( mMicroChunks )
|
if ( mMicroChunks )
|
||||||
{
|
{
|
||||||
@@ -746,7 +746,7 @@ public:
|
|||||||
mMicro = createMicroAllocator(this,defaultChunkSize);
|
mMicro = createMicroAllocator(this,defaultChunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
~MyHeapManager(void)
|
virtual ~MyHeapManager()
|
||||||
{
|
{
|
||||||
releaseMicroAllocator(mMicro);
|
releaseMicroAllocator(mMicro);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -902,7 +902,7 @@ bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FMOD_MODE base_mode = FMOD_LOOP_NORMAL;
|
FMOD_MODE base_mode = FMOD_LOOP_NORMAL;
|
||||||
FMOD_CREATESOUNDEXINFO exinfo = {0};
|
FMOD_CREATESOUNDEXINFO exinfo = { };
|
||||||
exinfo.cbsize = sizeof(exinfo);
|
exinfo.cbsize = sizeof(exinfo);
|
||||||
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading.
|
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading.
|
||||||
// Load up the wav file into an fmod sample
|
// Load up the wav file into an fmod sample
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) :
|
|||||||
|
|
||||||
Check_FMOD_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup");
|
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;
|
dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION;
|
||||||
strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name));
|
strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name));
|
||||||
dspdesc.numoutputbuffers = 1;
|
dspdesc.numoutputbuffers = 1;
|
||||||
@@ -632,7 +632,7 @@ FMOD_RESULT LLAudioStreamManagerFMODSTUDIO::getOpenState(FMOD_OPENSTATE& state,
|
|||||||
void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
|
void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
|
||||||
{
|
{
|
||||||
Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize");
|
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.cbSize=sizeof(settings);
|
||||||
settings.defaultDecodeBufferSize = decodebuffertime;//ms
|
settings.defaultDecodeBufferSize = decodebuffertime;//ms
|
||||||
Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings");
|
Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings");
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const int LL_ERR_NOERR = 0;
|
|||||||
|
|
||||||
#endif // !_DEBUG
|
#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
|
#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
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
#ifndef LL_LLINSTANCETRACKER_H
|
#ifndef LL_LLINSTANCETRACKER_H
|
||||||
#define LL_LLINSTANCETRACKER_H
|
#define LL_LLINSTANCETRACKER_H
|
||||||
|
|
||||||
#include <map>
|
#include <boost/unordered_map.hpp>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
|
||||||
#include "llstringtable.h"
|
#include "llstringtable.h"
|
||||||
@@ -89,8 +89,7 @@ template<typename T, typename KEY = T*>
|
|||||||
class LLInstanceTracker : public LLInstanceTrackerBase
|
class LLInstanceTracker : public LLInstanceTrackerBase
|
||||||
{
|
{
|
||||||
typedef LLInstanceTracker<T, KEY> MyT;
|
typedef LLInstanceTracker<T, KEY> MyT;
|
||||||
typedef typename std::map<KEY, T*> InstanceMap;
|
typedef typename boost::unordered_map<KEY, T*> InstanceMap;
|
||||||
typedef typename InstanceMap::iterator::difference_type difference_type;
|
|
||||||
struct StaticData: public StaticBase
|
struct StaticData: public StaticBase
|
||||||
{
|
{
|
||||||
InstanceMap sMap;
|
InstanceMap sMap;
|
||||||
@@ -121,10 +120,6 @@ public:
|
|||||||
|
|
||||||
void increment() { mIterator++; }
|
void increment() { mIterator++; }
|
||||||
void decrement() { --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
|
bool equal(instance_iter const& other) const
|
||||||
{
|
{
|
||||||
return mIterator == other.mIterator;
|
return mIterator == other.mIterator;
|
||||||
@@ -166,10 +161,6 @@ public:
|
|||||||
|
|
||||||
void increment() { mIterator++; }
|
void increment() { mIterator++; }
|
||||||
void decrement() { --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
|
bool equal(key_iter const& other) const
|
||||||
{
|
{
|
||||||
return mIterator == other.mIterator;
|
return mIterator == other.mIterator;
|
||||||
|
|||||||
@@ -144,6 +144,11 @@ LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data,
|
|||||||
/// is_approx_equal_fraction().
|
/// is_approx_equal_fraction().
|
||||||
LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
|
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
|
// Simple function to copy data out of input & output iterators if
|
||||||
// there is no need for casting.
|
// there is no need for casting.
|
||||||
template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
|
template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
|
||||||
|
|||||||
@@ -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;
|
w_out[real_output_str_len] = 0;
|
||||||
|
|
||||||
return w_out;
|
return w_out;
|
||||||
return {&w_out[0]};
|
// return {&w_out[0]};
|
||||||
}
|
}
|
||||||
|
|
||||||
S32 wchartchars_to_llwchar(const std::wstring::value_type* inchars, llwchar* outchar)
|
S32 wchartchars_to_llwchar(const std::wstring::value_type* inchars, llwchar* outchar)
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ inline U32 LLUUID::getCRC32() const
|
|||||||
// </alchemy>
|
// </alchemy>
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLUUID>{}, "LLUUID must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLUUID>::value, "LLUUID must be a trivially copyable type");
|
||||||
|
|
||||||
typedef std::vector<LLUUID> uuid_vec_t;
|
typedef std::vector<LLUUID> uuid_vec_t;
|
||||||
typedef boost::unordered_set<LLUUID> uuid_set_t;
|
typedef boost::unordered_set<LLUUID> uuid_set_t;
|
||||||
|
|||||||
@@ -108,4 +108,16 @@ typedef U8 LLPCode;
|
|||||||
|
|
||||||
#define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) )
|
#define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) )
|
||||||
|
|
||||||
|
#if __GNUG__ && __GNUC__ < 5
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
struct is_trivially_copyable
|
||||||
|
{
|
||||||
|
static const bool value = __has_trivial_copy(T);
|
||||||
|
operator bool() { return value; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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));
|
moonquat = convert_azimuth_and_altitude_to_quat(altitude + (F_PI * 0.125f), azimuth + (F_PI * 0.125f));
|
||||||
|
|
||||||
// Magic constants copied form dfltsetting.xml
|
// Magic constants copied form dfltsetting.xml
|
||||||
dfltsetting[SETTING_CLOUD_COLOR] = LLColor4(0.4099, 0.4099, 0.4099, 0.0).getValue();
|
dfltsetting[SETTING_CLOUD_COLOR] = LLColor4(0.4099f, 0.4099f, 0.4099f, 0.0f).getValue();
|
||||||
dfltsetting[SETTING_CLOUD_POS_DENSITY1] = LLColor4(1.0000, 0.5260, 1.0000, 0.0).getValue();
|
dfltsetting[SETTING_CLOUD_POS_DENSITY1] = LLColor4(1.0000f, 0.5260f, 1.0000f, 0.0f).getValue();
|
||||||
dfltsetting[SETTING_CLOUD_POS_DENSITY2] = LLColor4(1.0000, 0.5260, 1.0000, 0.0).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_SCALE] = LLSD::Real(0.4199);
|
||||||
dfltsetting[SETTING_CLOUD_SCROLL_RATE] = LLSDArray(0.0f)(0.0f);
|
dfltsetting[SETTING_CLOUD_SCROLL_RATE] = LLSDArray(0.0f)(0.0f);
|
||||||
dfltsetting[SETTING_CLOUD_SHADOW] = LLSD::Real(0.2699);
|
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_OFFSET] = LLSD::Real(0.96f);
|
||||||
dfltsetting[SETTING_DOME_RADIUS] = LLSD::Real(15000.f);
|
dfltsetting[SETTING_DOME_RADIUS] = LLSD::Real(15000.f);
|
||||||
dfltsetting[SETTING_GAMMA] = LLSD::Real(1.0);
|
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_MAX_Y] = LLSD::Real(1605);
|
||||||
dfltsetting[SETTING_MOON_ROTATION] = moonquat.getValue();
|
dfltsetting[SETTING_MOON_ROTATION] = moonquat.getValue();
|
||||||
dfltsetting[SETTING_MOON_BRIGHTNESS] = LLSD::Real(0.5f);
|
dfltsetting[SETTING_MOON_BRIGHTNESS] = LLSD::Real(0.5f);
|
||||||
|
|
||||||
dfltsetting[SETTING_STAR_BRIGHTNESS] = LLSD::Real(256.0000);
|
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_SUN_ROTATION] = sunquat.getValue();
|
||||||
|
|
||||||
dfltsetting[SETTING_BLOOM_TEXTUREID] = GetDefaultBloomTextureId();
|
dfltsetting[SETTING_BLOOM_TEXTUREID] = GetDefaultBloomTextureId();
|
||||||
|
|||||||
@@ -127,6 +127,6 @@ public:
|
|||||||
inline bool isOkRotation() const;
|
inline bool isOkRotation() const;
|
||||||
} LL_ALIGN_POSTFIX(16);
|
} LL_ALIGN_POSTFIX(16);
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLMatrix3a>{}, "LLMatrix3a must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLMatrix3a>::value, "LLMatrix3a must be a trivially copyable type");
|
||||||
static_assert(std::is_trivially_copyable<LLRotation>{}, "LLRotation must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLRotation>::value, "LLRotation must be a trivially copyable type");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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);
|
void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents);
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLMatrix4a>{}, "LLMatrix4a must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLMatrix4a>::value, "LLMatrix4a must be a trivially copyable type");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ inline void LLQuaternion::setValue(const LLSD& sd)
|
|||||||
mQ[3] = sd[3].asReal();
|
mQ[3] = sd[3].asReal();
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLQuaternion>{}, "LLQuaternion must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLQuaternion>::value, "LLQuaternion must be a trivially copyable type");
|
||||||
|
|
||||||
// checker
|
// checker
|
||||||
inline BOOL LLQuaternion::isFinite() const
|
inline BOOL LLQuaternion::isFinite() const
|
||||||
|
|||||||
@@ -105,6 +105,6 @@ protected:
|
|||||||
|
|
||||||
} LL_ALIGN_POSTFIX(16);
|
} LL_ALIGN_POSTFIX(16);
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLQuaternion2>{}, "LLQuaternion2 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLQuaternion2>::value, "LLQuaternion2 must be a trivially copyable type");
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ private:
|
|||||||
LLQuad mQ;
|
LLQuad mQ;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLBool32>{}, "LLBool32 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLBool32>::value, "LLBool32 must be a trivially copyable type");
|
||||||
static_assert(std::is_trivially_copyable<LLSimdScalar>{}, "LLSimdScalar must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLSimdScalar>::value, "LLSimdScalar must be a trivially copyable type");
|
||||||
|
|
||||||
#endif //LL_SIMD_TYPES_H
|
#endif //LL_SIMD_TYPES_H
|
||||||
|
|||||||
@@ -346,5 +346,5 @@ inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLVector4a>{}, "LLVector4a must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLVector4a>::value, "LLVector4a must be a trivially copyable type");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ class LLMatrix3
|
|||||||
friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a
|
friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLMatrix3>{}, "LLMatrix3 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLMatrix3>::value, "LLMatrix3 must be a trivially copyable type");
|
||||||
|
|
||||||
inline LLMatrix3::LLMatrix3(void)
|
inline LLMatrix3::LLMatrix3(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ public:
|
|||||||
friend std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a); // Stream a
|
friend std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a); // Stream a
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLMatrix4>{}, "LLMatrix4 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLMatrix4>::value, "LLMatrix4 must be a trivially copyable type");
|
||||||
|
|
||||||
inline const LLMatrix4& LLMatrix4::setIdentity()
|
inline const LLMatrix4& LLMatrix4::setIdentity()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class LLVector2
|
|||||||
friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a
|
friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLVector2>{}, "LLVector2 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLVector2>::value, "LLVector2 must be a trivially copyable type");
|
||||||
|
|
||||||
// Non-member functions
|
// Non-member functions
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public:
|
|||||||
inline void exp(); // Do an exponential on the color
|
inline void exp(); // Do an exponential on the color
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLColor3>{}, "LLColor3 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLColor3>::value, "LLColor3 must be a trivially copyable type");
|
||||||
|
|
||||||
LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u);
|
LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u);
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class LLVector3d
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLVector3d>{}, "LLVector3d must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLVector3d>::value, "LLVector3d must be a trivially copyable type");
|
||||||
|
|
||||||
typedef LLVector3d LLGlobalVec;
|
typedef LLVector3d LLGlobalVec;
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ class LLVector3
|
|||||||
static BOOL parseVector3(const std::string& buf, LLVector3* value);
|
static BOOL parseVector3(const std::string& buf, LLVector3* value);
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLVector3>{}, "LLVector3 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLVector3>::value, "LLVector3 must be a trivially copyable type");
|
||||||
|
|
||||||
typedef LLVector3 LLSimLocalVec;
|
typedef LLVector3 LLSimLocalVec;
|
||||||
|
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ class LLColor4
|
|||||||
inline void clamp();
|
inline void clamp();
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLColor4>{}, "LLColor4 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLColor4>::value, "LLColor4 must be a trivially copyable type");
|
||||||
|
|
||||||
// Non-member functions
|
// Non-member functions
|
||||||
F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b
|
F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public:
|
|||||||
static LLColor4U blue;
|
static LLColor4U blue;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLColor4U>{}, "LLColor4U must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLColor4U>::value, "LLColor4U must be a trivially copyable type");
|
||||||
|
|
||||||
// Non-member functions
|
// Non-member functions
|
||||||
F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b
|
F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class LLVector4
|
|||||||
friend LLVector4 operator-(const LLVector4 &a); // Return vector -a
|
friend LLVector4 operator-(const LLVector4 &a); // Return vector -a
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<LLVector4>{}, "LLVector4 must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLVector4>::value, "LLVector4 must be a trivially copyable type");
|
||||||
|
|
||||||
// Non-member functions
|
// Non-member functions
|
||||||
F32 angle_between(const LLVector4 &a, const LLVector4 &b); // Returns angle (radians) between a and b
|
F32 angle_between(const LLVector4 &a, const LLVector4 &b); // Returns angle (radians) between a and b
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include "llstl.h"
|
#include "llstl.h"
|
||||||
#include "llindexedvector.h"
|
#include "llindexedvector.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
extern U32 sMsgDataAllocSize;
|
extern U32 sMsgDataAllocSize;
|
||||||
extern U32 sMsgdataAllocCount;
|
extern U32 sMsgdataAllocCount;
|
||||||
class LLMsgVarData
|
class LLMsgVarData
|
||||||
@@ -131,7 +133,7 @@ public:
|
|||||||
void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1);
|
void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef LLIndexedVector<LLMsgBlkData*, char*> msg_blk_data_map_t;
|
typedef std::unordered_map<char*, LLMsgBlkData*> msg_blk_data_map_t;
|
||||||
msg_blk_data_map_t mMemberBlocks;
|
msg_blk_data_map_t mMemberBlocks;
|
||||||
char *mName;
|
char *mName;
|
||||||
S32 mTotalSize;
|
S32 mTotalSize;
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ const U32 ESTATE_ACCESS_MANAGER_ADD = 1 << 8;
|
|||||||
const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9;
|
const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9;
|
||||||
const U32 ESTATE_ACCESS_NO_REPLY = 1 << 10;
|
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_ACCESS_IDS = 500; // max for access, banned
|
||||||
const S32 ESTATE_MAX_GROUP_IDS = (S32) ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET;
|
const S32 ESTATE_MAX_GROUP_IDS = (S32) ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ private:
|
|||||||
} ;
|
} ;
|
||||||
|
|
||||||
static_assert(sizeof(LLMaterialID) == MATERIAL_ID_SIZE, "LLMaterialID must be sizeof(mID)");
|
static_assert(sizeof(LLMaterialID) == MATERIAL_ID_SIZE, "LLMaterialID must be sizeof(mID)");
|
||||||
static_assert(std::is_trivially_copyable<LLMaterialID>{}, "LLMaterialID must be a trivially copyable type");
|
static_assert(std::is_trivially_copyable<LLMaterialID>::value, "LLMaterialID must be a trivially copyable type");
|
||||||
|
|
||||||
#endif // LL_LLMATERIALID_H
|
#endif // LL_LLMATERIALID_H
|
||||||
|
|
||||||
|
|||||||
@@ -393,4 +393,4 @@ void LLGLTexture::setTexelsPerImage()
|
|||||||
S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT);
|
S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT);
|
||||||
S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT);
|
S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT);
|
||||||
mTexelsPerImage = (F32)fullwidth * fullheight;
|
mTexelsPerImage = (F32)fullwidth * fullheight;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5264,30 +5264,30 @@ void LLPieMenu::draw()
|
|||||||
mHoverIndex = -1;
|
mHoverIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
F32 width = (F32) getRect().getWidth();
|
const auto& rect = getRect();
|
||||||
F32 height = (F32) getRect().getHeight();
|
// correct for non-square pixels
|
||||||
mCurRadius = PIE_SCALE_FACTOR * llmax( width/2, height/2 );
|
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;
|
mOuterRingAlpha = mUseInfiniteRadius ? 0.f : 1.f;
|
||||||
if (mShrinkBorderTimer.getStarted())
|
if (mShrinkBorderTimer.getStarted())
|
||||||
{
|
{
|
||||||
mOuterRingAlpha = clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 0.f, 1.f);
|
const auto& elapsed = mShrinkBorderTimer.getElapsedTimeF32();
|
||||||
mCurRadius *= clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 1.f, 1.f / PIE_SCALE_FACTOR);
|
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.pushUIMatrix();
|
||||||
{
|
{
|
||||||
gGL.translateUI(center_x, center_y, 0.f);
|
gGL.translateUI(center_x, center_y, 0.f);
|
||||||
|
|
||||||
F32 line_width = LLUI::sConfigGroup->getF32("PieMenuLineWidth");
|
static const LLUICachedControl<F32> line_width("PieMenuLineWidth");
|
||||||
LLColor4 line_color = LLUI::sColorsGroup->getColor("PieMenuLineColor");
|
static const LLCachedControl<LLColor4> line_color(*LLUI::sColorsGroup, "PieMenuLineColor");
|
||||||
LLColor4 bg_color = LLUI::sColorsGroup->getColor("PieMenuBgColor");
|
static const LLCachedControl<LLColor4> bg_color(*LLUI::sColorsGroup, "PieMenuBgColor");
|
||||||
LLColor4 selected_color = LLUI::sColorsGroup->getColor("PieMenuSelectedColor");
|
static const LLCachedControl<LLColor4> selected_color(*LLUI::sColorsGroup, "PieMenuSelectedColor");
|
||||||
|
|
||||||
// main body
|
// main body
|
||||||
LLColor4 outer_color = bg_color;
|
LLColor4 outer_color = bg_color;
|
||||||
@@ -5297,7 +5297,7 @@ void LLPieMenu::draw()
|
|||||||
// selected wedge
|
// selected wedge
|
||||||
if (mHoverItem)
|
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 start_radians = (mHoverIndex * arc_size) - (arc_size * 0.5f);
|
||||||
F32 end_radians = start_radians + arc_size;
|
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 );
|
gl_washer_spokes_2d( mCurRadius, (F32)PIE_CENTER_SIZE, 8, line_color, outer_color );
|
||||||
|
|
||||||
// inner circle
|
// inner circle
|
||||||
gGL.color4fv( line_color.mV );
|
gGL.color4fv( line_color().mV );
|
||||||
gl_circle_2d( 0, 0, (F32)PIE_CENTER_SIZE, steps, FALSE );
|
gl_circle_2d( 0, 0, (F32)PIE_CENTER_SIZE, steps, FALSE );
|
||||||
|
|
||||||
// outer circle
|
// outer circle
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ std::vector<LLMenuGL*> LLScrollListCtrl::sMenus = {}; // List menus that recur,
|
|||||||
// local structures & classes.
|
// local structures & classes.
|
||||||
struct SortScrollListItem
|
struct SortScrollListItem
|
||||||
{
|
{
|
||||||
SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal)
|
SortScrollListItem(const std::vector<LLScrollListCtrl::sort_column_t >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal)
|
||||||
: mSortOrders(sort_orders)
|
: mSortOrders(sort_orders)
|
||||||
, mSortSignal(sort_signal)
|
, mSortSignal(sort_signal)
|
||||||
{}
|
{}
|
||||||
@@ -102,7 +102,7 @@ struct SortScrollListItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef std::vector<std::pair<S32, BOOL> > sort_order_t;
|
typedef std::vector<LLScrollListCtrl::sort_column_t > sort_order_t;
|
||||||
const LLScrollListCtrl::sort_signal_t* mSortSignal;
|
const LLScrollListCtrl::sort_signal_t* mSortSignal;
|
||||||
const sort_order_t& mSortOrders;
|
const sort_order_t& mSortOrders;
|
||||||
};
|
};
|
||||||
@@ -2294,7 +2294,7 @@ struct SameSortColumn
|
|||||||
SameSortColumn(S32 column) : mColumn(column) {}
|
SameSortColumn(S32 column) : mColumn(column) {}
|
||||||
S32 mColumn;
|
S32 mColumn;
|
||||||
|
|
||||||
bool operator()(std::pair<S32, BOOL> 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)
|
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
|
// for one-shot sorts, does not save sort column/order
|
||||||
void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending)
|
void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<S32, BOOL> > sort_column;
|
std::vector<sort_column_t > sort_column;
|
||||||
sort_column.push_back(std::make_pair(column, ascending));
|
sort_column.push_back(std::make_pair(column, ascending));
|
||||||
|
|
||||||
// do stable sort to preserve any previous sorts
|
// 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();
|
std::string value = row_child->getTextContents();
|
||||||
row["columns"][column_idx]["value"] = value;
|
row["columns"][column_idx]["value"] = value;
|
||||||
|
|
||||||
std::string columnname("");
|
std::string columnname;
|
||||||
if (row_child->getAttributeString("name", columnname))
|
if (row_child->getAttributeString("name", columnname))
|
||||||
row["columns"][column_idx]["column"] = columnname;
|
row["columns"][column_idx]["column"] = columnname;
|
||||||
|
|
||||||
std::string font("");
|
std::string font;
|
||||||
if (row_child->getAttributeString("font", font))
|
if (row_child->getAttributeString("font", font))
|
||||||
row["columns"][column_idx]["font"] = font;
|
row["columns"][column_idx]["font"] = font;
|
||||||
|
|
||||||
std::string font_style("");
|
std::string font_style;
|
||||||
if (row_child->getAttributeString("font-style", font_style))
|
if (row_child->getAttributeString("font-style", font_style))
|
||||||
row["columns"][column_idx]["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()
|
void LLScrollListCtrl::copy()
|
||||||
{
|
{
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
|
for (auto item : getAllSelected())
|
||||||
std::vector<LLScrollListItem*> items = getAllSelected();
|
|
||||||
std::vector<LLScrollListItem*>::iterator itor;
|
|
||||||
for (itor = items.begin(); itor != items.end(); ++itor)
|
|
||||||
{
|
{
|
||||||
buffer += (*itor)->getContentsCSV() + "\n";
|
buffer += item->getContentsCSV() + '\n';
|
||||||
}
|
}
|
||||||
gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
|
gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,6 +320,8 @@ public:
|
|||||||
void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
|
void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
|
||||||
S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
|
S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
|
||||||
|
|
||||||
|
typedef std::pair<S32, bool> sort_column_t;
|
||||||
|
const std::vector<sort_column_t>& getSortColumns() const { return mSortColumns; }
|
||||||
std::string getSortColumnName();
|
std::string getSortColumnName();
|
||||||
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
|
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
|
||||||
BOOL hasSortOrder() const;
|
BOOL hasSortOrder() const;
|
||||||
@@ -354,7 +356,7 @@ public:
|
|||||||
void setNeedsSortColumn(S32 col)
|
void setNeedsSortColumn(S32 col)
|
||||||
{
|
{
|
||||||
if(!isSorted())return;
|
if(!isSorted())return;
|
||||||
for(std::vector<std::pair<S32, BOOL> >::iterator it=mSortColumns.begin();it!=mSortColumns.end();++it)
|
for(auto it=mSortColumns.begin();it!=mSortColumns.end();++it)
|
||||||
{
|
{
|
||||||
if((*it).first == col)
|
if((*it).first == col)
|
||||||
{
|
{
|
||||||
@@ -482,7 +484,6 @@ private:
|
|||||||
typedef std::vector<LLScrollListColumn*> ordered_columns_t;
|
typedef std::vector<LLScrollListColumn*> ordered_columns_t;
|
||||||
ordered_columns_t mColumnsIndexed;
|
ordered_columns_t mColumnsIndexed;
|
||||||
|
|
||||||
typedef std::pair<S32, BOOL> sort_column_t;
|
|
||||||
std::vector<sort_column_t> mSortColumns;
|
std::vector<sort_column_t> mSortColumns;
|
||||||
|
|
||||||
sort_signal_t* mSortCallback;
|
sort_signal_t* mSortCallback;
|
||||||
|
|||||||
@@ -293,6 +293,8 @@ LLTextEditor::LLTextEditor(
|
|||||||
mMouseDownY(0),
|
mMouseDownY(0),
|
||||||
mLastSelectionX(-1),
|
mLastSelectionX(-1),
|
||||||
mLastSelectionY(-1),
|
mLastSelectionY(-1),
|
||||||
|
mParseHTML(parse_html),
|
||||||
|
mParseHighlights(false),
|
||||||
mLastContextMenuX(-1),
|
mLastContextMenuX(-1),
|
||||||
mLastContextMenuY(-1),
|
mLastContextMenuY(-1),
|
||||||
mReflowNeeded(FALSE),
|
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);
|
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);
|
addChild(mBorder);
|
||||||
|
|
||||||
mParseHTML = parse_html;
|
|
||||||
appendText(default_text, FALSE, FALSE);
|
appendText(default_text, FALSE, FALSE);
|
||||||
|
|
||||||
resetDirty(); // Update saved text state
|
resetDirty(); // Update saved text state
|
||||||
@@ -741,7 +742,7 @@ LLMenuGL* LLTextEditor::createUrlContextMenu(S32 x, S32 y, const std::string &in
|
|||||||
return menu;
|
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
|
// clear out the existing text and segments
|
||||||
mWText.clear();
|
mWText.clear();
|
||||||
@@ -756,7 +757,7 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str)
|
|||||||
//LLStringUtil::removeCRLF(text);
|
//LLStringUtil::removeCRLF(text);
|
||||||
|
|
||||||
// appendText modifies mCursorPos...
|
// appendText modifies mCursorPos...
|
||||||
appendText(utf8str, false, false);
|
appendText(utf8str, false, false, nullptr, force_replace_links);
|
||||||
// ...so move cursor to top after appending text
|
// ...so move cursor to top after appending text
|
||||||
setCursorPos(0);
|
setCursorPos(0);
|
||||||
|
|
||||||
@@ -766,9 +767,9 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str)
|
|||||||
mTextIsUpToDate = true;
|
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
|
// virtual
|
||||||
@@ -4274,7 +4275,7 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
std::string text = prepend_newline && !mWText.empty() ? ('\n' + new_text) : new_text;
|
std::string text = prepend_newline && !mWText.empty() ? ('\n' + new_text) : new_text;
|
||||||
appendTextImpl(text, style);
|
appendTextImpl(text, style, force_replace_links);
|
||||||
|
|
||||||
if (!allow_undo)
|
if (!allow_undo)
|
||||||
{
|
{
|
||||||
@@ -4317,7 +4318,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s
|
|||||||
if (always_underline) link_style->mUnderline = true;
|
if (always_underline) link_style->mUnderline = true;
|
||||||
appendAndHighlightText(link, part, link_style, !always_underline/*match.underlineOnHoverOnly()*/);
|
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))
|
while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, cb))
|
||||||
{
|
{
|
||||||
start = match.getStart();
|
start = match.getStart();
|
||||||
@@ -4340,7 +4341,7 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s
|
|||||||
|
|
||||||
auto url = match.getUrl();
|
auto url = match.getUrl();
|
||||||
const auto& label = match.getLabel();
|
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
|
// add icon before url if need
|
||||||
/* Singu TODO: Icons next to links?
|
/* Singu TODO: Icons next to links?
|
||||||
@@ -4552,39 +4553,29 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h
|
|||||||
// This is where we appendHighlightedText
|
// This is where we appendHighlightedText
|
||||||
// If LindenUserDir is empty then we didn't login yet.
|
// If LindenUserDir is empty then we didn't login yet.
|
||||||
// In that case we can't instantiate LLTextParser, which is initialized per user.
|
// 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();
|
const LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), (LLTextParser::EHighlightPosition)highlight_part);
|
||||||
|
auto cur_length = getLength();
|
||||||
if (highlight && stylep)
|
for (auto i = pieces.beginArray(), end = pieces.endArray(); i < end; ++i)
|
||||||
{
|
{
|
||||||
LLStyleSP highlight_params(new LLStyle(*stylep));
|
const auto& piece = *i;
|
||||||
LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params->getColor(), (LLTextParser::EHighlightPosition)highlight_part);
|
LLWString wide_text = utf8str_to_wstring(piece["text"].asString());
|
||||||
for (S32 i=0;i<pieces.size();i++)
|
|
||||||
{
|
|
||||||
LLSD color_llsd = pieces[i]["color"];
|
|
||||||
LLColor4 lcolor;
|
|
||||||
lcolor.setValue(color_llsd);
|
|
||||||
highlight_params->setColor(lcolor);
|
|
||||||
|
|
||||||
LLWString wide_text;
|
insertStringNoUndo(cur_length, wide_text);
|
||||||
wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
|
LLStyleSP sp(new LLStyle(*stylep));
|
||||||
|
sp->setColor(piece["color"]);
|
||||||
S32 cur_length = getLength();
|
auto wide_size = wide_text.size();
|
||||||
insertStringNoUndo(cur_length, wide_text);
|
LLTextSegmentPtr segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_size);
|
||||||
LLStyleSP sp(new LLStyle(*highlight_params));
|
cur_length += wide_size;
|
||||||
LLTextSegmentPtr segmentp;
|
if (underline_on_hover) segmentp->setUnderlineOnHover(true);
|
||||||
segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_text.size());
|
mSegments.push_back(segmentp);
|
||||||
if (underline_on_hover) segmentp->setUnderlineOnHover(true);
|
|
||||||
mSegments.push_back(segmentp);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else
|
else
|
||||||
{
|
{
|
||||||
LLWString wide_text;
|
LLWString wide_text = utf8str_to_wstring(new_text);
|
||||||
wide_text = utf8str_to_wstring(new_text);
|
|
||||||
|
|
||||||
auto length = getLength();
|
auto length = getLength();
|
||||||
auto insert_len = length + insertStringNoUndo(length, utf8str_to_wstring(new_text));
|
auto insert_len = length + insertStringNoUndo(length, utf8str_to_wstring(new_text));
|
||||||
@@ -4627,7 +4618,7 @@ void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 h
|
|||||||
}
|
}
|
||||||
endOfDoc();
|
endOfDoc();
|
||||||
}
|
}
|
||||||
else if( selection_start != selection_end )
|
else if (was_selecting || selection_start != selection_end)
|
||||||
{
|
{
|
||||||
mSelectionStart = selection_start;
|
mSelectionStart = selection_start;
|
||||||
mSelectionEnd = selection_end;
|
mSelectionEnd = selection_end;
|
||||||
@@ -4690,8 +4681,61 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)
|
|||||||
|
|
||||||
S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length)
|
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;
|
||||||
|
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
|
||||||
|
seg_iter = mSegments.erase(seg_iter);
|
||||||
|
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);
|
mWText.erase(pos, length);
|
||||||
|
|
||||||
|
// recreate default segment in case we erased everything
|
||||||
|
createDefaultSegment();
|
||||||
|
|
||||||
mTextIsUpToDate = FALSE;
|
mTextIsUpToDate = FALSE;
|
||||||
|
|
||||||
|
/*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
|
return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4966,6 +5010,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<LLTextSegment> index_segment = new LLTextSegment(index);
|
||||||
|
auto it = std::lower_bound(mSegments.begin(), mSegments.end(), index_segment, LLTextSegment::compare());
|
||||||
|
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<LLTextSegment> index_segment = new LLTextSegment(index);
|
||||||
|
auto it = std::lower_bound(mSegments.begin(), mSegments.end(), index_segment, LLTextSegment::compare());
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
// Finds the text segment (if any) at the give local screen position
|
// Finds the text segment (if any) at the give local screen position
|
||||||
LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const
|
LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -276,8 +276,8 @@ public:
|
|||||||
LLMenuGL* createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url
|
LLMenuGL* createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url
|
||||||
|
|
||||||
// Non-undoable
|
// Non-undoable
|
||||||
void setText(const LLStringExplicit &utf8str);
|
void setText(const LLStringExplicit &utf8str, bool force_replace_links = true);
|
||||||
void setWText(const LLWString &wtext);
|
void setWText(const LLWString &wtext, bool force_replace_links = true);
|
||||||
|
|
||||||
// Returns byte length limit
|
// Returns byte length limit
|
||||||
S32 getMaxLength() const { return mMaxTextByteLength; }
|
S32 getMaxLength() const { return mMaxTextByteLength; }
|
||||||
@@ -310,10 +310,10 @@ protected:
|
|||||||
|
|
||||||
LLHandle<LLView> mPopupMenuHandle;
|
LLHandle<LLView> mPopupMenuHandle;
|
||||||
|
|
||||||
S32 getLength() const { return mWText.length(); }
|
|
||||||
void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const;
|
void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const;
|
||||||
void drawPreeditMarker();
|
void drawPreeditMarker();
|
||||||
public:
|
public:
|
||||||
|
S32 getLength() const { return mWText.length(); }
|
||||||
void updateLineStartList(S32 startpos = 0);
|
void updateLineStartList(S32 startpos = 0);
|
||||||
protected:
|
protected:
|
||||||
void updateScrollFromCursor();
|
void updateScrollFromCursor();
|
||||||
@@ -415,8 +415,10 @@ protected:
|
|||||||
S32 removeChar(S32 pos);
|
S32 removeChar(S32 pos);
|
||||||
void removeWord(bool prev);
|
void removeWord(bool prev);
|
||||||
S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op);
|
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);
|
S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op);
|
||||||
|
protected:
|
||||||
|
|
||||||
// Direct operations
|
// Direct operations
|
||||||
S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted
|
S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted
|
||||||
S32 removeStringNoUndo(S32 pos, S32 length);
|
S32 removeStringNoUndo(S32 pos, S32 length);
|
||||||
@@ -463,6 +465,10 @@ protected:
|
|||||||
BOOL mParseHighlights;
|
BOOL mParseHighlights;
|
||||||
|
|
||||||
typedef std::vector<LLTextSegmentPtr> segment_list_t;
|
typedef std::vector<LLTextSegmentPtr> segment_list_t;
|
||||||
|
|
||||||
|
segment_list_t::iterator getSegIterContaining(S32 index);
|
||||||
|
segment_list_t::const_iterator getSegIterContaining(S32 index) const;
|
||||||
|
|
||||||
segment_list_t mSegments;
|
segment_list_t mSegments;
|
||||||
LLTextSegmentPtr mHoverSegment;
|
LLTextSegmentPtr mHoverSegment;
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
//#include "lluicolortable.h"
|
//#include "lluicolortable.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/format.hpp> // <alchemy/>
|
#include <boost/format.hpp> // <alchemy/>
|
||||||
|
|
||||||
#define APP_HEADER_REGEX "((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/app)|(secondlife:///app))"
|
#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;
|
size_t start = 0;
|
||||||
while (! isspace(text[start]))
|
while (! isspace(text[start]))
|
||||||
{
|
{
|
||||||
start++;
|
++start;
|
||||||
}
|
}
|
||||||
while (text[start] == ' ' || text[start] == '\t')
|
while (text[start] == ' ' || text[start] == '\t')
|
||||||
{
|
{
|
||||||
start++;
|
++start;
|
||||||
}
|
}
|
||||||
return unescapeUrl(url.substr(start, url.size()-start-1));
|
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;
|
size_t end = 0;
|
||||||
while (! isspace(text[end]))
|
while (! isspace(text[end]))
|
||||||
{
|
{
|
||||||
end++;
|
++end;
|
||||||
}
|
}
|
||||||
return escapeUrl(string.substr(1, end-1));
|
return escapeUrl(string.substr(1, end-1));
|
||||||
}
|
}
|
||||||
@@ -273,7 +274,7 @@ std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const
|
|||||||
//
|
//
|
||||||
LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
|
LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
|
||||||
{
|
{
|
||||||
mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]",
|
mPattern = boost::regex("\\[(https?://\\S+|\\S+\\.([^\\s<]*)?)[ \t]+[^\\]]+\\]",
|
||||||
boost::regex::perl|boost::regex::icase);
|
boost::regex::perl|boost::regex::icase);
|
||||||
mMenuName = "menu_url_http.xml";
|
mMenuName = "menu_url_http.xml";
|
||||||
mTooltip = LLTrans::getString("TooltipHttpUrl");
|
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
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -334,7 +338,7 @@ std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const
|
|||||||
LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()
|
LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()
|
||||||
: LLUrlEntryBase()
|
: 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("(https?://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?\\S*)?)?",
|
||||||
boost::regex::perl|boost::regex::icase);
|
boost::regex::perl|boost::regex::icase);
|
||||||
mMenuName = "menu_url_http.xml";
|
mMenuName = "menu_url_http.xml";
|
||||||
mTooltip = LLTrans::getString("TooltipHttpUrl");
|
mTooltip = LLTrans::getString("TooltipHttpUrl");
|
||||||
@@ -421,7 +425,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const
|
|||||||
LLUrlEntrySLURL::LLUrlEntrySLURL()
|
LLUrlEntrySLURL::LLUrlEntrySLURL()
|
||||||
{
|
{
|
||||||
// see http://slurl.com/about.php for details on the SLURL format
|
// 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("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?\\S*)?)?",
|
||||||
boost::regex::perl|boost::regex::icase);
|
boost::regex::perl|boost::regex::icase);
|
||||||
mMenuName = "menu_url_slurl.xml";
|
mMenuName = "menu_url_slurl.xml";
|
||||||
mTooltip = LLTrans::getString("TooltipSLURL");
|
mTooltip = LLTrans::getString("TooltipSLURL");
|
||||||
@@ -669,9 +673,12 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
|
if (cb)
|
||||||
mAvatarNameCacheConnections.emplace(agent_id, connection);
|
{
|
||||||
addObserver(agent_id_string, url, 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");
|
return LLTrans::getString("LoadingData");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -781,9 +788,12 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
|
if (cb)
|
||||||
mAvatarNameCacheConnections.emplace(agent_id, connection);
|
{
|
||||||
addObserver(agent_id_string, url, 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");
|
return LLTrans::getString("LoadingData");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -917,10 +927,13 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gCacheName->getGroup(group_id,
|
if (cb)
|
||||||
boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
|
{
|
||||||
this, _1, _2, _3));
|
gCacheName->getGroup(group_id,
|
||||||
addObserver(group_id_string, url, cb);
|
boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
|
||||||
|
this, _1, _2, _3));
|
||||||
|
addObserver(group_id_string, url, cb);
|
||||||
|
}
|
||||||
return LLTrans::getString("LoadingData");
|
return LLTrans::getString("LoadingData");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1024,7 +1037,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC
|
|||||||
std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
|
std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
|
||||||
|
|
||||||
// Add an observer to call LLUrlLabelCallback when we have parcel name.
|
// 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);
|
LLUUID parcel_id(parcel_id_string);
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class LLAvatarName;
|
|||||||
typedef boost::signals2::signal<void (const std::string& url,
|
typedef boost::signals2::signal<void (const std::string& url,
|
||||||
const std::string& label,
|
const std::string& label,
|
||||||
const std::string& icon)> LLUrlLabelSignal;
|
const std::string& icon)> LLUrlLabelSignal;
|
||||||
typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback;
|
typedef LLUrlLabelSignal::slot_function_type LLUrlLabelCallback;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// LLUrlEntryBase is the base class of all Url types registered in the
|
/// LLUrlEntryBase is the base class of all Url types registered in the
|
||||||
|
|||||||
@@ -2831,6 +2831,10 @@ void LLView::initFromXML(LLXMLNodePtr node, LLView* parent)
|
|||||||
node->getAttributeBOOL("mouse_opaque", mMouseOpaque);
|
node->getAttributeBOOL("mouse_opaque", mMouseOpaque);
|
||||||
|
|
||||||
node->getAttributeS32("default_tab_group", mDefaultTabGroup);
|
node->getAttributeS32("default_tab_group", mDefaultTabGroup);
|
||||||
|
if (node->hasAttribute("sound_flags"))
|
||||||
|
{
|
||||||
|
node->getAttributeU8("sound_flags", mSoundFlags);
|
||||||
|
}
|
||||||
|
|
||||||
reshape(view_rect.getWidth(), view_rect.getHeight());
|
reshape(view_rect.getWidth(), view_rect.getHeight());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ include(BuildVersion)
|
|||||||
include(BuildBranding)
|
include(BuildBranding)
|
||||||
include(CMakeCopyIfDifferent)
|
include(CMakeCopyIfDifferent)
|
||||||
include(DBusGlib)
|
include(DBusGlib)
|
||||||
|
include(FindVCRedist)
|
||||||
include(FMODSTUDIO)
|
include(FMODSTUDIO)
|
||||||
include(GLOD)
|
include(GLOD)
|
||||||
include(FindOpenGL)
|
|
||||||
include(Hunspell)
|
include(Hunspell)
|
||||||
include(Json)
|
include(Json)
|
||||||
include(LLAddBuildTest)
|
include(LLAddBuildTest)
|
||||||
@@ -36,6 +36,7 @@ include(Linking)
|
|||||||
include(NDOF)
|
include(NDOF)
|
||||||
include(NVAPI)
|
include(NVAPI)
|
||||||
include(OPENAL)
|
include(OPENAL)
|
||||||
|
include(OpenGL)
|
||||||
include(OpenSSL)
|
include(OpenSSL)
|
||||||
include(StateMachine)
|
include(StateMachine)
|
||||||
include(TemplateCheck)
|
include(TemplateCheck)
|
||||||
@@ -45,10 +46,6 @@ include(WinManifest)
|
|||||||
include(ZLIB)
|
include(ZLIB)
|
||||||
include(URIPARSER)
|
include(URIPARSER)
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
use_prebuilt_binary(vcredist)
|
|
||||||
endif (MSVC)
|
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${STATEMACHINE_INCLUDE_DIRS}
|
${STATEMACHINE_INCLUDE_DIRS}
|
||||||
${DBUSGLIB_INCLUDE_DIRS}
|
${DBUSGLIB_INCLUDE_DIRS}
|
||||||
@@ -1434,6 +1431,12 @@ endif (WINDOWS)
|
|||||||
|
|
||||||
set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
|
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})
|
list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES})
|
||||||
|
|
||||||
set_source_files_properties(${viewer_HEADER_FILES}
|
set_source_files_properties(${viewer_HEADER_FILES}
|
||||||
@@ -1505,12 +1508,7 @@ if (WINDOWS)
|
|||||||
${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll
|
${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll
|
||||||
${SHARED_LIB_STAGING_DIR}/Debug/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}/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}/vivoxplatform.dll
|
||||||
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxoal.dll
|
|
||||||
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ca-bundle.crt
|
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ca-bundle.crt
|
||||||
${GOOGLE_PERF_TOOLS_SOURCE}
|
${GOOGLE_PERF_TOOLS_SOURCE}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/licenses-win32.txt
|
${CMAKE_CURRENT_SOURCE_DIR}/licenses-win32.txt
|
||||||
@@ -1526,6 +1524,18 @@ if (WINDOWS)
|
|||||||
windows-crash-logger
|
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 (FMODSTUDIO)
|
||||||
if (WORD_SIZE EQUAL 64)
|
if (WORD_SIZE EQUAL 64)
|
||||||
list(APPEND COPY_INPUT_DEPENDENCIES
|
list(APPEND COPY_INPUT_DEPENDENCIES
|
||||||
@@ -1571,7 +1581,29 @@ if (WINDOWS)
|
|||||||
|
|
||||||
add_custom_target(copy_w_viewer_manifest ALL DEPENDS ${CMAKE_CFG_INTDIR}/copy_touched.bat)
|
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)
|
if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts)
|
||||||
add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts)
|
add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts)
|
||||||
|
|||||||
@@ -7493,22 +7493,6 @@ This should be as low as possible, but too low may break functionality</string>
|
|||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
<key>FloaterFavoritesRect</key>
|
|
||||||
<map>
|
|
||||||
<key>Comment</key>
|
|
||||||
<string>Rectangle for favorites window</string>
|
|
||||||
<key>Persist</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>Type</key>
|
|
||||||
<string>Rect</string>
|
|
||||||
<key>Value</key>
|
|
||||||
<array>
|
|
||||||
<integer>0</integer>
|
|
||||||
<integer>400</integer>
|
|
||||||
<integer>250</integer>
|
|
||||||
<integer>0</integer>
|
|
||||||
</array>
|
|
||||||
</map>
|
|
||||||
<key>FloaterFindRect2</key>
|
<key>FloaterFindRect2</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
@@ -8048,6 +8032,20 @@ This should be as low as possible, but too low may break functionality</string>
|
|||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</map>
|
</map>
|
||||||
|
<key>RadarSortOrder</key>
|
||||||
|
<map>
|
||||||
|
<key>Comment</key>
|
||||||
|
<string>Sort order for the radar, map of column names to ascending boolean</string>
|
||||||
|
<key>Persist</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>LLSD</string>
|
||||||
|
<key>Value</key>
|
||||||
|
<array>
|
||||||
|
<string>distance</string>
|
||||||
|
<boolean>1</boolean>
|
||||||
|
</array>
|
||||||
|
</map>
|
||||||
<key>FloaterLandRect5</key>
|
<key>FloaterLandRect5</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
@@ -8160,22 +8158,6 @@ This should be as low as possible, but too low may break functionality</string>
|
|||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</array>
|
</array>
|
||||||
</map>
|
</map>
|
||||||
<key>FloaterMyOutfitsRect</key>
|
|
||||||
<map>
|
|
||||||
<key>Comment</key>
|
|
||||||
<string>Rectangle for My Outfits window</string>
|
|
||||||
<key>Persist</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>Type</key>
|
|
||||||
<string>Rect</string>
|
|
||||||
<key>Value</key>
|
|
||||||
<array>
|
|
||||||
<integer>0</integer>
|
|
||||||
<integer>400</integer>
|
|
||||||
<integer>250</integer>
|
|
||||||
<integer>0</integer>
|
|
||||||
</array>
|
|
||||||
</map>
|
|
||||||
<key>FloaterObjectIMInfo</key>
|
<key>FloaterObjectIMInfo</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; secondlife setup.nsi
|
;; Second Life setup.nsi
|
||||||
;; Copyright 2004-2011, Linden Research, Inc.
|
;; Copyright 2004-2015, Linden Research, Inc.
|
||||||
;; Copyright 2013-2015 Alchemy Viewer Project
|
|
||||||
;;
|
;;
|
||||||
;; This library is free software; you can redistribute it and/or
|
;; This library is free software; you can redistribute it and/or
|
||||||
;; modify it under the terms of the GNU Lesser General Public
|
;; 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
|
;; Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
;;
|
;;
|
||||||
;; NSIS Unicode 2.46.5 or higher required
|
;; NSIS 3 or higher required for Unicode support
|
||||||
;; http://www.scratchpaper.com/
|
|
||||||
;;
|
;;
|
||||||
;; Author: James Cook, Don Kjer, Callum Prentice, Drake Arconis
|
;; Author: James Cook, Don Kjer, Callum Prentice, Drake Arconis
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -34,6 +32,8 @@
|
|||||||
!include "LogicLib.nsh"
|
!include "LogicLib.nsh"
|
||||||
!include "StdUtils.nsh"
|
!include "StdUtils.nsh"
|
||||||
!include "FileFunc.nsh"
|
!include "FileFunc.nsh"
|
||||||
|
!insertmacro GetParameters
|
||||||
|
!insertmacro GetOptions
|
||||||
!include "x64.nsh"
|
!include "x64.nsh"
|
||||||
!include "WinVer.nsh"
|
!include "WinVer.nsh"
|
||||||
!include "MUI2.nsh"
|
!include "MUI2.nsh"
|
||||||
@@ -44,8 +44,8 @@
|
|||||||
%%INST_VARS%%
|
%%INST_VARS%%
|
||||||
%%WIN64_BIN_BUILD%%
|
%%WIN64_BIN_BUILD%%
|
||||||
|
|
||||||
Var INSTPROG
|
|
||||||
Var INSTEXE
|
Var INSTEXE
|
||||||
|
Var INSTPROG
|
||||||
Var INSTSHORTCUT
|
Var INSTSHORTCUT
|
||||||
Var AUTOSTART
|
Var AUTOSTART
|
||||||
Var COMMANDLINE ; command line passed to this installer, set in .onInit
|
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 SKIP_AUTORUN ; skip automatic launch of viewer after install
|
||||||
Var STARTMENUFOLDER
|
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
|
;General
|
||||||
|
|
||||||
@@ -73,7 +81,7 @@
|
|||||||
|
|
||||||
;Get installation folder from registry if available and 32bit otherwise do it in init
|
;Get installation folder from registry if available and 32bit otherwise do it in init
|
||||||
!ifndef WIN64_BIN_BUILD
|
!ifndef WIN64_BIN_BUILD
|
||||||
InstallDirRegKey HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" ""
|
InstallDirRegKey HKLM "${INSTNAME_KEY}" ""
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
;Request application privileges for Windows UAC
|
;Request application privileges for Windows UAC
|
||||||
@@ -90,12 +98,13 @@
|
|||||||
;Version Information
|
;Version Information
|
||||||
|
|
||||||
VIProductVersion "${VERSION_LONG}"
|
VIProductVersion "${VERSION_LONG}"
|
||||||
VIAddVersionKey "ProductName" "Singularity Viewer"
|
VIAddVersionKey "ProductName" "Singularity Viewer Installer"
|
||||||
VIAddVersionKey "Comments" "A viewer for the meta-verse!"
|
VIAddVersionKey "Comments" "A viewer for the meta-verse!"
|
||||||
VIAddVersionKey "CompanyName" "${VENDORSTR}"
|
VIAddVersionKey "CompanyName" "${VENDORSTR}"
|
||||||
VIAddVersionKey "LegalCopyright" "Copyright © 2010-2016, ${VENDORSTR}"
|
VIAddVersionKey "LegalCopyright" "Copyright © 2010-2019, ${VENDORSTR}"
|
||||||
VIAddVersionKey "FileDescription" "${APPNAME} Installer"
|
VIAddVersionKey "FileDescription" "${APPNAME} Installer"
|
||||||
VIAddVersionKey "ProductVersion" "${VERSION_LONG}"
|
VIAddVersionKey "ProductVersion" "${VERSION_LONG}"
|
||||||
|
VIAddVersionKey "FileVersion" "${VERSION_LONG}"
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
;Interface Settings
|
;Interface Settings
|
||||||
@@ -118,7 +127,7 @@
|
|||||||
|
|
||||||
;Remember the installer language
|
;Remember the installer language
|
||||||
!define MUI_LANGDLL_REGISTRY_ROOT "HKLM"
|
!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"
|
!define MUI_LANGDLL_REGISTRY_VALUENAME "InstallerLanguage"
|
||||||
|
|
||||||
;Always show the dialog
|
;Always show the dialog
|
||||||
@@ -140,7 +149,7 @@
|
|||||||
|
|
||||||
;Start Menu Folder Page
|
;Start Menu Folder Page
|
||||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
|
!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_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
|
||||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip
|
||||||
!ifdef WIN64_BIN_BUILD
|
!ifdef WIN64_BIN_BUILD
|
||||||
@@ -177,10 +186,9 @@
|
|||||||
;Languages
|
;Languages
|
||||||
|
|
||||||
!include "%%SOURCE%%\installers\windows\lang_en-us.nsi"
|
!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_de.nsi"
|
||||||
!include "%%SOURCE%%\installers\windows\lang_es.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_ja.nsi"
|
||||||
!include "%%SOURCE%%\installers\windows\lang_pl.nsi"
|
!include "%%SOURCE%%\installers\windows\lang_pl.nsi"
|
||||||
!include "%%SOURCE%%\installers\windows\lang_it.nsi"
|
!include "%%SOURCE%%\installers\windows\lang_it.nsi"
|
||||||
@@ -188,6 +196,7 @@
|
|||||||
!include "%%SOURCE%%\installers\windows\lang_da.nsi"
|
!include "%%SOURCE%%\installers\windows\lang_da.nsi"
|
||||||
!include "%%SOURCE%%\installers\windows\lang_ru.nsi"
|
!include "%%SOURCE%%\installers\windows\lang_ru.nsi"
|
||||||
!include "%%SOURCE%%\installers\windows\lang_tr.nsi"
|
!include "%%SOURCE%%\installers\windows\lang_tr.nsi"
|
||||||
|
!include "%%SOURCE%%\installers\windows\lang_zh.nsi"
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
;Reserve Files
|
;Reserve Files
|
||||||
@@ -197,7 +206,7 @@
|
|||||||
;because this will make your installer start faster.
|
;because this will make your installer start faster.
|
||||||
|
|
||||||
!insertmacro MUI_RESERVEFILE_LANGDLL
|
!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\nsDialogs.dll"
|
||||||
ReserveFile "${NSISDIR}\Plugins\x86-unicode\StartMenu.dll"
|
ReserveFile "${NSISDIR}\Plugins\x86-unicode\StartMenu.dll"
|
||||||
ReserveFile "${NSISDIR}\Plugins\x86-unicode\StdUtils.dll"
|
ReserveFile "${NSISDIR}\Plugins\x86-unicode\StdUtils.dll"
|
||||||
@@ -374,39 +383,7 @@ Function un.CloseSecondLife
|
|||||||
Return
|
Return
|
||||||
FunctionEnd
|
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
|
;Installer Sections
|
||||||
@@ -424,7 +401,6 @@ Section "Viewer"
|
|||||||
|
|
||||||
Call CheckIfAlreadyCurrent
|
Call CheckIfAlreadyCurrent
|
||||||
Call CloseSecondLife ; Make sure we're not running
|
Call CloseSecondLife ; Make sure we're not running
|
||||||
Call CheckNetworkConnection ; ping secondlife.com
|
|
||||||
|
|
||||||
SetOutPath "$INSTDIR"
|
SetOutPath "$INSTDIR"
|
||||||
;Remove old shader files first so fallbacks will work.
|
;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 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 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"
|
WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Scripting Language Help.url" "InternetShortcut" "URL" "http://wiki.secondlife.com/wiki/LSL_Portal"
|
||||||
|
|
||||||
!insertmacro MUI_STARTMENU_WRITE_END
|
!insertmacro MUI_STARTMENU_WRITE_END
|
||||||
|
|
||||||
;Other shortcuts
|
;Other shortcuts
|
||||||
@@ -473,28 +448,28 @@ Section "Viewer"
|
|||||||
!endif
|
!endif
|
||||||
|
|
||||||
;Write registry
|
;Write registry
|
||||||
WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "" "$INSTDIR"
|
WriteRegStr HKLM "${INSTNAME_KEY}" "" "$INSTDIR"
|
||||||
WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "Version" "${VERSION_LONG}"
|
WriteRegStr HKLM "${INSTNAME_KEY}" "Version" "${VERSION_LONG}"
|
||||||
WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "Shortcut" "$INSTSHORTCUT"
|
WriteRegStr HKLM "${INSTNAME_KEY}" "Shortcut" "$INSTSHORTCUT"
|
||||||
WriteRegStr HKLM "SOFTWARE\${VENDORSTR}\$INSTPROG" "Exe" "$INSTEXE"
|
WriteRegStr HKLM "${INSTNAME_KEY}" "Exe" "$INSTEXE"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "Comments" "A viewer for the meta-verse!"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "Comments" "A viewer for the meta-verse!"
|
||||||
!ifdef WIN64_BIN_BUILD
|
!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
|
!else
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayName" "$INSTSHORTCUT Viewer"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayName" "$INSTSHORTCUT Viewer"
|
||||||
!endif
|
!endif
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayIcon" "$INSTDIR\$INSTEXE"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayIcon" "$INSTDIR\$INSTEXE"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayVersion" "${VERSION_LONG}"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "DisplayVersion" "${VERSION_LONG}"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "InstallLocation" "$INSTDIR"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "InstallLocation" "$INSTDIR"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "InstallSource" "$EXEDIR\"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "InstallSource" "$EXEDIR\"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "HelpLink" "http://www.singularityviewer.org"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "HelpLink" "http://www.singularityviewer.org"
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "NoModify" 1
|
WriteRegDWORD HKLM "${MSUNINSTALL_KEY}" "NoModify" 1
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "NoRepair" 1
|
WriteRegDWORD HKLM "${MSUNINSTALL_KEY}" "NoRepair" 1
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "Publisher" "${VENDORSTR}"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "Publisher" "${VENDORSTR}"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "URLInfoAbout" "http://www.singularityviewer.org"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "URLInfoAbout" "http://www.singularityviewer.org"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "URLUpdateInfo" "http://www.singularityviewer.org/downloads"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "URLUpdateInfo" "http://www.singularityviewer.org/downloads"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "UninstallString" "$\"$INSTDIR\uninst.exe$\""
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "UninstallString" "$\"$INSTDIR\uninst.exe$\""
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "QuietUninstallString" "$\"$INSTDIR\uninst.exe$\" /S"
|
WriteRegStr HKLM "${MSUNINSTALL_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninst.exe$\" /S"
|
||||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||||
IntFmt $0 "0x%08X" $0
|
IntFmt $0 "0x%08X" $0
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "EstimatedSize" "$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:Hypergrid legacy"
|
||||||
WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" ""
|
WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" ""
|
||||||
WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" "$\"$INSTDIR\$INSTEXE$\""
|
WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" "$\"$INSTDIR\$INSTEXE$\""
|
||||||
|
|
||||||
;; URL param must be last item passed to viewer, it ignores subsequent params
|
;; URL param must be last item passed to viewer, it ignores subsequent params
|
||||||
;; to avoid parameter injection attacks.
|
;; to avoid parameter injection attacks.
|
||||||
WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell" "" "open"
|
WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell" "" "open"
|
||||||
@@ -552,6 +528,9 @@ SectionEnd
|
|||||||
;--------------------------------
|
;--------------------------------
|
||||||
;Installer Functions
|
;Installer Functions
|
||||||
Function .onInit
|
Function .onInit
|
||||||
|
!ifdef WIN64_BIN_BUILD
|
||||||
|
SetRegView 64
|
||||||
|
!endif
|
||||||
;Don't install on unsupported operating systems
|
;Don't install on unsupported operating systems
|
||||||
Call CheckWindowsVersion
|
Call CheckWindowsVersion
|
||||||
;Don't install if not administator
|
;Don't install if not administator
|
||||||
@@ -563,7 +542,6 @@ Function .onInit
|
|||||||
|
|
||||||
;Get installation folder from registry if available for 64bit
|
;Get installation folder from registry if available for 64bit
|
||||||
!ifdef WIN64_BIN_BUILD
|
!ifdef WIN64_BIN_BUILD
|
||||||
SetRegView 64
|
|
||||||
ReadRegStr $0 HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" ""
|
ReadRegStr $0 HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" ""
|
||||||
IfErrors +2 0 ; If error jump past setting SKIP_AUTORUN
|
IfErrors +2 0 ; If error jump past setting SKIP_AUTORUN
|
||||||
StrCpy $INSTDIR $0
|
StrCpy $INSTDIR $0
|
||||||
@@ -583,6 +561,7 @@ Function .onInit
|
|||||||
IfErrors +2 0 ; If error jump past setting AUTOSTART
|
IfErrors +2 0 ; If error jump past setting AUTOSTART
|
||||||
StrCpy $AUTOSTART "true"
|
StrCpy $AUTOSTART "true"
|
||||||
|
|
||||||
|
|
||||||
${GetOptions} $COMMANDLINE "/LANGID=" $0 ; /LANGID=1033 implies US English
|
${GetOptions} $COMMANDLINE "/LANGID=" $0 ; /LANGID=1033 implies US English
|
||||||
; If no language (error), then proceed
|
; If no language (error), then proceed
|
||||||
IfErrors lbl_configure_default_lang
|
IfErrors lbl_configure_default_lang
|
||||||
@@ -613,7 +592,6 @@ Section "Uninstall"
|
|||||||
!endif
|
!endif
|
||||||
|
|
||||||
StrCpy $INSTPROG "${APPNAMEONEWORD}"
|
StrCpy $INSTPROG "${APPNAMEONEWORD}"
|
||||||
StrCpy $INSTEXE "${INSTEXE}"
|
|
||||||
StrCpy $INSTSHORTCUT "${APPNAME}"
|
StrCpy $INSTSHORTCUT "${APPNAME}"
|
||||||
|
|
||||||
Call un.CloseSecondLife
|
Call un.CloseSecondLife
|
||||||
@@ -660,7 +638,9 @@ SectionEnd
|
|||||||
;Uninstaller Functions
|
;Uninstaller Functions
|
||||||
|
|
||||||
Function un.onInit
|
Function un.onInit
|
||||||
|
!ifdef WIN64_BIN_BUILD
|
||||||
|
SetRegView 64
|
||||||
|
!endif
|
||||||
Call un.CheckIfAdministrator
|
Call un.CheckIfAdministrator
|
||||||
|
|
||||||
!insertmacro MUI_UNGETLANGUAGE
|
!insertmacro MUI_UNGETLANGUAGE
|
||||||
|
|||||||
@@ -22,23 +22,53 @@
|
|||||||
|
|
||||||
#include "lffloaterinvpanel.h"
|
#include "lffloaterinvpanel.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
|
|
||||||
#include "llinventorypanel.h"
|
#include "llinventorypanel.h"
|
||||||
#include "lluictrlfactory.h"
|
#include "lluictrlfactory.h"
|
||||||
|
|
||||||
|
|
||||||
LFFloaterInvPanel::LFFloaterInvPanel(const LLUUID& cat_id, LLInventoryModel* model, const std::string& name)
|
LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, LLInventoryModel* model)
|
||||||
: LLInstanceTracker<LFFloaterInvPanel, LLUUID>(cat_id)
|
: LLInstanceTracker<LFFloaterInvPanel, LLSD>(cat)
|
||||||
{
|
{
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
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));
|
mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2));
|
||||||
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml");
|
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml");
|
||||||
|
|
||||||
|
// 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<LLPanel>("placeholder_panel");
|
LLPanel* panel = getChild<LLPanel>("placeholder_panel");
|
||||||
mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, LLSD().with("id", cat_id), panel->getRect(), model, true);
|
mPanel->setRect(panel->getRect());
|
||||||
mPanel->postBuild();
|
mPanel->postBuild();
|
||||||
mPanel->setFollows(FOLLOWS_ALL);
|
mPanel->setFollows(FOLLOWS_ALL);
|
||||||
mPanel->setEnabled(true);
|
mPanel->setEnabled(true);
|
||||||
addChild(mPanel);
|
addChild(mPanel);
|
||||||
removeChild(panel);
|
removeChild(panel);
|
||||||
setTitle(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LFFloaterInvPanel::~LFFloaterInvPanel()
|
LFFloaterInvPanel::~LFFloaterInvPanel()
|
||||||
@@ -47,10 +77,10 @@ LFFloaterInvPanel::~LFFloaterInvPanel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// 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);
|
LFFloaterInvPanel* floater = LFFloaterInvPanel::getInstance(cat);
|
||||||
if (!floater) floater = new LFFloaterInvPanel(cat_id, model, name);
|
if (!floater) floater = new LFFloaterInvPanel(cat, name, model);
|
||||||
floater->open();
|
floater->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,10 +93,10 @@ void LFFloaterInvPanel::closeAll()
|
|||||||
{
|
{
|
||||||
cache.push_back(&*i);
|
cache.push_back(&*i);
|
||||||
}
|
}
|
||||||
// Now close all panels, without using instance_iter iterators.
|
// Now close all, without using instance_iter iterators.
|
||||||
for (std::vector<LFFloaterInvPanel*>::iterator i = cache.begin(); i != cache.end(); ++i)
|
for (auto& floater : cache)
|
||||||
{
|
{
|
||||||
(*i)->close();
|
floater->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,15 +23,23 @@
|
|||||||
|
|
||||||
#include "llfloater.h"
|
#include "llfloater.h"
|
||||||
#include "llinstancetracker.h"
|
#include "llinstancetracker.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
|
||||||
|
|
||||||
class LFFloaterInvPanel : public LLFloater, public LLInstanceTracker<LFFloaterInvPanel, LLUUID>
|
class LFFloaterInvPanel : public LLFloater, public LLInstanceTracker<LFFloaterInvPanel, LLSD>
|
||||||
{
|
{
|
||||||
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();
|
~LFFloaterInvPanel();
|
||||||
|
|
||||||
public:
|
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 toggle(const LLSD& cat)
|
||||||
|
{
|
||||||
|
if (auto instance = getInstance(cat))
|
||||||
|
instance->close();
|
||||||
|
else
|
||||||
|
show(cat);
|
||||||
|
}
|
||||||
static void closeAll(); // Called when not allowed to have inventory open
|
static void closeAll(); // Called when not allowed to have inventory open
|
||||||
|
|
||||||
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
|
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ LFSimFeatureHandler::LFSimFeatureHandler()
|
|||||||
{
|
{
|
||||||
if (!gHippoGridManager->getCurrentGrid()->isSecondLife()) // Remove this line if we ever handle SecondLife sim features
|
if (!gHippoGridManager->getCurrentGrid()->isSecondLife()) // Remove this line if we ever handle SecondLife sim features
|
||||||
gAgent.addRegionChangedCallback(boost::bind(&LFSimFeatureHandler::handleRegionChange, this));
|
gAgent.addRegionChangedCallback(boost::bind(&LFSimFeatureHandler::handleRegionChange, this));
|
||||||
LLMuteList::instance().mGodLastNames.insert("Linden");
|
LLMuteList::instance().mGodLastNames = { "Linden", "ProductEngine" };
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportPolicy LFSimFeatureHandler::exportPolicy() const
|
ExportPolicy LFSimFeatureHandler::exportPolicy() const
|
||||||
@@ -138,7 +138,7 @@ void LFSimFeatureHandler::setSupportedFeatures()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mute_list.mGodLastNames.insert("Linden");
|
mute_list.mGodLastNames = { "Linden", "ProductEngine" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,54 @@
|
|||||||
#include "llfloaterworldmap.h"
|
#include "llfloaterworldmap.h"
|
||||||
#include "llagent.h"
|
#include "llagent.h"
|
||||||
#include "llappviewer.h" // for gPacificDaylightTime
|
#include "llappviewer.h" // for gPacificDaylightTime
|
||||||
|
#include "llcommandhandler.h" // secondlife:///app/... support
|
||||||
#include "llviewercontrol.h"
|
#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 gEventNotifier;
|
||||||
|
|
||||||
LLEventNotifier::LLEventNotifier()
|
LLEventNotifier::LLEventNotifier()
|
||||||
|
|||||||
@@ -977,12 +977,12 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po
|
|||||||
{
|
{
|
||||||
const LLMatrix4a& vol_mat = getWorldMatrix();
|
const LLMatrix4a& vol_mat = getWorldMatrix();
|
||||||
const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset);
|
const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset);
|
||||||
const LLVector4a& normal = vf.mNormals[0];
|
if (!vf.mTangents)
|
||||||
const LLVector4a& tangent = vf.mTangents[0];
|
|
||||||
if (!&tangent)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const LLVector4a& normal = vf.mNormals[0];
|
||||||
|
const LLVector4a& tangent = vf.mTangents[0];
|
||||||
|
|
||||||
LLVector4a binormal;
|
LLVector4a binormal;
|
||||||
binormal.setCross3(normal, tangent);
|
binormal.setCross3(normal, tangent);
|
||||||
|
|||||||
@@ -71,6 +71,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "cef/dullahan.h"
|
#include "cef/dullahan.h"
|
||||||
|
#if VLCPLUGIN
|
||||||
|
#include "vlc/libvlc_version.h"
|
||||||
|
#endif // LL_WINDOWS
|
||||||
|
|
||||||
extern LLMemoryInfo gSysMemory;
|
extern LLMemoryInfo gSysMemory;
|
||||||
extern U32 gPacketsIn;
|
extern U32 gPacketsIn;
|
||||||
@@ -295,9 +298,20 @@ LLFloaterAbout::LLFloaterAbout()
|
|||||||
<< DULLAHAN_VERSION_BUILD
|
<< DULLAHAN_VERSION_BUILD
|
||||||
|
|
||||||
<< " / CEF: " << CEF_VERSION
|
<< " / 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 += supportstrm.str();
|
||||||
support += '\n';
|
|
||||||
|
|
||||||
if (gPacketsIn > 0)
|
if (gPacketsIn > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -257,6 +257,14 @@ LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar")),
|
|||||||
LLFloaterAvatarList::~LLFloaterAvatarList()
|
LLFloaterAvatarList::~LLFloaterAvatarList()
|
||||||
{
|
{
|
||||||
mCleanup = true;
|
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();
|
mAvatars.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +433,13 @@ BOOL LLFloaterAvatarList::postBuild()
|
|||||||
|
|
||||||
// Get a pointer to the scroll list from the interface
|
// Get a pointer to the scroll list from the interface
|
||||||
mAvatarList = getChild<LLScrollListCtrl>("avatar_list");
|
mAvatarList = getChild<LLScrollListCtrl>("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->setCommitOnSelectionChange(true);
|
||||||
mAvatarList->setCommitCallback(boost::bind(&LLFloaterAvatarList::onSelectName,this));
|
mAvatarList->setCommitCallback(boost::bind(&LLFloaterAvatarList::onSelectName,this));
|
||||||
mAvatarList->setDoubleClickCallback(boost::bind(&LLFloaterAvatarList::onClickFocus,this));
|
mAvatarList->setDoubleClickCallback(boost::bind(&LLFloaterAvatarList::onClickFocus,this));
|
||||||
|
|||||||
@@ -342,12 +342,13 @@ void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInve
|
|||||||
//status_text.setArg("[STATUS]", getString("status_ok_text"));
|
//status_text.setArg("[STATUS]", getString("status_ok_text"));
|
||||||
status_text.setArg("[STATUS]", "");
|
status_text.setArg("[STATUS]", "");
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//status_text.setArg("[STATUS]", getString("status_bad_text"));
|
//status_text.setArg("[STATUS]", getString("status_bad_text"));
|
||||||
status_text.setArg("[STATUS]", "");
|
status_text.setArg("[STATUS]", "");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
list->addSimpleElement(status_text.getString());
|
list->addSimpleElement(status_text.getString());
|
||||||
|
|
||||||
//TODO if we are an object inside an object we should check a recuse flag and if set
|
//TODO if we are an object inside an object we should check a recuse flag and if set
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include "llfloaterevent.h"
|
#include "llfloaterevent.h"
|
||||||
|
|
||||||
// viewer project includes
|
// viewer project includes
|
||||||
#include "llcommandhandler.h"
|
|
||||||
#include "llpanelevent.h"
|
#include "llpanelevent.h"
|
||||||
|
|
||||||
// linden library includes
|
// linden library includes
|
||||||
@@ -52,29 +51,6 @@
|
|||||||
|
|
||||||
LLMap< U32, LLFloaterEventInfo* > gEventInfoInstances;
|
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)
|
LLFloaterEventInfo::LLFloaterEventInfo(const std::string& name, const U32 event_id)
|
||||||
: LLFloater(name),
|
: LLFloater(name),
|
||||||
mEventID( event_id )
|
mEventID( event_id )
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user