makepackage.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149
  1. #!/usr/bin/env python
  2. import sys
  3. import os
  4. import shutil
  5. import glob
  6. import re
  7. import subprocess
  8. from makepandacore import *
  9. from installpanda import *
  10. INSTALLER_DEB_FILE = """
  11. Package: panda3dMAJOR
  12. Version: VERSION
  13. Section: libdevel
  14. Priority: optional
  15. Architecture: ARCH
  16. Essential: no
  17. Depends: DEPENDS
  18. Recommends: RECOMMENDS
  19. Provides: PROVIDES
  20. Conflicts: PROVIDES
  21. Replaces: PROVIDES
  22. Maintainer: rdb <[email protected]>
  23. Installed-Size: INSTSIZE
  24. Description: Panda3D free 3D engine SDK
  25. Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost.
  26. Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface.
  27. .
  28. This package contains the SDK for development with Panda3D.
  29. """
  30. # We're not putting "python" in the "Requires" field,
  31. # since the rpm-based distros don't have a common
  32. # naming for the Python package.
  33. INSTALLER_SPEC_FILE = """
  34. Summary: The Panda3D free 3D engine SDK
  35. Name: panda3d
  36. Version: VERSION
  37. Release: RPMRELEASE
  38. License: BSD License
  39. Group: Development/Libraries
  40. BuildRoot: PANDASOURCE/targetroot
  41. %description
  42. Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost.
  43. Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface.
  44. This package contains the SDK for development with Panda3D.
  45. %post
  46. /sbin/ldconfig
  47. %postun
  48. /sbin/ldconfig
  49. %files
  50. %defattr(-,root,root)
  51. /etc/Confauto.prc
  52. /etc/Config.prc
  53. /usr/share/panda3d
  54. /etc/ld.so.conf.d/panda3d.conf
  55. /usr/%_lib/panda3d
  56. /usr/include/panda3d
  57. """
  58. INSTALLER_SPEC_FILE_MIME = """\
  59. /usr/share/mime-info/panda3d.mime
  60. /usr/share/mime-info/panda3d.keys
  61. /usr/share/mime/packages/panda3d.xml
  62. /usr/share/application-registry/panda3d.applications
  63. """
  64. # plist file for Mac OSX
  65. Info_plist = """\
  66. <?xml version="1.0" encoding="UTF-8"?>
  67. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  68. <plist version="1.0">
  69. <dict>
  70. <key>CFBundleIdentifier</key>
  71. <string>{package_id}</string>
  72. <key>CFBundleShortVersionString</key>
  73. <string>{version}</string>
  74. <key>IFPkgFlagRelocatable</key>
  75. <false/>
  76. <key>IFPkgFlagAuthorizationAction</key>
  77. <string>RootAuthorization</string>
  78. <key>IFPkgFlagAllowBackRev</key>
  79. <true/>
  80. </dict>
  81. </plist>
  82. """
  83. # FreeBSD pkg-descr
  84. INSTALLER_PKG_DESCR_FILE = """
  85. Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost.
  86. Panda3D's intended game-development language is Python. The engine itself is written in C++, and utilizes an automatic wrapper-generator to expose the complete functionality of the engine in a Python interface.
  87. This package contains the SDK for development with Panda3D.
  88. WWW: https://www.panda3d.org/
  89. """
  90. # FreeBSD PKG Manifest template file
  91. INSTALLER_PKG_MANIFEST_FILE = """
  92. name: NAME
  93. version: VERSION
  94. arch: ARCH
  95. origin: ORIGIN
  96. comment: "Panda3D free 3D engine SDK"
  97. www: https://www.panda3d.org
  98. maintainer: rdb <[email protected]>
  99. prefix: /usr/local
  100. flatsize: INSTSIZEMB
  101. deps: {DEPENDS}
  102. """
  103. # Since we're adding a bunch of install scripts to the macOS intaller, we'll
  104. # put the platform-checking code in some variables to reduce repetition.
  105. MACOS_SCRIPT_PREFIX = """\
  106. #!/bin/bash
  107. IFS=.
  108. read -a version_info <<< "`sw_vers -productVersion`"
  109. if (( ${version_info[0]} == 10 && ${version_info[1]} < 15 )); then
  110. """
  111. MACOS_SCRIPT_POSTFIX = """\
  112. fi
  113. """
  114. EXCLUDE_BINARIES = ["deploy-stub", "deploy-stubw", "run_tests"]
  115. def MakeInstallerNSIS(version, file, title, installdir, compressor="lzma", **kwargs):
  116. outputdir = GetOutputDir()
  117. if os.path.isfile(file):
  118. os.remove(file)
  119. elif os.path.isdir(file):
  120. shutil.rmtree(file)
  121. if GetTargetArch() == 'x64':
  122. regview = '64'
  123. else:
  124. regview = '32'
  125. print("Building " + title + " installer at %s" % (file))
  126. if compressor != "lzma":
  127. print("Note: you are using zlib, which is faster, but lzma gives better compression.")
  128. if os.path.exists("nsis-output.exe"):
  129. os.remove("nsis-output.exe")
  130. WriteFile(outputdir + "/tmp/__init__.py", "")
  131. nsis_defs = {
  132. 'COMPRESSOR': compressor,
  133. 'TITLE': title,
  134. 'INSTALLDIR': installdir,
  135. 'OUTFILE': '..\\' + file,
  136. 'BUILT': '..\\' + outputdir,
  137. 'SOURCE': '..',
  138. 'REGVIEW': regview,
  139. 'MAJOR_VER': '.'.join(version.split('.')[:2]),
  140. }
  141. # Are we shipping a version of Python?
  142. if os.path.isfile(os.path.join(outputdir, "python", "python.exe")):
  143. py_dlls = (
  144. glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9].dll"))
  145. + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9]_d.dll"))
  146. + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9][0-9].dll"))
  147. + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9][0-9]_d.dll"))
  148. )
  149. assert py_dlls
  150. py_dll = os.path.basename(py_dlls[0])
  151. py_dllver = py_dll.strip(".DHLNOPTY_dhlnopty")
  152. pyver = py_dllver[0] + '.' + py_dllver[1:]
  153. if GetTargetArch() != 'x64':
  154. pyver += '-32'
  155. nsis_defs['INCLUDE_PYVER'] = pyver
  156. if GetHost() == 'windows':
  157. cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2'
  158. for item in nsis_defs.items():
  159. cmd += ' /D%s="%s"' % item
  160. else:
  161. cmd = 'makensis -V2'
  162. for item in nsis_defs.items():
  163. cmd += ' -D%s="%s"' % item
  164. cmd += ' "makepanda\\installer.nsi"'
  165. oscmd(cmd)
  166. def MakeDebugSymbolZipArchive(zipname):
  167. import zipfile
  168. outputdir = GetOutputDir()
  169. zip = zipfile.ZipFile(zipname + '.zip', 'w', zipfile.ZIP_DEFLATED)
  170. for fn in glob.glob(os.path.join(outputdir, 'bin', '*.pdb')):
  171. zip.write(fn, 'bin/' + os.path.basename(fn))
  172. for fn in glob.glob(os.path.join(outputdir, 'panda3d', '*.pdb')):
  173. zip.write(fn, 'panda3d/' + os.path.basename(fn))
  174. for fn in glob.glob(os.path.join(outputdir, 'plugins', '*.pdb')):
  175. zip.write(fn, 'plugins/' + os.path.basename(fn))
  176. for fn in glob.glob(os.path.join(outputdir, 'python', '*.pdb')):
  177. zip.write(fn, 'python/' + os.path.basename(fn))
  178. for fn in glob.glob(os.path.join(outputdir, 'python', 'DLLs', '*.pdb')):
  179. zip.write(fn, 'python/DLLs/' + os.path.basename(fn))
  180. zip.close()
  181. def MakeDebugSymbolSevenZipArchive(zipname, compressor):
  182. zipname += '.7z'
  183. flags = ['-t7z', '-y']
  184. if compressor == 'zlib':
  185. # This will still build an LZMA2 archive by default,
  186. # but will complete significantly faster.
  187. flags.extend(['-mx=3'])
  188. # Remove the old archive before proceeding.
  189. if os.path.exists(zipname):
  190. os.remove(zipname)
  191. outputdir = GetOutputDir()
  192. # We'll be creating the archive inside the output
  193. # directory, so we need the relative path to the archive
  194. zipname = os.path.relpath(zipname, outputdir)
  195. # Create a 7-zip archive, including all *.pdb files
  196. # that are not in the tmp folder
  197. cmd = [GetSevenZip(), 'a']
  198. cmd.extend(flags)
  199. cmd.extend(['-ir!*.pdb', '-x!' + os.path.join('tmp', '*'), zipname])
  200. subprocess.call(cmd, stdout=subprocess.DEVNULL, cwd=outputdir)
  201. def MakeDebugSymbolArchive(zipname, compressor):
  202. if HasSevenZip():
  203. MakeDebugSymbolSevenZipArchive(zipname, compressor)
  204. else:
  205. MakeDebugSymbolZipArchive(zipname)
  206. def MakeInstallerLinux(version, debversion=None, rpmversion=None, rpmrelease=1,
  207. python_versions=[], **kwargs):
  208. outputdir = GetOutputDir()
  209. # Only pack the versions of Python included with this Ubuntu version.
  210. install_python_versions = []
  211. for version_info in python_versions:
  212. if os.path.isdir("/usr/lib/python" + version_info["version"]):
  213. install_python_versions.append(version_info)
  214. major_version = '.'.join(version.split('.')[:2])
  215. if not debversion:
  216. debversion = version
  217. if not rpmversion:
  218. rpmversion = version
  219. # Clean and set up a directory to install Panda3D into
  220. oscmd("rm -rf targetroot data.tar.gz control.tar.gz panda3d.spec")
  221. oscmd("mkdir -m 0755 targetroot")
  222. dpkg_present = False
  223. if os.path.exists("/usr/bin/dpkg-architecture") and os.path.exists("/usr/bin/dpkg-deb"):
  224. dpkg_present = True
  225. rpmbuild_present = False
  226. if os.path.exists("/usr/bin/rpmbuild"):
  227. rpmbuild_present = True
  228. if dpkg_present and rpmbuild_present:
  229. Warn("both dpkg and rpmbuild present.")
  230. if dpkg_present:
  231. # Invoke installpanda.py to install it into a temporary dir
  232. lib_dir = GetDebLibDir()
  233. InstallPanda(
  234. destdir="targetroot",
  235. prefix="/usr",
  236. outputdir=outputdir,
  237. libdir=lib_dir,
  238. python_versions=install_python_versions,
  239. )
  240. oscmd("chmod -R 755 targetroot/usr/share/panda3d")
  241. oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1")
  242. oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/")
  243. oscmd("dpkg --print-architecture > " + outputdir + "/tmp/architecture.txt")
  244. pkg_arch = ReadFile(outputdir + "/tmp/architecture.txt").strip()
  245. txt = INSTALLER_DEB_FILE[1:]
  246. txt = (
  247. txt.replace("VERSION", debversion)
  248. .replace("ARCH", pkg_arch)
  249. .replace("MAJOR", major_version)
  250. )
  251. txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024))
  252. oscmd("mkdir -m 0755 -p targetroot/DEBIAN")
  253. oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums")
  254. oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums")
  255. WriteFile("targetroot/DEBIAN/conffiles", "/etc/Config.prc\n")
  256. WriteFile("targetroot/DEBIAN/postinst", "#!/bin/sh\necho running ldconfig\nldconfig\n")
  257. oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm")
  258. # Determine the package name and the locations that
  259. # dpkg-shlibdeps should look in for executables.
  260. pkg_version = debversion
  261. pkg_name = "panda3d" + major_version
  262. lib_pattern = "debian/%s/usr/%s/panda3d/*.so*" % (pkg_name, lib_dir)
  263. bin_pattern = "debian/%s/usr/bin/*" % (pkg_name)
  264. # dpkg-shlibdeps looks in the debian/{pkg_name}/DEBIAN/shlibs directory
  265. # and also expects a debian/control file, so we create this dummy set-up.
  266. oscmd("mkdir targetroot/debian")
  267. oscmd("ln -s .. targetroot/debian/" + pkg_name)
  268. WriteFile("targetroot/debian/control", "")
  269. dpkg_shlibdeps = "dpkg-shlibdeps"
  270. if GetVerbose():
  271. dpkg_shlibdeps += " -v"
  272. pkg_name = "panda3d" + major_version
  273. pkg_dir = "debian/panda3d" + major_version
  274. # Generate a symbols file so that other packages can know which symbols we export.
  275. oscmd(f"cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v{pkg_version} -p{pkg_name} -e{lib_pattern}")
  276. # Library dependencies are required, binary dependencies are recommended.
  277. # We explicitly exclude libphysx-extras since we don't want to depend on PhysX.
  278. oscmd(f"cd targetroot && LD_LIBRARY_PATH=usr/{lib_dir}/panda3d {dpkg_shlibdeps} -Tdebian/substvars_dep --ignore-missing-info -x{pkg_name} -xlibphysx-extras {lib_pattern}")
  279. oscmd(f"cd targetroot && LD_LIBRARY_PATH=usr/{lib_dir}/panda3d {dpkg_shlibdeps} -Tdebian/substvars_rec --ignore-missing-info -x{pkg_name} {bin_pattern}")
  280. # Parse the substvars files generated by dpkg-shlibdeps.
  281. depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
  282. recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip()
  283. provides = "panda3d"
  284. # Require at least one of the Python versions we built for.
  285. if install_python_versions:
  286. depends += ", " + " | ".join("python" + version_info["version"] for version_info in install_python_versions)
  287. # But recommend the system version of Python 3.
  288. recommends += ", python3"
  289. recommends += ", python3-tk"
  290. provides += ", python3-panda3d"
  291. if not PkgSkip("NVIDIACG"):
  292. depends += ", nvidia-cg-toolkit"
  293. # Write back the dependencies, and delete the dummy set-up.
  294. txt = txt.replace("DEPENDS", depends.strip(', '))
  295. txt = txt.replace("RECOMMENDS", recommends.strip(', '))
  296. txt = txt.replace("PROVIDES", provides.strip(', '))
  297. WriteFile("targetroot/DEBIAN/control", txt)
  298. oscmd("rm -rf targetroot/debian")
  299. # Package it all up into a .deb file.
  300. oscmd("chmod -R 755 targetroot/DEBIAN")
  301. oscmd("chmod 644 targetroot/DEBIAN/control targetroot/DEBIAN/md5sums")
  302. oscmd("chmod 644 targetroot/DEBIAN/conffiles targetroot/DEBIAN/symbols")
  303. oscmd("fakeroot dpkg-deb -Zxz -b targetroot %s_%s_%s.deb" % (pkg_name, pkg_version, pkg_arch))
  304. elif rpmbuild_present:
  305. # Invoke installpanda.py to install it into a temporary dir
  306. InstallPanda(
  307. destdir="targetroot",
  308. prefix="/usr",
  309. outputdir=outputdir,
  310. libdir=GetRPMLibDir(),
  311. python_versions=install_python_versions,
  312. )
  313. oscmd("chmod -R 755 targetroot/usr/share/panda3d")
  314. oscmd("rpm -E '%_target_cpu' > " + outputdir + "/tmp/architecture.txt")
  315. arch = ReadFile(outputdir + "/tmp/architecture.txt").strip()
  316. pandasource = os.path.abspath(os.getcwd())
  317. txt = INSTALLER_SPEC_FILE[1:]
  318. # Add the MIME associations if we have pview or pstats
  319. if not PkgSkip("PVIEW") or not PkgSkip("PSTATS"):
  320. txt += INSTALLER_SPEC_FILE_MIME
  321. if not PkgSkip("PVIEW"):
  322. txt += "/usr/share/applications/pview.desktop\n"
  323. if not PkgSkip("PSTATS"):
  324. txt += "/usr/share/applications/pstats.desktop\n"
  325. # Add the platform-specific Python directories.
  326. dirs = set()
  327. for version_info in install_python_versions:
  328. dirs.add(version_info["platlib"])
  329. dirs.add(version_info["purelib"])
  330. for dir in dirs:
  331. txt += dir + "\n"
  332. # Add the binaries in /usr/bin explicitly to the spec file
  333. for base in os.listdir(outputdir + "/bin"):
  334. if base not in EXCLUDE_BINARIES:
  335. txt += "/usr/bin/%s\n" % (base)
  336. # Write out the spec file.
  337. txt = txt.replace("VERSION", rpmversion)
  338. txt = txt.replace("RPMRELEASE", str(rpmrelease))
  339. txt = txt.replace("PANDASOURCE", pandasource)
  340. WriteFile("panda3d.spec", txt)
  341. oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec")
  342. oscmd("mv "+arch+"/panda3d-"+rpmversion+"-"+rpmrelease+"."+arch+".rpm .")
  343. oscmd("rm -rf "+arch, True)
  344. else:
  345. exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
  346. def MakeInstallerOSX(version, python_versions=[], installdir=None, **kwargs):
  347. outputdir = GetOutputDir()
  348. if installdir is None:
  349. installdir = "/Library/Developer/Panda3D"
  350. dmg_name = "Panda3D-" + version
  351. if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."):
  352. dmg_name += "-py" + python_versions[0]["version"]
  353. dmg_name += ".dmg"
  354. if os.path.isfile(dmg_name):
  355. oscmd("rm -f %s" % dmg_name)
  356. if os.path.exists("dstroot"):
  357. oscmd("rm -rf dstroot")
  358. if os.path.exists("Panda3D-rw.dmg"):
  359. oscmd('rm -f Panda3D-rw.dmg')
  360. oscmd("mkdir -p dstroot/base/%s/lib" % installdir)
  361. oscmd("mkdir -p dstroot/base/%s/etc" % installdir)
  362. oscmd("cp %s/etc/Config.prc dstroot/base/%s/etc/Config.prc" % (outputdir, installdir))
  363. oscmd("cp %s/etc/Confauto.prc dstroot/base/%s/etc/Confauto.prc" % (outputdir, installdir))
  364. oscmd("cp -R %s/models dstroot/base/%s/models" % (outputdir, installdir))
  365. oscmd("cp -R doc/LICENSE dstroot/base/%s/LICENSE" % installdir)
  366. oscmd("cp -R doc/ReleaseNotes dstroot/base/%s/ReleaseNotes" % installdir)
  367. if os.path.isdir(outputdir + "/Frameworks") and os.listdir(outputdir + "/Frameworks"):
  368. oscmd("cp -R %s/Frameworks dstroot/base/%s/Frameworks" % (outputdir, installdir))
  369. if os.path.isdir(outputdir + "/plugins"):
  370. oscmd("cp -R %s/plugins dstroot/base/%s/plugins" % (outputdir, installdir))
  371. # Libraries that shouldn't be in base, but are instead in other modules.
  372. no_base_libs = ['libp3ffmpeg', 'libp3fmod_audio', 'libfmodex', 'libfmodexL']
  373. for base in os.listdir(outputdir + "/lib"):
  374. if not base.endswith(".a") and base.split('.')[0] not in no_base_libs:
  375. libname = ("dstroot/base/%s/lib/" % installdir) + base
  376. # We really need to specify -R in order not to follow symlinks
  377. # On OSX, just specifying -P is not enough to do that.
  378. oscmd("cp -R -P " + outputdir + "/lib/" + base + " " + libname)
  379. oscmd("mkdir -p dstroot/tools/%s/bin" % installdir)
  380. oscmd("mkdir -p dstroot/tools/etc/paths.d")
  381. # Trailing newline is important, works around a bug in OSX
  382. WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/%s/bin\n" % installdir)
  383. oscmd("mkdir -m 0755 -p dstroot/tools/usr/local/share/man/man1")
  384. oscmd("install -m 0644 doc/man/*.1 dstroot/tools/usr/local/share/man/man1/")
  385. for base in os.listdir(outputdir + "/bin"):
  386. if base not in EXCLUDE_BINARIES:
  387. binname = ("dstroot/tools/%s/bin/" % installdir) + base
  388. # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird.
  389. oscmd("cp -R " + outputdir + "/bin/" + base + " " + binname)
  390. if python_versions:
  391. # Let's only write a ppython link if there is only one Python version.
  392. if len(python_versions) == 1:
  393. oscmd("mkdir -p dstroot/pythoncode/usr/local/bin")
  394. oscmd("ln -s %s dstroot/pythoncode/usr/local/bin/ppython" % (python_versions[0]["executable"]))
  395. oscmd("mkdir -p dstroot/pythoncode/%s/panda3d" % installdir)
  396. oscmd("cp -R %s/pandac dstroot/pythoncode/%s/pandac" % (outputdir, installdir))
  397. oscmd("cp -R %s/direct dstroot/pythoncode/%s/direct" % (outputdir, installdir))
  398. oscmd("cp -R %s/*.so dstroot/pythoncode/%s/" % (outputdir, installdir), True)
  399. oscmd("cp -R %s/*.py dstroot/pythoncode/%s/" % (outputdir, installdir), True)
  400. if os.path.isdir(outputdir+"/Pmw"):
  401. oscmd("cp -R %s/Pmw dstroot/pythoncode/%s/Pmw" % (outputdir, installdir))
  402. # Copy over panda3d.dist-info directory.
  403. if os.path.isdir(outputdir + "/panda3d.dist-info"):
  404. oscmd("cp -R %s/panda3d.dist-info dstroot/pythoncode/%s/panda3d.dist-info" % (outputdir, installdir))
  405. for base in os.listdir(outputdir + "/panda3d"):
  406. if base.endswith('.py'):
  407. libname = ("dstroot/pythoncode/%s/panda3d/" % installdir) + base
  408. oscmd("cp -R " + outputdir + "/panda3d/" + base + " " + libname)
  409. for version_info in python_versions:
  410. pyver = version_info["version"]
  411. oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
  412. oscmd("mkdir -p dstroot/pybindings%s/%s/panda3d" % (pyver, installdir))
  413. # Copy over extension modules.
  414. suffix = version_info["ext_suffix"]
  415. for base in os.listdir(outputdir+"/panda3d"):
  416. if base.endswith(suffix) and '.' not in base[:-len(suffix)]:
  417. libname = "dstroot/pybindings%s/%s/panda3d/%s" % (pyver, installdir, base)
  418. # We really need to specify -R in order not to follow symlinks
  419. # On OSX, just specifying -P is not enough to do that.
  420. oscmd("cp -R -P " + outputdir + "/panda3d/" + base + " " + libname)
  421. # Write a .pth file.
  422. oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
  423. WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), installdir)
  424. # Somewhere in Python 2.7.13 and 3.7, the above path was removed from
  425. # sys.path of the python.org distribution. See bpo-28440 and GH #502.
  426. if pyver not in ("3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6"):
  427. dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver)
  428. oscmd("mkdir -p %s" % (dir))
  429. WriteFile("%s/Panda3D.pth" % (dir), installdir)
  430. # Also place it somewhere the Homebrew version of Python can find it.
  431. dir = "dstroot/pybindings%s/usr/local/lib/python%s/site-packages" % (pyver, pyver)
  432. oscmd("mkdir -p %s" % (dir))
  433. WriteFile("%s/Panda3D.pth" % (dir), installdir)
  434. if not PkgSkip("FFMPEG"):
  435. oscmd("mkdir -p dstroot/ffmpeg/%s/lib" % installdir)
  436. oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/%s/lib/" % (outputdir, installdir))
  437. #if not PkgSkip("OPENAL"):
  438. # oscmd("mkdir -p dstroot/openal/Developer/Panda3D/lib")
  439. # oscmd("cp -R %s/lib/libp3openal_audio.* dstroot/openal/Developer/Panda3D/lib/" % outputdir)
  440. if not PkgSkip("FMODEX"):
  441. oscmd("mkdir -p dstroot/fmodex/%s/lib" % installdir)
  442. oscmd("cp -R %s/lib/libp3fmod_audio.* dstroot/fmodex/%s/lib/" % (outputdir, installdir))
  443. oscmd("cp -R %s/lib/libfmodex* dstroot/fmodex/%s/lib/" % (outputdir, installdir))
  444. oscmd("mkdir -p dstroot/headers/%s/lib" % installdir)
  445. oscmd("cp -R %s/include dstroot/headers/%s/include" % (outputdir, installdir))
  446. if os.path.isdir("samples"):
  447. oscmd("mkdir -p dstroot/samples/%s/samples" % installdir)
  448. oscmd("cp -R samples/* dstroot/samples/%s/samples" % installdir)
  449. DeleteVCS("dstroot")
  450. DeleteBuildFiles("dstroot")
  451. # Compile Python files. Do this *after* the DeleteVCS step, above, which
  452. # deletes __pycache__ directories.
  453. for version_info in python_versions:
  454. if os.path.isdir("dstroot/pythoncode/%s/Pmw" % installdir):
  455. oscmd("%s -m compileall -q -f -d %s/Pmw dstroot/pythoncode/%s/Pmw" % (version_info["executable"], installdir, installdir), True)
  456. oscmd("%s -m compileall -q -f -d %s/direct dstroot/pythoncode/%s/direct" % (version_info["executable"], installdir, installdir))
  457. oscmd("%s -m compileall -q -f -d %s/pandac dstroot/pythoncode/%s/pandac" % (version_info["executable"], installdir, installdir))
  458. oscmd("%s -m compileall -q -f -d %s/panda3d dstroot/pythoncode/%s/panda3d" % (version_info["executable"], installdir, installdir))
  459. oscmd("chmod -R 0775 dstroot/*")
  460. # We need to be root to perform a chown. Bleh.
  461. # Fortunately PackageMaker does it for us, on 10.5 and above.
  462. #oscmd("chown -R root:admin dstroot/*", True)
  463. oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/")
  464. oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/")
  465. pkgs = ["base", "tools", "headers"]
  466. # Starting with 1.10.5, Panda3D is installed by default in
  467. # /Library/Developer/Panda3D instead of /Developer/Panda3D. To keep
  468. # compatibility for those who rely on the old location, we add a symlink
  469. # if they're running macOS 10.14 or less. We also remove the old
  470. # installation.
  471. script_components = set()
  472. def write_script(component, phase, contents):
  473. if installdir == "/Developer/Panda3D":
  474. return
  475. script_components.add(component)
  476. oscmd("mkdir -p dstroot/scripts/%s" % component)
  477. ln_script = open("dstroot/scripts/%s/%s" % (component, phase), "w")
  478. ln_script.write(MACOS_SCRIPT_PREFIX)
  479. ln_script.write(contents)
  480. ln_script.write(MACOS_SCRIPT_POSTFIX)
  481. ln_script.close()
  482. oscmd("chmod +x dstroot/scripts/%s/%s" % (component, phase))
  483. write_script('base', 'postinstall', """
  484. pkgutil --pkg-info org.panda3d.panda3d.base.pkg
  485. if [ $? = 0 ]; then
  486. rm -rf /Developer/Panda3D
  487. fi
  488. mkdir -p /Developer
  489. ln -s %s /Developer/Panda3D
  490. """ % installdir)
  491. # We don't specify rm -r since /Developer/Panda3D/Tools is a symlink
  492. write_script('tools', 'postinstall', """
  493. pkgutil --pkg-info org.panda3d.panda3d.tools.pkg
  494. if [ $? = 0 ]; then
  495. rm -f /Developer/Tools/Panda3D
  496. fi
  497. mkdir -p /Developer/Tools
  498. ln -s %s/bin /Developer/Tools/Panda3D
  499. """ % installdir)
  500. if os.path.isdir("samples"):
  501. pkgs.append("samples")
  502. write_script('samples', 'postinstall', """
  503. pkgutil --pkg-info org.panda3d.panda3d.samples.pkg
  504. if [ $? = 0 ]; then
  505. rm -f /Developer/Examples/Panda3D
  506. fi
  507. mkdir -p /Developer/Examples
  508. ln -s %s/samples /Developer/Examples/Panda3D
  509. """ % installdir)
  510. if python_versions:
  511. pkgs.append("pythoncode")
  512. for version_info in python_versions:
  513. pkgs.append("pybindings" + version_info["version"])
  514. if not PkgSkip("FFMPEG"):
  515. pkgs.append("ffmpeg")
  516. #if not PkgSkip("OPENAL"):
  517. # pkgs.append("openal")
  518. if not PkgSkip("FMODEX"):
  519. pkgs.append("fmodex")
  520. for pkg in pkgs:
  521. identifier = "org.panda3d.panda3d.%s.pkg" % pkg
  522. scripts_path = "dstroot/scripts/%s" % pkg
  523. plist = open("/tmp/Info_plist", "w")
  524. plist.write(Info_plist.format(package_id=identifier, version=version))
  525. plist.close()
  526. if not os.path.isdir("dstroot/" + pkg):
  527. os.makedirs("dstroot/" + pkg)
  528. if pkg in script_components:
  529. pkg_scripts = ' --scripts ' + scripts_path
  530. else:
  531. pkg_scripts = ''
  532. if os.path.exists("/usr/bin/pkgbuild"):
  533. cmd = f'/usr/bin/pkgbuild --identifier {identifier} --version {version} --root dstroot/{pkg}/ dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/{pkg}.pkg {pkg_scripts}'
  534. else:
  535. exit("pkgbuild could not be found!")
  536. oscmd(cmd)
  537. if os.path.isfile("/tmp/Info_plist"):
  538. oscmd("rm -f /tmp/Info_plist")
  539. # Now that we've built all of the individual packages, build the metapackage.
  540. dist = open("dstroot/Panda3D/Panda3D.mpkg/Contents/distribution.dist", "w")
  541. dist.write('<?xml version="1.0" encoding="utf-8"?>\n')
  542. dist.write('<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.3" authoringToolBuild="174">\n')
  543. dist.write(' <title>Panda3D SDK %s</title>\n' % (version))
  544. dist.write(' <allowed-os-versions>\n')
  545. dist.write(' <os-version min="10.9"/>\n')
  546. dist.write(' </allowed-os-versions>\n')
  547. dist.write(' <options customize="always" allow-external-scripts="no" rootVolumeOnly="false" hostArchitectures="x86_64"/>\n')
  548. dist.write(' <license language="en" mime-type="text/plain">%s</license>\n' % ReadFile("doc/LICENSE"))
  549. dist.write(' <readme language="en" mime-type="text/plain">')
  550. dist.write('WARNING: From Panda3D version 1.10.5 onwards, the default installation has been changed from /Developer/Panda3D to /Library/Developer/Panda3D\n')
  551. dist.write('This installation script will remove any existing installation in /Developer and if possible create a symbolic link towards /Library/Developer/Panda3D\n')
  552. dist.write(' </readme>')
  553. dist.write(' <script>\n')
  554. dist.write(' function isPythonVersionInstalled(version) {\n')
  555. dist.write(' return system.files.fileExistsAtPath("/usr/bin/python" + version)\n')
  556. dist.write(' || system.files.fileExistsAtPath("/usr/local/bin/python" + version)\n')
  557. dist.write(' || system.files.fileExistsAtPath("/opt/local/bin/python" + version)\n')
  558. dist.write(' || system.files.fileExistsAtPath("/sw/bin/python" + version)\n')
  559. dist.write(' || system.files.fileExistsAtPath("/System/Library/Frameworks/Python.framework/Versions/" + version + "/bin/python")\n')
  560. dist.write(' || system.files.fileExistsAtPath("/Library/Frameworks/Python.framework/Versions/" + version + "/bin/python");\n')
  561. dist.write(' }\n')
  562. dist.write(' </script>\n')
  563. dist.write(' <choices-outline>\n')
  564. dist.write(' <line choice="base"/>\n')
  565. if python_versions:
  566. dist.write(' <line choice="pythoncode">\n')
  567. for version_info in sorted(python_versions, key=lambda info: info["version"], reverse=True):
  568. dist.write(' <line choice="pybindings%s"/>\n' % (version_info["version"]))
  569. dist.write(' </line>\n')
  570. dist.write(' <line choice="tools"/>\n')
  571. if os.path.isdir("samples"):
  572. dist.write(' <line choice="samples"/>\n')
  573. if not PkgSkip("FFMPEG"):
  574. dist.write(' <line choice="ffmpeg"/>\n')
  575. if not PkgSkip("FMODEX"):
  576. dist.write(' <line choice="fmodex"/>\n')
  577. dist.write(' <line choice="headers"/>\n')
  578. dist.write(' </choices-outline>\n')
  579. dist.write(' <choice id="base" title="Panda3D Base Installation" description="This package contains the Panda3D libraries, configuration files and models/textures that are needed to use Panda3D.&#10;&#10;Location: %s/" start_enabled="false">\n' % installdir)
  580. dist.write(' <pkg-ref id="org.panda3d.panda3d.base.pkg"/>\n')
  581. dist.write(' </choice>\n')
  582. dist.write(' <choice id="tools" title="Tools" tooltip="Useful tools and model converters to help with Panda3D development" description="This package contains the various utilities that ship with Panda3D, including packaging tools, model converters, and many more.&#10;&#10;Location: %s/bin/">\n' % installdir)
  583. dist.write(' <pkg-ref id="org.panda3d.panda3d.tools.pkg"/>\n')
  584. dist.write(' </choice>\n')
  585. if python_versions:
  586. dist.write(' <choice id="pythoncode" title="Python Support" tooltip="Python bindings for the Panda3D libraries" description="This package contains the \'direct\', \'pandac\' and \'panda3d\' python packages that are needed to do Python development with Panda3D.&#10;&#10;Location: %s/">\n' % installdir)
  587. dist.write(' <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg"/>\n')
  588. dist.write(' </choice>\n')
  589. for version_info in python_versions:
  590. pyver = version_info["version"]
  591. cond = "isPythonVersionInstalled('%s')" % (pyver)
  592. dist.write(' <choice id="pybindings%s" start_selected="%s" title="Python %s Bindings" tooltip="Python bindings for the Panda3D libraries" description="Support for Python %s.">\n' % (pyver, cond, pyver, pyver))
  593. dist.write(' <pkg-ref id="org.panda3d.panda3d.pybindings%s.pkg"/>\n' % (pyver))
  594. dist.write(' </choice>\n')
  595. if not PkgSkip("FFMPEG"):
  596. dist.write(' <choice id="ffmpeg" title="FFMpeg Plug-In" tooltip="FFMpeg video and audio decoding plug-in" description="This package contains the FFMpeg plug-in, which is used for decoding video and audio files with OpenAL.')
  597. if PkgSkip("VORBIS") and PkgSkip("OPUS"):
  598. dist.write(' It is not required for loading .wav files, which Panda3D can read out of the box.">\n')
  599. elif PkgSkip("VORBIS"):
  600. dist.write(' It is not required for loading .wav or .opus files, which Panda3D can read out of the box.">\n')
  601. elif PkgSkip("OPUS"):
  602. dist.write(' It is not required for loading .wav or .ogg files, which Panda3D can read out of the box.">\n')
  603. else:
  604. dist.write(' It is not required for loading .wav, .ogg or .opus files, which Panda3D can read out of the box.">\n')
  605. dist.write(' <pkg-ref id="org.panda3d.panda3d.ffmpeg.pkg"/>\n')
  606. dist.write(' </choice>\n')
  607. #if not PkgSkip("OPENAL"):
  608. # dist.write(' <choice id="openal" title="OpenAL Audio Plug-In" tooltip="OpenAL audio output plug-in" description="This package contains the OpenAL audio plug-in, which is an open-source library for playing sounds.">\n')
  609. # dist.write(' <pkg-ref id="org.panda3d.panda3d.openal.pkg"/>\n')
  610. # dist.write(' </choice>\n')
  611. if not PkgSkip("FMODEX"):
  612. dist.write(' <choice id="fmodex" title="FMOD Ex Plug-In" tooltip="FMOD Ex audio output plug-in" description="This package contains the FMOD Ex audio plug-in, which is a commercial library for playing sounds. It is an optional component as Panda3D can use the open-source alternative OpenAL instead.">\n')
  613. dist.write(' <pkg-ref id="org.panda3d.panda3d.fmodex.pkg"/>\n')
  614. dist.write(' </choice>\n')
  615. if os.path.isdir("samples"):
  616. dist.write(' <choice id="samples" title="Sample Programs" tooltip="Python sample programs that use Panda3D" description="This package contains the Python sample programs that can help you with learning how to use Panda3D.&#10;&#10;Location: %s/samples">\n' % installdir)
  617. dist.write(' <pkg-ref id="org.panda3d.panda3d.samples.pkg"/>\n')
  618. dist.write(' </choice>\n')
  619. dist.write(' <choice id="headers" title="C++ Header Files" tooltip="Header files for C++ development with Panda3D" description="This package contains the C++ header files that are needed in order to do C++ development with Panda3D. You don\'t need this if you want to develop in Python.&#10;&#10;Location: %s/include/" start_selected="false">\n' % installdir)
  620. dist.write(' <pkg-ref id="org.panda3d.panda3d.headers.pkg"/>\n')
  621. dist.write(' </choice>\n')
  622. for pkg in pkgs:
  623. size = GetDirectorySize("dstroot/" + pkg) // 1024
  624. dist.write(' <pkg-ref id="org.panda3d.panda3d.%s.pkg" installKBytes="%d" version="1" auth="Root">file:./Contents/Packages/%s.pkg</pkg-ref>\n' % (pkg, size, pkg))
  625. dist.write('</installer-script>\n')
  626. dist.close()
  627. oscmd('hdiutil create Panda3D-rw.dmg -fs HFS+ -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (version))
  628. oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o %s' % (dmg_name))
  629. oscmd('rm -f Panda3D-rw.dmg')
  630. def MakeInstallerFreeBSD(version, python_versions=[], **kwargs):
  631. outputdir = GetOutputDir()
  632. oscmd("rm -rf targetroot +DESC pkg-plist +MANIFEST")
  633. oscmd("mkdir targetroot")
  634. # Invoke installpanda.py to install it into a temporary dir
  635. InstallPanda(
  636. destdir="targetroot",
  637. prefix="/usr/local",
  638. outputdir=outputdir,
  639. python_versions=python_versions,
  640. )
  641. if not os.path.exists("/usr/sbin/pkg"):
  642. exit("Cannot create an installer without pkg")
  643. plist_txt = ''
  644. for root, dirs, files in os.walk("targetroot/usr/local/", True):
  645. for f in files:
  646. plist_txt += os.path.join(root, f)[21:] + "\n"
  647. plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n"
  648. plist_txt += "@postunexec /sbin/ldconfig -R\n"
  649. for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"):
  650. for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False):
  651. for d in dirs:
  652. plist_txt += "@dir %s\n" % os.path.join(root, d)[21:]
  653. plist_txt += "@dir %s\n" % remdir
  654. oscmd("echo \"`pkg config abi | tr '[:upper:]' '[:lower:]' | cut -d: -f1,2`:*\" > " + outputdir + "/tmp/architecture.txt")
  655. pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip()
  656. dependencies = ''
  657. if not PkgSkip("PYTHON"):
  658. # If this version of Python was installed from a package or ports, let's mark it as dependency.
  659. oscmd("rm -f %s/tmp/python_dep" % outputdir)
  660. if "PYTHONVERSION" in SDK:
  661. pyver_nodot = SDK["PYTHONVERSION"][6:].rstrip('dmut').replace('.', '')
  662. else:
  663. pyver_nodot = "%d%d" % (sys.version_info[:2])
  664. oscmd("pkg query \"\n\t%%n : {\n\t\torigin : %%o,\n\t\tversion : %%v\n\t},\n\" python%s > %s/tmp/python_dep" % (pyver_nodot, outputdir), True)
  665. if os.path.isfile(outputdir + "/tmp/python_dep"):
  666. python_pkg = ReadFile(outputdir + "/tmp/python_dep")
  667. if python_pkg:
  668. dependencies += python_pkg
  669. manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d')
  670. manifest_txt = manifest_txt.replace("VERSION", version)
  671. manifest_txt = manifest_txt.replace("ARCH", pkg_arch)
  672. manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d')
  673. manifest_txt = manifest_txt.replace("DEPENDS", dependencies)
  674. manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024 // 1024))
  675. WriteFile("pkg-plist", plist_txt)
  676. WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:])
  677. WriteFile("+MANIFEST", manifest_txt)
  678. oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet"))
  679. def MakeInstallerAndroid(version, **kwargs):
  680. outputdir = GetOutputDir()
  681. oscmd("rm -rf apkroot")
  682. oscmd("mkdir apkroot")
  683. # Also remove the temporary apks.
  684. apk_unaligned = os.path.join(outputdir, "tmp", "panda3d-unaligned.apk")
  685. apk_unsigned = os.path.join(outputdir, "tmp", "panda3d-unsigned.apk")
  686. if os.path.exists(apk_unaligned):
  687. os.unlink(apk_unaligned)
  688. if os.path.exists(apk_unsigned):
  689. os.unlink(apk_unsigned)
  690. # Copy the compiled Java classes.
  691. oscmd("cp %s apkroot/classes.dex" % (os.path.join(outputdir, "classes.dex")))
  692. # Copy the libraries one by one. In case of library dependencies, strip
  693. # off any suffix (eg. libfile.so.1.0), as Android does not support them.
  694. source_dir = os.path.join(outputdir, "lib")
  695. target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
  696. if not os.path.exists(target_dir):
  697. os.makedirs(target_dir, mode=0o755)
  698. # Determine the library directories we should look in.
  699. libpath = [source_dir]
  700. for dir in os.environ.get("LD_LIBRARY_PATH", "").split(':'):
  701. dir = os.path.expandvars(dir)
  702. dir = os.path.expanduser(dir)
  703. if os.path.isdir(dir):
  704. dir = os.path.realpath(dir)
  705. if not dir.startswith("/system") and not dir.startswith("/vendor"):
  706. libpath.append(dir)
  707. def copy_library(source, base):
  708. # Copy file to destination, stripping version suffix.
  709. target = os.path.join(target_dir, base)
  710. if not target.endswith('.so'):
  711. target = target.rpartition('.so.')[0] + '.so'
  712. if os.path.isfile(target):
  713. # Already processed.
  714. return
  715. shutil.copy(source, target)
  716. # Walk through the library dependencies.
  717. handle = subprocess.Popen(['readelf', '--dynamic', target], stdout=subprocess.PIPE)
  718. for line in handle.communicate()[0].splitlines():
  719. # The line will look something like:
  720. # 0x0000000000000001 (NEEDED) Shared library: [libpanda.so]
  721. line = line.decode('utf-8', 'replace').strip()
  722. if not line or '(NEEDED)' not in line or '[' not in line or ']' not in line:
  723. continue
  724. # Extract the part between square brackets.
  725. idx = line.index('[')
  726. dep = line[idx + 1 : line.index(']', idx)]
  727. # Change .so.1.2 suffix to .so, as needed for loading in .apk
  728. if '.so.' in dep:
  729. orig_dep = dep
  730. dep = dep.rpartition('.so.')[0] + '.so'
  731. oscmd("patchelf --replace-needed %s %s %s" % (orig_dep, dep, target), True)
  732. # Find it on the LD_LIBRARY_PATH.
  733. for dir in libpath:
  734. fulldep = os.path.join(dir, dep)
  735. if os.path.isfile(fulldep):
  736. copy_library(os.path.realpath(fulldep), dep)
  737. break
  738. # Now copy every lib in the lib dir, and its dependencies.
  739. for base in os.listdir(source_dir):
  740. if not base.startswith('lib'):
  741. continue
  742. if not base.endswith('.so') and '.so.' not in base:
  743. continue
  744. source = os.path.join(source_dir, base)
  745. if os.path.islink(source):
  746. continue
  747. copy_library(source, base)
  748. # Same for Python extension modules. However, Android is strict about
  749. # library naming, so we have a special naming scheme for these, in
  750. # conjunction with a custom import hook to find these modules.
  751. if not PkgSkip("PYTHON"):
  752. suffix = GetExtensionSuffix()
  753. source_dir = os.path.join(outputdir, "panda3d")
  754. for base in os.listdir(source_dir):
  755. if not base.endswith(suffix):
  756. continue
  757. modname = base[:-len(suffix)]
  758. if '.' not in modname:
  759. source = os.path.join(source_dir, base)
  760. copy_library(source, "libpy.panda3d.{}.so".format(modname))
  761. # Same for standard Python modules.
  762. if CrossCompiling():
  763. source_dir = os.path.join(GetThirdpartyDir(), "python", "lib", SDK["PYTHONVERSION"], "lib-dynload")
  764. else:
  765. import _ctypes
  766. source_dir = os.path.dirname(_ctypes.__file__)
  767. for base in os.listdir(source_dir):
  768. if not base.endswith('.so'):
  769. continue
  770. modname = base.partition('.')[0]
  771. source = os.path.join(source_dir, base)
  772. copy_library(source, "libpy.{}.so".format(modname))
  773. def copy_python_tree(source_root, target_root):
  774. for source_dir, dirs, files in os.walk(source_root):
  775. if 'site-packages' in dirs:
  776. dirs.remove('site-packages')
  777. if not any(base.endswith('.py') for base in files):
  778. continue
  779. target_dir = os.path.join(target_root, os.path.relpath(source_dir, source_root))
  780. target_dir = os.path.normpath(target_dir)
  781. os.makedirs(target_dir, 0o755)
  782. for base in files:
  783. if base.endswith('.py'):
  784. target = os.path.join(target_dir, base)
  785. shutil.copy(os.path.join(source_dir, base), target)
  786. # Copy the Python standard library to the .apk as well.
  787. from locations import get_python_lib
  788. stdlib_source = get_python_lib(False, True)
  789. stdlib_target = os.path.join("apkroot", "lib", "python{0}.{1}".format(*sys.version_info))
  790. copy_python_tree(stdlib_source, stdlib_target)
  791. # But also copy over our custom site.py.
  792. shutil.copy("panda/src/android/site.py", os.path.join(stdlib_target, "site.py"))
  793. # And now make a site-packages directory containing our direct/panda3d/pandac modules.
  794. for tree in "panda3d", "direct", "pandac":
  795. copy_python_tree(
  796. os.path.join(outputdir, tree),
  797. os.path.join(stdlib_target, "site-packages", tree),
  798. )
  799. # Copy the models and config files to the virtual assets filesystem.
  800. oscmd("mkdir apkroot/assets")
  801. oscmd("cp -R %s apkroot/assets/models" % (os.path.join(outputdir, "models")))
  802. oscmd("cp -R %s apkroot/assets/etc" % (os.path.join(outputdir, "etc")))
  803. # Make an empty res folder. It's needed for the apk to be installable, apparently.
  804. oscmd("mkdir apkroot/res")
  805. # Now package up the application
  806. oscmd("cp panda/src/android/pview_manifest.xml apkroot/AndroidManifest.xml")
  807. aapt_cmd = "aapt package"
  808. aapt_cmd += " -F %s" % (apk_unaligned)
  809. aapt_cmd += " -M apkroot/AndroidManifest.xml"
  810. aapt_cmd += " -A apkroot/assets -S apkroot/res"
  811. aapt_cmd += " -I %s" % (SDK["ANDROID_JAR"])
  812. oscmd(aapt_cmd)
  813. # And add all the libraries to it.
  814. oscmd("aapt add %s classes.dex" % (os.path.join('..', apk_unaligned)), cwd="apkroot")
  815. for path, dirs, files in os.walk('apkroot/lib'):
  816. if files:
  817. rel = os.path.relpath(path, 'apkroot')
  818. rel_files = [os.path.join(rel, file).replace('\\', '/') for file in files]
  819. oscmd("aapt add %s %s" % (os.path.join('..', apk_unaligned), ' '.join(rel_files)), cwd="apkroot")
  820. # Now align the .apk, which is necessary for Android to load it.
  821. oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
  822. # Finally, sign it using a debug key. This is generated if it doesn't exist.
  823. if GetHost() == 'android':
  824. # Termux version of apksigner automatically generates a debug key.
  825. oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
  826. else:
  827. if not os.path.isfile('debug.ks'):
  828. oscmd("keytool -genkey -noprompt -dname CN=Panda3D,O=Panda3D,C=US -keystore debug.ks -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 1000")
  829. oscmd("apksigner sign --ks debug.ks --ks-pass pass:android --min-sdk-version %s --out panda3d.apk %s" % (SDK["ANDROID_API"], apk_unsigned))
  830. # Clean up.
  831. oscmd("rm -rf apkroot")
  832. os.unlink(apk_unaligned)
  833. os.unlink(apk_unsigned)
  834. def MakeInstaller(version, **kwargs):
  835. target = GetTarget()
  836. if target == 'windows':
  837. dir = kwargs.pop('installdir', None)
  838. if dir is None:
  839. dir = "C:\\Panda3D-" + version
  840. if GetTargetArch() == 'x64':
  841. dir += '-x64'
  842. fn = "Panda3D-"
  843. title = "Panda3D SDK " + version
  844. fn += version
  845. python_versions = kwargs.get('python_versions', [])
  846. if len(python_versions) == 1:
  847. fn += '-py' + python_versions[0]["version"]
  848. if GetOptimize() <= 2:
  849. fn += "-dbg"
  850. if GetTargetArch() == 'x64':
  851. fn += '-x64'
  852. compressor = kwargs.get('compressor')
  853. MakeInstallerNSIS(version, fn + '.exe', title, dir, **kwargs)
  854. MakeDebugSymbolArchive(fn + '-pdb', compressor)
  855. elif target == 'linux':
  856. MakeInstallerLinux(version, **kwargs)
  857. elif target == 'darwin':
  858. MakeInstallerOSX(version, **kwargs)
  859. elif target == 'freebsd':
  860. MakeInstallerFreeBSD(version, **kwargs)
  861. elif target == 'android':
  862. MakeInstallerAndroid(version, **kwargs)
  863. else:
  864. exit("Do not know how to make an installer for this platform")
  865. if __name__ == "__main__":
  866. version = GetMetadataValue('version')
  867. parser = OptionParser()
  868. parser.add_option(
  869. '',
  870. '--version',
  871. dest='version',
  872. help='Panda3D version number (default: %s)' % (version),
  873. default=version,
  874. )
  875. parser.add_option(
  876. '',
  877. '--debversion',
  878. dest='debversion',
  879. help='Version number for .deb file',
  880. default=None,
  881. )
  882. parser.add_option(
  883. '',
  884. '--rpmversion',
  885. dest='rpmversion',
  886. help='Version number for .rpm file',
  887. default=None,
  888. )
  889. parser.add_option(
  890. '',
  891. '--rpmrelease',
  892. dest='rpmrelease',
  893. help='Release number for .rpm file',
  894. default='1',
  895. )
  896. parser.add_option(
  897. '',
  898. '--outputdir',
  899. dest='outputdir',
  900. help='Makepanda\'s output directory (default: built)',
  901. default='built',
  902. )
  903. parser.add_option(
  904. '',
  905. '--verbose',
  906. dest='verbose',
  907. help='Enable verbose output',
  908. action='store_true',
  909. default=False,
  910. )
  911. parser.add_option(
  912. '',
  913. '--lzma',
  914. dest='compressor',
  915. help='Use LZMA compression',
  916. action='store_const',
  917. const='lzma',
  918. default='zlib',
  919. )
  920. parser.add_option(
  921. '',
  922. '--installdir',
  923. dest='installdir',
  924. help='Where on the system the installer should put the SDK (Windows, macOS)',
  925. )
  926. (options, args) = parser.parse_args()
  927. SetVerbose(options.verbose)
  928. SetOutputDir(options.outputdir)
  929. # Read out the optimize option.
  930. opt = ReadFile(os.path.join(options.outputdir, "tmp", "optimize.dat"))
  931. SetOptimize(int(opt.strip()))
  932. # Read out whether we should set PkgSkip("PYTHON") and some others.
  933. # Temporary hack; needs better solution.
  934. pkg_list = "PYTHON", "NVIDIACG", "FFMPEG", "OPENAL", "FMODEX", "PVIEW", "NVIDIACG", "VORBIS", "OPUS"
  935. PkgListSet(pkg_list)
  936. for pkg in pkg_list:
  937. dat_path = "dtool_have_%s.dat" % (pkg.lower())
  938. content = ReadFile(os.path.join(options.outputdir, "tmp", dat_path))
  939. if int(content.strip()):
  940. PkgEnable(pkg)
  941. else:
  942. PkgDisable(pkg)
  943. # Parse the version.
  944. match = re.match(r'^\d+\.\d+(\.\d+)+', options.version)
  945. if not match:
  946. exit("version requires three digits")
  947. MakeInstaller(
  948. version=match.group(),
  949. outputdir=options.outputdir,
  950. optimize=GetOptimize(),
  951. compressor=options.compressor,
  952. debversion=options.debversion,
  953. rpmversion=options.rpmversion,
  954. rpmrelease=options.rpmrelease,
  955. python_versions=ReadPythonVersionInfoFile(),
  956. installdir=options.installdir,
  957. )