viewer_manifest.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. #!/usr/bin/env python
  2. # @file viewer_manifest.py
  3. # @author Ryan Williams - Lot's of modifications by Henri Beauchamp.
  4. # @brief Description of all installer viewer files, and methods for packaging
  5. # them into installers for all supported platforms.
  6. #
  7. # $LicenseInfo:firstyear=2006&license=viewergpl$
  8. #
  9. # Copyright (c) 2006-2009, Linden Research, Inc.
  10. # Copyright (c) 2009-2023, Henri Beauchamp.
  11. #
  12. # Second Life Viewer Source Code
  13. # The source code in this file ("Source Code") is provided by Linden Lab
  14. # to you under the terms of the GNU General Public License, version 2.0
  15. # ("GPL"), unless you have obtained a separate licensing agreement
  16. # ("Other License"), formally executed by you and Linden Lab. Terms of
  17. # the GPL can be found in doc/GPL-license.txt in this distribution, or
  18. # online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  19. #
  20. # There are special exceptions to the terms and conditions of the GPL as
  21. # it is applied to this Source Code. View the full text of the exception
  22. # in the file doc/FLOSS-exception.txt in this software distribution, or
  23. # online at
  24. # http://secondlifegrid.net/programs/open_source/licensing/flossexception
  25. #
  26. # By copying, modifying or distributing this software, you acknowledge
  27. # that you have read and understood your obligations described above,
  28. # and agree to abide by those obligations.
  29. #
  30. # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  31. # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  32. # COMPLETENESS OR PERFORMANCE.
  33. # $/LicenseInfo$
  34. from __future__ import print_function
  35. import sys
  36. import os.path
  37. import errno
  38. import re
  39. import tarfile
  40. # Look for scripts/lib/python in all possible parent directories (which
  41. # will result in rootdir pointing at the root of the viewer sources, since
  42. # scripts/ is a subdirectory of that root directory.
  43. # Add the full path for scripts/lib/python to sys.path, so that our custom
  44. # modules are found.
  45. rootdir = os.path.realpath(__file__)
  46. while rootdir != os.path.sep:
  47. rootdir = os.path.dirname(rootdir)
  48. dir = os.path.join(rootdir, 'scripts', 'lib', 'python')
  49. if os.path.isdir(dir):
  50. if dir not in sys.path:
  51. sys.path.insert(0, dir)
  52. break
  53. else:
  54. print("This script is not inside a valid source tree.", file=sys.stderr)
  55. sys.exit(1)
  56. if sys.version_info[0] == 3:
  57. from indra.llmanifest3 import LLManifest, main, proper_windows_path, path_ancestors
  58. else:
  59. from indra.llmanifest import LLManifest, main, proper_windows_path, path_ancestors
  60. # Where the pre-built libraries and binaries reside:
  61. binreleasedir = os.path.join(rootdir, 'bin', 'release')
  62. libreleasedir = os.path.join(rootdir, 'lib', 'release')
  63. libdebugdir = os.path.join(rootdir, 'lib', 'debug')
  64. class ViewerManifest(LLManifest):
  65. def construct(self):
  66. super(ViewerManifest, self).construct()
  67. self.exclude("*.svn*")
  68. self.path(src=os.path.join(rootdir, 'doc', 'CoolVLViewerReadme.txt'), dst="CoolVLViewerReadme.txt")
  69. self.path(src=os.path.join(rootdir, 'doc', 'RestrainedLoveReadme.txt'), dst="RestrainedLoveReadme.txt")
  70. if self.prefix(src="app_settings"):
  71. self.path("*.crt")
  72. self.path("*.ini")
  73. self.path("*.xml")
  74. self.path("*.msg")
  75. self.path("gpu_table.txt")
  76. # Include the entire shaders directory recursively
  77. self.path("shaders")
  78. # ... and the entire windlight directory
  79. self.path("windlight")
  80. # ... and the entire meshes directory
  81. self.path("meshes")
  82. # ... and the entire dictionaries directory
  83. self.path("dictionaries")
  84. self.end_prefix("app_settings")
  85. if self.prefix(src="character"):
  86. option = self.custom_option()
  87. self.path("avatar_lad.xml")
  88. self.path("attentions.xml")
  89. self.path("avatar_skeleton.xml")
  90. if option.find('puppetry') != -1:
  91. self.path("avatar_constraint.llsd")
  92. self.path("*.llm")
  93. self.path("*.tga")
  94. # Include the entire anims directory
  95. self.path("anims")
  96. self.end_prefix("character")
  97. # Include our fonts
  98. if self.prefix(src="fonts"):
  99. self.path("MtB*.ttf")
  100. self.path("profont*")
  101. self.path("Roboto*")
  102. self.path("Twemoji*")
  103. self.path("Readme.txt")
  104. self.end_prefix("fonts")
  105. # Skins
  106. if self.prefix(src="skins"):
  107. self.path("paths.xml")
  108. # include pre-decoded sounds if any
  109. if self.prefix(src="default/sounds"):
  110. self.path("*.dsf")
  111. self.end_prefix("default/sounds")
  112. # include the entire textures directory recursively
  113. if self.prefix(src="*/textures"):
  114. self.path("*.tga")
  115. self.path("*.j2c")
  116. self.path("*.jpg")
  117. self.path("*.png")
  118. self.path("textures.xml")
  119. self.end_prefix("*/textures")
  120. self.path("*/xui/*/*.xml")
  121. self.path("*/*.xml")
  122. # Local HTML files (e.g. loading screen)
  123. if self.prefix(src="*/html"):
  124. self.path("*/*/*.html")
  125. self.end_prefix("*/html")
  126. self.end_prefix("skins")
  127. def buildtype(self):
  128. return self.args['buildtype']
  129. def viewer_branding_id(self):
  130. return self.args['branding_id']
  131. def installer_prefix(self):
  132. return "CoolVLViewer_"
  133. def custom_option(self):
  134. return self.args['custom']
  135. class WindowsManifest(ViewerManifest):
  136. def construct(self):
  137. super(WindowsManifest, self).construct()
  138. config = self.args['configuration']
  139. build_dir = self.args['build']
  140. plugins = os.path.join(build_dir, "..", "media_plugins")
  141. if self.prefix(src="app_settings"):
  142. self.path("featuretable_windows.txt", dst="featuretable.txt")
  143. self.end_prefix("app_settings")
  144. # Include fallback fonts needed by Windows for special UTF-8 characters
  145. if self.prefix(src="fonts"):
  146. self.path("DejaVu*")
  147. self.end_prefix("fonts")
  148. if self.prefix(src=config, dst=""):
  149. self.path("CoolVLViewer.exe")
  150. # The config file name needs to match the exe's name.
  151. #self.path("CoolVLViewer.exe.config")
  152. self.end_prefix()
  153. # Media plugins
  154. has_cef = 0
  155. has_webrtc_plugin = 0
  156. if self.prefix(src=os.path.join(plugins, "cef", config), dst="llplugin"):
  157. if (self.path("media_plugin_cef.dll")):
  158. has_cef = 1
  159. self.end_prefix()
  160. if self.prefix(src=os.path.join(plugins, "gstreamer", config), dst="llplugin"):
  161. self.path("media_plugin_gstreamer.dll")
  162. self.end_prefix()
  163. if self.prefix(src=os.path.join(plugins, "webrtc", config), dst="llplugin"):
  164. if (self.path("voice_plugin_webrtc.dll")):
  165. has_webrtc_plugin = 1
  166. self.end_prefix()
  167. # Media plugins launcher
  168. if self.prefix(src=os.path.join(plugins, "slplugin", config), dst=""):
  169. self.path("SLPlugin.exe")
  170. self.end_prefix()
  171. if self.prefix(src=libreleasedir, dst=""):
  172. # OpenSSL libraries (either v1.0 or v1.1, which are named differently)
  173. self.path("libeay32.dll")
  174. self.path("ssleay32.dll")
  175. self.path("libssl-1_1-x64.dll")
  176. self.path("libcrypto-1_1-x64.dll")
  177. # HTTP/2
  178. self.path("nghttp2.dll")
  179. # OpenAL
  180. self.path("alut.dll")
  181. self.path("OpenAL32.dll")
  182. # Mesh 3rd party lib
  183. self.path("glod.dll")
  184. # Lua
  185. self.path("lua54.dll")
  186. # For spellchecking
  187. self.path("libhunspell.dll")
  188. # WebRTC Voice
  189. if has_webrtc_plugin != 1:
  190. self.path("llwebrtc.dll")
  191. self.end_prefix()
  192. # For CEF plugin runtimes
  193. if has_cef == 1:
  194. if self.prefix(src=binreleasedir, dst="llplugin"):
  195. self.path("dullahan_host.exe")
  196. self.path("chrome_elf.dll")
  197. self.path("libcef.dll")
  198. self.path("libEGL.dll")
  199. self.path("libGLESv2.dll")
  200. self.path("d3dcompiler*.dll")
  201. self.path("*snapshot.bin")
  202. self.path("*_blob.bin")
  203. self.end_prefix()
  204. if self.prefix(src=os.path.join(binreleasedir, 'swiftshader'), dst=os.path.join('llplugin', 'swiftshader')):
  205. self.path("libEGL.dll")
  206. self.path("libGLESv2.dll")
  207. self.end_prefix()
  208. # CEF plugin resources
  209. cefresources = os.path.join(rootdir, 'Resources')
  210. if self.prefix(src=cefresources, dst="llplugin"):
  211. # CEF 89 and older: copy all but devtools_resources.pak
  212. self.path("cef*.pak")
  213. # CEF 90 and newer:
  214. self.path("resources.pak")
  215. self.path("chrome_*.pak")
  216. # Common to all versions
  217. self.path("icudtl.dat")
  218. self.end_prefix()
  219. # CEF plugin locales
  220. if self.prefix(src=os.path.join(cefresources, 'locales'), dst=os.path.join('llplugin', 'locales')):
  221. self.path("*.pak")
  222. self.end_prefix()
  223. # For WebRTC plugin runtime
  224. if has_webrtc_plugin == 1:
  225. if self.prefix(src=libreleasedir, dst="llplugin"):
  226. self.path("llwebrtc.dll")
  227. self.end_prefix()
  228. # SLVoice client
  229. if self.prefix(src=binreleasedir, dst=""):
  230. self.path("SLVoice.exe")
  231. self.end_prefix()
  232. # Vivox libraries
  233. if self.prefix(src=libreleasedir, dst=""):
  234. self.path("ortp_x64.dll")
  235. self.path("vivoxsdk_x64.dll")
  236. self.end_prefix()
  237. # Tracy, if in use
  238. has_tracy = 0
  239. if self.prefix(src=binreleasedir, dst=""):
  240. if (self.path("Tracy.exe")):
  241. # Change for this if using Tracy v0.9.1 or newer. HB
  242. #has_tracy = 1
  243. has_tracy = 0
  244. self.end_prefix()
  245. self.path(src=os.path.join(rootdir, 'doc', 'licenses-windows.txt'), dst='licenses.txt')
  246. self.path(src=os.path.join(rootdir, 'doc', 'VivoxAUP.txt'), dst='VivoxAUP.txt')
  247. # For using FMOD for sound...
  248. if (not self.path("fmod.dll")):
  249. if (not self.path("fmod64.dll")):
  250. print("FMOD library not found: the viewer will lack FMOD support.")
  251. # mimalloc overrides, if available
  252. has_mimalloc = 0
  253. if self.prefix(src=libreleasedir, dst=""):
  254. if (self.path("mimalloc-override.dll")):
  255. has_mimalloc = 1
  256. self.path("mimalloc-redirect.dll")
  257. else:
  258. print("Skipping mimalloc - not found (normal if built without it).")
  259. self.end_prefix()
  260. if has_mimalloc == 1 and self.prefix(src=libreleasedir, dst="llplugin"):
  261. self.path("mimalloc-override.dll")
  262. self.path("mimalloc-redirect.dll")
  263. self.end_prefix()
  264. runtime_libs = os.path.join(rootdir, 'lib', 'vc143_x64')
  265. if self.prefix(src=runtime_libs, dst=""):
  266. # For use in crash reporting (generates minidumps).
  267. # Actually, including dbghelp.dll is a bad idea, since it should be
  268. # present in any modern Windows system, and including a newer
  269. # version causes issues with missing DLL dependencies (such as
  270. # "api-ms-win-core-com-l1-1-0.dll", but not only). HB
  271. #self.path("dbghelp.dll")
  272. #self.path("api-ms-win-core-com-l1-1-0.dll")
  273. # VC141 runtimes
  274. #self.path("concrt140.dll")
  275. #self.path("msvcp140*.dll")
  276. #self.path("vccorlib140.dll")
  277. self.path("msvcp140.dll")
  278. self.path("vcruntime140*.dll")
  279. option = self.custom_option()
  280. if option.find('openmp') != -1:
  281. self.path("vcomp140.dll")
  282. if (has_tracy == 1):
  283. # Needed, starting with Tracy v0.9.1. HB
  284. self.path("msvcp140_atomic_wait.dll")
  285. self.end_prefix()
  286. # Needed by the CEF plugin wrappers
  287. if self.prefix(src=runtime_libs, dst="llplugin"):
  288. # See above about dbghelp.dll and api-ms-win-core-com-l1-1-0.dll
  289. #self.path("api-ms-win-core-com-l1-1-0.dll")
  290. #self.path("concrt140.dll")
  291. #self.path("msvcp140*.dll")
  292. #self.path("vccorlib140.dll")
  293. self.path("msvcp140.dll")
  294. self.path("vcruntime140*.dll")
  295. self.end_prefix()
  296. def package_finish(self):
  297. self.package_file = 'touched.bat'
  298. # removed: we use InstallJammer to make packages
  299. return
  300. class DarwinManifest(ViewerManifest):
  301. def construct(self):
  302. # copy over the build result (this is a no-op if run within the xcode script)
  303. self.path(self.args['configuration'] + "/" + self.app_name() + ".app", dst="")
  304. # jemalloc presence flag
  305. has_jemalloc = 0
  306. if self.prefix(src="", dst="Contents"): # everything goes in Contents
  307. self.path(self.info_plist_name(), dst="Info.plist")
  308. # Copy jemalloc, if in use
  309. if (self.path(os.path.join(libreleasedir, 'libjemalloc.2.dylib'), dst='Resources/libjemalloc.2.dylib')):
  310. has_jemalloc = 1
  311. else:
  312. print("Skipping libjemalloc.dylib - not found (normal if built without it or statically linked).")
  313. # Copy additional libs in <bundle>/Contents/MacOS/ and <bundle>/Contents/Resources/
  314. self.path(os.path.join(libreleasedir, 'libhunspell-1.3.0.dylib'), dst='Resources/libhunspell-1.3.0.dylib')
  315. self.path(os.path.join(libreleasedir, 'libndofdev.dylib'), dst='Resources/libndofdev.dylib')
  316. # Copy macOS feature table into Resources/app_settings
  317. self.path(os.path.join(rootdir, 'indra', 'newview', 'app_settings', 'featuretable_macos.txt'), dst='Resources/app_settings/featuretable.txt')
  318. # Most everything goes in the Resources directory
  319. if self.prefix(src="", dst="Resources"):
  320. super(DarwinManifest, self).construct()
  321. if self.prefix("cursors_mac"):
  322. self.path("*.tif")
  323. self.end_prefix("cursors_mac")
  324. self.path(src=os.path.join(rootdir, 'doc', 'licenses-mac.txt'), dst='licenses.txt')
  325. self.path(src=os.path.join(rootdir, 'doc', 'VivoxAUP.txt'), dst='VivoxAUP.txt')
  326. self.path("CoolVLViewer.nib")
  327. self.path("cool_vl_viewer.icns")
  328. # Translations
  329. self.path("English.lproj")
  330. # SLVoice and Vivox libs
  331. if self.prefix(src=binreleasedir, dst=""):
  332. self.path("SLVoice")
  333. self.end_prefix()
  334. if self.prefix(src=libreleasedir, dst=""):
  335. self.path("libortp.dylib")
  336. self.path("libvivoxsdk.dylib")
  337. self.end_prefix()
  338. # WebRTC library.
  339. if self.prefix(src=libreleasedir, dst=""):
  340. self.path("libllwebrtc.dylib")
  341. self.end_prefix("")
  342. # Tracy, if in use
  343. if self.prefix(src=binreleasedir, dst=""):
  344. self.path("tracy")
  345. self.end_prefix()
  346. for libfile in ("libapr-1.0.dylib",
  347. "libexpat.1*.dylib",
  348. "libglod.dylib",
  349. "libnghttp2.*dylib",
  350. "libalut.*dylib",
  351. "libopenal.*dylib",
  352. "libfmod*.dylib"):
  353. self.path(os.path.join(libreleasedir, libfile), libfile)
  354. # Plugin launcher
  355. self.path("../media_plugins/slplugin/" + self.args['configuration'] + "/SLPlugin.app", "SLPlugin.app")
  356. self.end_prefix("Resources")
  357. self.end_prefix("Contents")
  358. # Media plugins presence flags
  359. has_gstreamer = 0
  360. has_cef = 0
  361. has_webrtc = 0
  362. if self.prefix(src="", dst="Contents"): # everything goes in Contents
  363. # most everything goes in the Resources directory
  364. if self.prefix(src="", dst="Resources"):
  365. # Media plugins
  366. if self.prefix(src="", dst="llplugin"):
  367. if self.path("../media_plugins/gstreamer/" + self.args['configuration'] + "/media_plugin_gstreamer.dylib", "media_plugin_gstreamer.dylib"):
  368. has_gstreamer = 1
  369. if self.path("../media_plugins/cef/" + self.args['configuration'] + "/media_plugin_cef.dylib", "media_plugin_cef.dylib"):
  370. has_cef = 1
  371. if self.path("../media_plugins/webrtc/" + self.args['configuration'] + "/voice_plugin_webrtc.dylib", "voice_plugin_webrtc.dylib"):
  372. has_webrtc = 1
  373. self.end_prefix("llplugin")
  374. # Dullahan helper app go inside SLPlugin.app
  375. if has_cef == 1 and self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"):
  376. self.path2basename(libreleasedir, 'DullahanHelper.app')
  377. pluginframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  378. if self.prefix(dst=os.path.join(
  379. 'DullahanHelper.app', 'Contents', "Frameworks")):
  380. self.cmakedirs(self.get_dst_prefix());
  381. dullahanframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  382. self.end_prefix()
  383. self.end_prefix()
  384. # Dullahan helper (GPU) app go inside SLPlugin.app
  385. if has_cef == 1 and self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"):
  386. self.path2basename(libreleasedir, 'DullahanHelper (GPU).app')
  387. pluginframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  388. if self.prefix(dst=os.path.join(
  389. 'DullahanHelper (GPU).app', 'Contents', "Frameworks")):
  390. self.cmakedirs(self.get_dst_prefix());
  391. dullahanframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  392. self.end_prefix()
  393. self.end_prefix()
  394. # Dullahan helper (Plugin) app go inside SLPlugin.app
  395. if has_cef == 1 and self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"):
  396. self.path2basename(libreleasedir, 'DullahanHelper (Plugin).app')
  397. pluginframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  398. if self.prefix(dst=os.path.join(
  399. 'DullahanHelper (Plugin).app', 'Contents', "Frameworks")):
  400. self.cmakedirs(self.get_dst_prefix());
  401. dullahanframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  402. self.end_prefix()
  403. self.end_prefix()
  404. # Dullahan helper (Renderer) app go inside SLPlugin.app
  405. if has_cef == 1 and self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"):
  406. self.path2basename(libreleasedir, 'DullahanHelper (Renderer).app')
  407. pluginframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  408. if self.prefix(dst=os.path.join(
  409. 'DullahanHelper (Renderer).app', 'Contents', "Frameworks")):
  410. self.cmakedirs(self.get_dst_prefix());
  411. dullahanframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework');
  412. self.end_prefix()
  413. self.end_prefix()
  414. self.end_prefix("Resources")
  415. # CEF framework goes inside Cool VL Viewer.app/Contents/Frameworks
  416. if has_cef == 1:
  417. if self.prefix(src="", dst="Frameworks"):
  418. frameworkfile="Chromium Embedded Framework.framework"
  419. self.path2basename(libreleasedir, frameworkfile)
  420. self.end_prefix("Frameworks")
  421. # This code constructs a relative path from the
  422. # target framework folder back to the location of the symlink.
  423. # It needs to be relative so that the symlink still works when
  424. # (as is normal) the user moves the app bunlde out of the DMG
  425. # and into the /Applications folder. Note we also call 'raise'
  426. # to terminate the process if we get an error since without
  427. # this symlink, Second Life web media can't possibly work.
  428. # Real Framework folder:
  429. # Cool VL Viewer.app/Contents/Frameworks/Chromium Embedded Framework.framework/
  430. # Location of symlink and why it is relative
  431. # Cool VL Viewer.app/Contents/Resources/SLPlugin.app/Contents/Frameworks/Chromium Embedded Framework.framework/
  432. frameworkpath = os.path.join(os.pardir, os.pardir, os.pardir, os.pardir, "Frameworks", "Chromium Embedded Framework.framework")
  433. try:
  434. symlinkf(frameworkpath, pluginframeworkpath)
  435. except OSError as err:
  436. print("Cannot symlink %s -> %s: %s" % (frameworkpath, pluginframeworkpath, err))
  437. raise
  438. try:
  439. symlinkf(frameworkpath, dullahanframeworkpath)
  440. except OSError as err:
  441. print("Cannot symlink %s -> %s: %s" % (frameworkpath, dullahanframeworkpath, err))
  442. raise
  443. self.end_prefix("Contents")
  444. # Fix library paths in plugins:
  445. self.run_command('install_name_tool -change @executable_path/../Resources/libexpat.1.dylib @executable_path/../../../libexpat.1.dylib "%(slplugin)s"' %
  446. { 'slplugin': self.dst_path_of('Contents/Resources/SLPlugin.app/Contents/MacOS/SLPlugin')})
  447. self.run_command('install_name_tool -change @executable_path/../Resources/libnghttp2.14.14.0.dylib @executable_path/../../../libnghttp2.14.14.0.dylib "%(slplugin)s"' %
  448. { 'slplugin': self.dst_path_of('Contents/Resources/SLPlugin.app/Contents/MacOS/SLPlugin')})
  449. if has_gstreamer == 1:
  450. self.run_command('install_name_tool -change @executable_path/../Resources/libexpat.1.dylib @executable_path/../../../libexpat.1.dylib "%(plugin)s"' %
  451. { 'plugin': self.dst_path_of('Contents/Resources/llplugin/media_plugin_gstreamer.dylib')})
  452. if has_cef == 1:
  453. # fix up media_plugin.dylib so it knows where to look for CEF files it needs
  454. self.run_command('install_name_tool -change "@rpath/Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" "@executable_path/../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework" "../Cool VL Viewer.app/Contents/Resources/llplugin/media_plugin_cef.dylib"' %
  455. { 'config' : self.args['configuration'] })
  456. self.run_command('install_name_tool -change @executable_path/../Resources/libexpat.1.dylib @executable_path/../../../libexpat.1.dylib "%(plugin)s"' %
  457. { 'plugin': self.dst_path_of('Contents/Resources/llplugin/media_plugin_cef.dylib')})
  458. if has_webrtc == 1:
  459. self.run_command('install_name_tool -change @executable_path/../Resources/libllwebrtc.dylib @executable_path/../../../libllwebrtc.1.dylib "%(plugin)s"' %
  460. { 'plugin': self.dst_path_of('Contents/Resources/llplugin/voice_plugin_webrtc.dylib')})
  461. # Patch libjemalloc.dylib path in image
  462. if has_jemalloc == 1:
  463. self.run_command('install_name_tool -change libjemalloc.2.dylib @executable_path/../Resources/libjemalloc.2.dylib "%(viewer_binary)s"' %
  464. { 'viewer_binary': self.dst_path_of('Contents/MacOS/'+self.app_name())})
  465. self.run_command('install_name_tool -change libjemalloc.2.dylib @executable_path/../../../libjemalloc.2.dylib "%(slplugin)s"' %
  466. { 'slplugin': self.dst_path_of('Contents/Resources/SLPlugin.app/Contents/MacOS/SLPlugin')})
  467. if has_gstreamer == 1:
  468. self.run_command('install_name_tool -change libjemalloc.2.dylib @executable_path/../../../libjemalloc.2.dylib "%(plugin)s"' %
  469. { 'plugin': self.dst_path_of('Contents/Resources/llplugin/media_plugin_gstreamer.dylib')})
  470. if has_cef == 1:
  471. self.run_command('install_name_tool -change libjemalloc.2.dylib @executable_path/../../../libjemalloc.2.dylib "%(plugin)s"' %
  472. { 'plugin': self.dst_path_of('Contents/Resources/llplugin/media_plugin_cef.dylib')})
  473. # NOTE: the -S argument to strip causes it to keep enough info for
  474. # annotated backtraces (i.e. function names in the crash log). 'strip' with no
  475. # arguments yields a slightly smaller binary but makes crash logs mostly useless.
  476. # This may be desirable for the final release. Or not.
  477. if self.buildtype().lower() == 'release':
  478. if ("package" in self.args['actions'] or
  479. "unpacked" in self.args['actions']):
  480. self.run_command('strip -S "%(viewer_binary)s"' %
  481. { 'viewer_binary' : self.dst_path_of('Contents/MacOS/'+self.app_name())})
  482. # patch libfmod.dylib path in image
  483. self.run_command('install_name_tool -change @rpath/libfmod.dylib @executable_path/../Resources/libfmod.dylib "%(viewer_binary)s"' %
  484. { 'viewer_binary': self.dst_path_of('Contents/MacOS/'+self.app_name())})
  485. def app_name(self):
  486. return "Cool VL Viewer"
  487. def info_plist_name(self):
  488. return "Info-CoolVLViewer.plist"
  489. def package_finish(self):
  490. # removed, not using this anymore
  491. pass
  492. class LinuxManifest(ViewerManifest):
  493. def construct(self):
  494. super(LinuxManifest, self).construct()
  495. notdebug = "rel" in self.buildtype().lower()
  496. release = "release" in self.buildtype().lower()
  497. # For the Release build type (which lacks the symbols in the viewer
  498. # binary), save the build directory path into .buildir, so that our
  499. # wrapper script can find the path of the CoolVLViewer.debug file, when
  500. # it is still around...
  501. if release:
  502. # Save the build directory path into .buildir, so that our wrapper
  503. # script can find the path of the CoolVLViewer.debug file, when it
  504. # is still around...
  505. builddir_file = os.path.join(self.get_build_prefix(), '.buildir')
  506. ofile = open(builddir_file, "w")
  507. ofile.write(self.get_build_prefix())
  508. ofile.close()
  509. self.path(builddir_file, '.buildir')
  510. # Features table
  511. if self.prefix(src="app_settings"):
  512. self.path("featuretable_linux.txt", dst="featuretable.txt")
  513. self.end_prefix("app_settings")
  514. # Licenses and README files
  515. self.path(os.path.join(rootdir, 'doc', 'licenses-linux.txt'), "licenses.txt")
  516. self.path(os.path.join(rootdir, 'doc', 'README-linux.txt'), "README-linux.txt")
  517. self.path(os.path.join(rootdir, 'doc', 'VivoxAUP.txt'), 'VivoxAUP.txt')
  518. # Icons
  519. if self.prefix("res-sdl"):
  520. self.path("*")
  521. self.end_prefix("res-sdl")
  522. # Create a relative symlink for icon_name(); sadly, os.symlink()
  523. # cannot create relative links, so we call 'ln' instead.
  524. os.system("cd " + self.get_dst_prefix() + ";ln -s ./res-sdl/" + self.icon_name("48") + " " + self.icon_name())
  525. else:
  526. self.path("res/" + self.icon_name(), self.icon_name())
  527. # Various wrapper and utility scripts
  528. if self.prefix("linux_tools", dst=""):
  529. self.path("wrapper.sh", self.wrapper_name())
  530. self.path("viewer.conf", self.conf_name())
  531. self.path("handle_secondlifeprotocol.sh", "bin/handle_secondlifeprotocol.sh")
  532. self.path("register_secondlifeprotocol.sh", "bin/register_secondlifeprotocol.sh")
  533. self.path("messagebox.sh", "bin/messagebox.sh")
  534. self.path("launch_url.sh", "bin/launch_url.sh")
  535. self.path("*.tk")
  536. self.path("install-wine-SLVoice.sh")
  537. self.end_prefix("linux_tools")
  538. # Viewer binary, stripped or not, depending on build type
  539. # Note: for both the Debug and RelWithDebInfo types, we want the
  540. # unstripped binary.
  541. if release:
  542. self.path("CoolVLViewer-stripped", "bin/" + self.binary_name())
  543. else:
  544. self.path("CoolVLViewer", "bin/" + self.binary_name())
  545. # Other libraries
  546. if self.prefix(libreleasedir, dst="lib"):
  547. self.path("libapr-1.so.0.*.*", "libapr-1.so.0")
  548. # IMPORTANT: *never* bundle libfontconfig.so.1 with the viewer (and
  549. # never link the latter with a static libfontconfig.a library): the
  550. # version in use on the user's system might not correspond to the
  551. # one we use to build the viewer and the /etc/fonts/ config files
  552. # might not be understood by the latter. Instead, let's use the
  553. # system library, which matches the /etc/fonts/ config files.
  554. #self.path("libfontconfig.so.1.12.0", "libfontconfig.so.1")
  555. # This should exist on all Linux distros (part of util-linux).
  556. #self.path("libuuid.so.1.3.0", "libuuid.so.1")
  557. self.path("libalut.so.0.0.0", "libalut.so.0")
  558. # SDL2
  559. self.path("libSDL2-2.0.so.0.*.*", "libSDL2-2.0.so.0")
  560. # This too seemed like a good idea, but it causes issues on some
  561. # systems...
  562. #self.path("libopenal.so.1.12.854", "libopenal.so.1")
  563. self.path("libglod.so") # Mesh support
  564. self.end_prefix("lib")
  565. # Boost libraries, when dynamically linked. Only the Debug build uses
  566. # their debug version.
  567. if (notdebug and self.prefix(libreleasedir, dst="lib")):
  568. if (self.path("libboost_context-mt*.so.1.*.0")):
  569. self.path("libboost_fiber-mt*.so.1.*.0")
  570. self.path("libboost_filesystem-mt*.so.1.*.0")
  571. self.path("libboost_program_options-mt*.so.1.*.0")
  572. self.path("libboost_thread-mt*.so.1.*.0")
  573. self.path("libboost_atomic-mt*.so.1.*.0")
  574. else:
  575. print("Skipping boost libraries: assumed statically linked.")
  576. self.end_prefix("lib")
  577. if (not notdebug and self.prefix(libdebugdir, dst="lib")):
  578. if (self.path("libboost_context*.so.1.*.0")):
  579. self.path("libboost_fiber-mt*.so.1.*.0")
  580. self.path("libboost_filesystem-mt*.so.1.*.0")
  581. self.path("libboost_program_options-mt*.so.1.*.0")
  582. self.path("libboost_thread-mt*.so.1.*.0")
  583. self.path("libboost_atomic-mt*.so.1.*.0")
  584. else:
  585. print("Skipping boost libraries: assumed statically linked.")
  586. self.end_prefix("lib")
  587. # Shared jemalloc library, maybe...
  588. no_jemalloc = 1
  589. if (not notdebug and self.prefix(libdebugdir, dst="lib")):
  590. if (self.path("libjemalloc.so.2")):
  591. no_jemalloc = 0
  592. self.end_prefix("lib")
  593. if (no_jemalloc and self.prefix(libreleasedir, dst="lib")):
  594. self.path("libjemalloc.so.2")
  595. self.end_prefix("lib")
  596. # Media plugins
  597. has_cef = 0
  598. if self.prefix(src="", dst="bin/llplugin"):
  599. if (self.path("../media_plugins/cef/media_plugin_cef.so", "media_plugin_cef.so")):
  600. has_cef = 1
  601. self.path("../media_plugins/gstreamer/media_plugin_gstreamer.so", "media_plugin_gstreamer.so")
  602. self.path("../media_plugins/webrtc/voice_plugin_webrtc.so", "voice_plugin_webrtc.so")
  603. # SLPlugin
  604. self.path("../media_plugins/slplugin/SLPlugin", "SLPlugin")
  605. self.end_prefix("bin/llplugin")
  606. # For CEF3 plugin runtimes (starting with CEF 73, everything goes in
  607. # lib/, dullahan_host excepted).
  608. if has_cef == 1:
  609. if self.prefix(src=libreleasedir, dst="lib"):
  610. self.path("libcef.so")
  611. self.path("libEGL.so")
  612. self.path("libGLESv2.so")
  613. self.path("libvk_swiftshader.so")
  614. self.path("*blob.bin")
  615. self.path("*snapshot.bin")
  616. self.path("*.pak")
  617. self.path("icudtl.dat")
  618. self.end_prefix("lib")
  619. if self.prefix(src=os.path.join(libreleasedir, 'swiftshader'), dst="lib/swiftshader"):
  620. self.path("*.so")
  621. self.end_prefix("lib/swiftshader")
  622. # The locales/ directory now goes along with the libcef library,
  623. if self.prefix(src=os.path.join(libreleasedir, 'locales'), dst="lib/locales"):
  624. self.path("*.pak")
  625. self.end_prefix("lib/locales")
  626. if self.prefix(src=binreleasedir, dst="bin/llplugin"):
  627. self.path("dullahan_host")
  628. self.end_prefix("bin/llplugin")
  629. # 64 bits FMOD library:
  630. if (self.prefix(libreleasedir, dst="lib")):
  631. if (not self.path("libfmod.so.14.*", "libfmod.so.14")):
  632. print("FMOD library not found: the viewer will lack FMOD support.")
  633. self.end_prefix("lib")
  634. # Vivox runtime (they are 32 bits program and libraries)
  635. if self.prefix(src=binreleasedir, dst="bin"):
  636. self.path("SLVoice")
  637. self.end_prefix("bin")
  638. if self.prefix(src=libreleasedir, dst="lib"):
  639. self.path("libortp.so")
  640. self.path("libsndfile.so.1")
  641. self.path("libvivoxoal.so.1")
  642. self.path("libvivoxplatform.so")
  643. self.path("libvivoxsdk.so")
  644. self.end_prefix("lib")
  645. # WebRTC library.
  646. if self.prefix(src=libreleasedir, dst="lib"):
  647. self.path("libllwebrtc.so")
  648. self.end_prefix("lib")
  649. # Tracy, if in use
  650. if self.prefix(src=binreleasedir, dst="bin"):
  651. self.path("tracy")
  652. self.end_prefix()
  653. def wrapper_name(self):
  654. return "cool_vl_viewer"
  655. def conf_name(self):
  656. return "cool_vl_viewer.conf"
  657. def binary_name(self):
  658. return "cool_vl_viewer-bin"
  659. def icon_name(self, size = ""):
  660. return "cvlv_icon" + size + ".png"
  661. def package_finish(self):
  662. if 'installer_name' in self.args:
  663. installer_name = self.args['installer_name']
  664. else:
  665. installer_name_components = [self.installer_prefix(), 'x86_64']
  666. installer_name_components.extend(self.args['version'])
  667. installer_name = "_".join(installer_name_components)
  668. # Fix access permissions
  669. self.run_command("""
  670. find '%(dst)s' -type d -print0 | xargs -0 --no-run-if-empty chmod 755;
  671. find '%(dst)s' -type f -perm 0700 -print0 | xargs -0 --no-run-if-empty chmod 0755;
  672. find '%(dst)s' -type f -perm 0500 -print0 | xargs -0 --no-run-if-empty chmod 0555;
  673. find '%(dst)s' -type f -perm 0600 -print0 | xargs -0 --no-run-if-empty chmod 0644;
  674. find '%(dst)s' -type f -perm 0400 -print0 | xargs -0 --no-run-if-empty chmod 0444;
  675. true""" % {'dst':self.get_dst_prefix() })
  676. self.package_file = installer_name + '.tar.bz2'
  677. # Temporarily move directory tree so that it has the right name in the
  678. # tarfile
  679. self.run_command("rm -rf '%(inst)s/'" % {
  680. 'inst': self.build_path_of(installer_name)})
  681. self.run_command("mv '%(dst)s' '%(inst)s'" % {
  682. 'dst': self.get_dst_prefix(),
  683. 'inst': self.build_path_of(installer_name)})
  684. if "release" in self.buildtype().lower():
  685. # Strip the shared libraries from their debug symbols to save space
  686. # and speed up loading
  687. self.run_command("strip -s %(libs)s" % {
  688. 'libs': self.build_path_of(installer_name + "/lib/*.so*")})
  689. # --numeric-owner hides the username of the builder for security etc.
  690. if (self.run_command("tar -C '%(dir)s' --numeric-owner -cjf "
  691. "'%(inst_path)s.tar.bz2' %(inst_name)s" % {
  692. 'dir': self.get_build_prefix(),
  693. 'inst_name': installer_name,
  694. 'inst_path':self.build_path_of(installer_name)})):
  695. self.run_command("mv '%(inst)s' '%(dst)s'" % {
  696. 'dst': self.get_dst_prefix(),
  697. 'inst': self.build_path_of(installer_name)})
  698. ################################################################
  699. def symlinkf(src, dst):
  700. """
  701. Like ln -sf, but uses os.symlink() instead of running ln.
  702. """
  703. try:
  704. os.symlink(src, dst)
  705. except OSError as err:
  706. if err.errno != errno.EEXIST:
  707. raise
  708. # We could just blithely attempt to remove and recreate the target
  709. # file, but that strategy doesn't work so well if we do not have
  710. # permissions to remove it. Check to see if it's already the
  711. # symlink we want, which is the usual reason for EEXIST.
  712. if not (os.path.islink(dst) and os.readlink(dst) == src):
  713. # Here either dst is not a symlink or it is the wrong symlink.
  714. # Remove and recreate. Caller will just have to deal with any
  715. # exceptions at this stage.
  716. os.remove(dst)
  717. os.symlink(src, dst)
  718. if __name__ == "__main__":
  719. main()