makepackage.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. #!/usr/bin/env python
  2. from makepandacore import *
  3. from installpanda import *
  4. import sys
  5. import os
  6. import shutil
  7. import glob
  8. import re
  9. INSTALLER_DEB_FILE = """
  10. Package: panda3dMAJOR
  11. Version: VERSION
  12. Section: libdevel
  13. Priority: optional
  14. Architecture: ARCH
  15. Essential: no
  16. Depends: DEPENDS
  17. Recommends: RECOMMENDS
  18. Provides: PROVIDES
  19. Conflicts: PROVIDES
  20. Replaces: PROVIDES
  21. Maintainer: rdb <[email protected]>
  22. Installed-Size: INSTSIZE
  23. Description: Panda3D free 3D engine SDK
  24. 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.
  25. 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.
  26. .
  27. This package contains the SDK for development with Panda3D.
  28. """
  29. RUNTIME_INSTALLER_DEB_FILE = """
  30. Package: panda3d-runtime
  31. Version: VERSION
  32. Section: web
  33. Priority: optional
  34. Architecture: ARCH
  35. Essential: no
  36. Depends: DEPENDS
  37. Provides: panda3d-runtime
  38. Maintainer: rdb <[email protected]>
  39. Installed-Size: INSTSIZE
  40. Description: Runtime binary and browser plugin for the Panda3D Game Engine
  41. This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file.
  42. """
  43. # We're not putting "python" in the "Requires" field,
  44. # since the rpm-based distros don't have a common
  45. # naming for the Python package.
  46. INSTALLER_SPEC_FILE = """
  47. Summary: The Panda3D free 3D engine SDK
  48. Name: panda3d
  49. Version: VERSION
  50. Release: RPMRELEASE
  51. License: BSD License
  52. Group: Development/Libraries
  53. BuildRoot: PANDASOURCE/targetroot
  54. %description
  55. 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.
  56. 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.
  57. This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files.
  58. %post
  59. /sbin/ldconfig
  60. %postun
  61. /sbin/ldconfig
  62. %files
  63. %defattr(-,root,root)
  64. /etc/Confauto.prc
  65. /etc/Config.prc
  66. /usr/share/panda3d
  67. /etc/ld.so.conf.d/panda3d.conf
  68. /usr/%_lib/panda3d
  69. /usr/include/panda3d
  70. """
  71. INSTALLER_SPEC_FILE_PVIEW = \
  72. """/usr/share/applications/pview.desktop
  73. /usr/share/mime-info/panda3d.mime
  74. /usr/share/mime-info/panda3d.keys
  75. /usr/share/mime/packages/panda3d.xml
  76. /usr/share/application-registry/panda3d.applications
  77. """
  78. RUNTIME_INSTALLER_SPEC_FILE = """
  79. Summary: Runtime binary and browser plugin for the Panda3D Game Engine
  80. Name: panda3d-runtime
  81. Version: VERSION
  82. Release: RPMRELEASE
  83. License: BSD License
  84. Group: Productivity/Graphics/Other
  85. BuildRoot: PANDASOURCE/targetroot
  86. %description
  87. This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file.
  88. %files
  89. %defattr(-,root,root)
  90. /usr/bin/panda3d
  91. /usr/%_lib/nppanda3d.so
  92. /usr/%_lib/mozilla/plugins/nppanda3d.so
  93. /usr/%_lib/mozilla-firefox/plugins/nppanda3d.so
  94. /usr/%_lib/xulrunner-addons/plugins/nppanda3d.so
  95. /usr/share/mime-info/panda3d-runtime.mime
  96. /usr/share/mime-info/panda3d-runtime.keys
  97. /usr/share/mime/packages/panda3d-runtime.xml
  98. /usr/share/application-registry/panda3d-runtime.applications
  99. /usr/share/applications/*.desktop
  100. """
  101. # plist file for Mac OSX
  102. Info_plist = """<?xml version="1.0" encoding="UTF-8"?>
  103. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  104. <plist version="1.0">
  105. <dict>
  106. <key>CFBundleIdentifier</key>
  107. <string>{package_id}</string>
  108. <key>CFBundleShortVersionString</key>
  109. <string>{version}</string>
  110. <key>IFPkgFlagRelocatable</key>
  111. <false/>
  112. <key>IFPkgFlagAuthorizationAction</key>
  113. <string>RootAuthorization</string>
  114. <key>IFPkgFlagAllowBackRev</key>
  115. <true/>
  116. </dict>
  117. </plist>
  118. """
  119. # FreeBSD pkg-descr
  120. INSTALLER_PKG_DESCR_FILE = """
  121. 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.
  122. 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.
  123. This package contains the SDK for development with Panda3D, install panda3d-runtime for the runtime files.
  124. WWW: https://www.panda3d.org/
  125. """
  126. # FreeBSD pkg-descr
  127. RUNTIME_INSTALLER_PKG_DESCR_FILE = """
  128. Runtime binary and browser plugin for the Panda3D Game Engine
  129. This package contains the runtime distribution and browser plugin of the Panda3D engine. It allows you view webpages that contain Panda3D content and to run games created with Panda3D that are packaged as .p3d file.
  130. WWW: https://www.panda3d.org/
  131. """
  132. # FreeBSD PKG Manifest template file
  133. INSTALLER_PKG_MANIFEST_FILE = """
  134. name: NAME
  135. version: VERSION
  136. arch: ARCH
  137. origin: ORIGIN
  138. comment: "Panda3D free 3D engine SDK"
  139. www: https://www.panda3d.org
  140. maintainer: rdb <[email protected]>
  141. prefix: /usr/local
  142. flatsize: INSTSIZEMB
  143. deps: {DEPENDS}
  144. """
  145. def MakeInstallerNSIS(version, file, title, installdir, runtime=False, compressor="lzma", **kwargs):
  146. outputdir = GetOutputDir()
  147. if os.path.isfile(file):
  148. os.remove(file)
  149. elif os.path.isdir(file):
  150. shutil.rmtree(file)
  151. if GetTargetArch() == 'x64':
  152. regview = '64'
  153. else:
  154. regview = '32'
  155. if runtime:
  156. # Invoke the make_installer script.
  157. AddToPathEnv("PATH", outputdir + "\\bin")
  158. AddToPathEnv("PATH", outputdir + "\\plugins")
  159. cmd = sys.executable + " -B -u " + os.path.join("direct", "src", "plugin_installer", "make_installer.py")
  160. cmd += " --version %s --regview %s" % (version, regview)
  161. if GetTargetArch() == 'x64':
  162. cmd += " --install \"$PROGRAMFILES64\\Panda3D\" "
  163. else:
  164. cmd += " --install \"$PROGRAMFILES32\\Panda3D\" "
  165. oscmd(cmd)
  166. shutil.move(os.path.join("direct", "src", "plugin_installer", "p3d-setup.exe"), file)
  167. return
  168. print("Building "+title+" installer at %s" % (file))
  169. if compressor != "lzma":
  170. print("Note: you are using zlib, which is faster, but lzma gives better compression.")
  171. if os.path.exists("nsis-output.exe"):
  172. os.remove("nsis-output.exe")
  173. WriteFile(outputdir+"/tmp/__init__.py", "")
  174. nsis_defs = {
  175. 'COMPRESSOR': compressor,
  176. 'TITLE' : title,
  177. 'INSTALLDIR': installdir,
  178. 'OUTFILE' : '..\\' + file,
  179. 'BUILT' : '..\\' + outputdir,
  180. 'SOURCE' : '..',
  181. 'REGVIEW' : regview,
  182. }
  183. # Are we shipping a version of Python?
  184. if os.path.isfile(os.path.join(outputdir, "python", "python.exe")):
  185. py_dlls = glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9].dll")) \
  186. + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9]_d.dll"))
  187. assert py_dlls
  188. py_dll = os.path.basename(py_dlls[0])
  189. pyver = py_dll[6] + "." + py_dll[7]
  190. if GetTargetArch() != 'x64':
  191. pyver += '-32'
  192. nsis_defs['INCLUDE_PYVER'] = pyver
  193. if GetHost() == 'windows':
  194. cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2'
  195. for item in nsis_defs.items():
  196. cmd += ' /D%s="%s"' % item
  197. else:
  198. cmd = 'makensis -V2'
  199. for item in nsis_defs.items():
  200. cmd += ' -D%s="%s"' % item
  201. cmd += ' "makepanda\\installer.nsi"'
  202. oscmd(cmd)
  203. def MakeDebugSymbolArchive(zipname, dirname):
  204. outputdir = GetOutputDir()
  205. import zipfile
  206. zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
  207. for fn in glob.glob(os.path.join(outputdir, 'bin', '*.pdb')):
  208. zip.write(fn, dirname + '/bin/' + os.path.basename(fn))
  209. for fn in glob.glob(os.path.join(outputdir, 'panda3d', '*.pdb')):
  210. zip.write(fn, dirname + '/panda3d/' + os.path.basename(fn))
  211. for fn in glob.glob(os.path.join(outputdir, 'plugins', '*.pdb')):
  212. zip.write(fn, dirname + '/plugins/' + os.path.basename(fn))
  213. for fn in glob.glob(os.path.join(outputdir, 'python', '*.pdb')):
  214. zip.write(fn, dirname + '/python/' + os.path.basename(fn))
  215. for fn in glob.glob(os.path.join(outputdir, 'python', 'DLLs', '*.pdb')):
  216. zip.write(fn, dirname + '/python/DLLs/' + os.path.basename(fn))
  217. zip.close()
  218. def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False,
  219. python_versions=[], **kwargs):
  220. outputdir = GetOutputDir()
  221. # We pack Python 2 and Python 3, if we built with support for it.
  222. python2_ver = None
  223. python3_ver = None
  224. install_python_versions = []
  225. if not runtime:
  226. # What's the system version of Python 3?
  227. oscmd('python3 -V > "%s/tmp/python3_version.txt"' % (outputdir))
  228. sys_python3_ver = '.'.join(ReadFile(outputdir + "/tmp/python3_version.txt").strip().split(' ')[1].split('.')[:2])
  229. # Check that we built with support for these.
  230. for version_info in python_versions:
  231. if version_info["version"] == "2.7":
  232. python2_ver = "2.7"
  233. install_python_versions.append(version_info)
  234. elif version_info["version"] == sys_python3_ver:
  235. python3_ver = sys_python3_ver
  236. install_python_versions.append(version_info)
  237. major_version = '.'.join(version.split('.')[:2])
  238. if not debversion:
  239. debversion = version
  240. # Clean and set up a directory to install Panda3D into
  241. oscmd("rm -rf targetroot data.tar.gz control.tar.gz panda3d.spec")
  242. oscmd("mkdir -m 0755 targetroot")
  243. dpkg_present = False
  244. if os.path.exists("/usr/bin/dpkg-architecture") and os.path.exists("/usr/bin/dpkg-deb"):
  245. dpkg_present = True
  246. rpmbuild_present = False
  247. if os.path.exists("/usr/bin/rpmbuild"):
  248. rpmbuild_present = True
  249. if dpkg_present and rpmbuild_present:
  250. Warn("both dpkg and rpmbuild present.")
  251. if dpkg_present:
  252. # Invoke installpanda.py to install it into a temporary dir
  253. lib_dir = GetDebLibDir()
  254. if runtime:
  255. InstallRuntime(destdir="targetroot", prefix="/usr",
  256. outputdir=outputdir, libdir=lib_dir)
  257. else:
  258. InstallPanda(destdir="targetroot", prefix="/usr",
  259. outputdir=outputdir, libdir=lib_dir,
  260. python_versions=install_python_versions)
  261. oscmd("chmod -R 755 targetroot/usr/share/panda3d")
  262. oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1")
  263. oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/")
  264. oscmd("dpkg --print-architecture > "+outputdir+"/tmp/architecture.txt")
  265. pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip()
  266. if runtime:
  267. txt = RUNTIME_INSTALLER_DEB_FILE[1:]
  268. else:
  269. txt = INSTALLER_DEB_FILE[1:]
  270. txt = txt.replace("VERSION", debversion).replace("ARCH", pkg_arch).replace("MAJOR", major_version)
  271. txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024))
  272. oscmd("mkdir -m 0755 -p targetroot/DEBIAN")
  273. oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums")
  274. if not runtime:
  275. oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums")
  276. WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n")
  277. WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n")
  278. oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm")
  279. # Determine the package name and the locations that
  280. # dpkg-shlibdeps should look in for executables.
  281. pkg_version = debversion
  282. if runtime:
  283. pkg_name = "panda3d-runtime"
  284. lib_pattern = "debian/%s/usr/%s/*.so" % (pkg_name, lib_dir)
  285. else:
  286. pkg_name = "panda3d" + major_version
  287. lib_pattern = "debian/%s/usr/%s/panda3d/*.so*" % (pkg_name, lib_dir)
  288. bin_pattern = "debian/%s/usr/bin/*" % (pkg_name)
  289. # dpkg-shlibdeps looks in the debian/{pkg_name}/DEBIAN/shlibs directory
  290. # and also expects a debian/control file, so we create this dummy set-up.
  291. oscmd("mkdir targetroot/debian")
  292. oscmd("ln -s .. targetroot/debian/" + pkg_name)
  293. WriteFile("targetroot/debian/control", "")
  294. dpkg_shlibdeps = "dpkg-shlibdeps"
  295. if GetVerbose():
  296. dpkg_shlibdeps += " -v"
  297. if runtime:
  298. # The runtime doesn't export any useful symbols, so just query the dependencies.
  299. oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
  300. depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip()
  301. recommends = ""
  302. provides = "panda3d-runtime"
  303. else:
  304. pkg_name = "panda3d" + major_version
  305. pkg_dir = "debian/panda3d" + major_version
  306. # Generate a symbols file so that other packages can know which symbols we export.
  307. oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals())
  308. # Library dependencies are required, binary dependencies are recommended.
  309. # We explicitly exclude libphysx-extras since we don't want to depend on PhysX.
  310. oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals())
  311. oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals())
  312. # Parse the substvars files generated by dpkg-shlibdeps.
  313. depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
  314. recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip()
  315. provides = "panda3d"
  316. if python2_ver or python3_ver:
  317. recommends += ", python-pmw"
  318. if python2_ver:
  319. depends += ", python%s" % (python2_ver)
  320. recommends += ", python-wxversion"
  321. recommends += ", python-tk (>= %s)" % (python2_ver)
  322. provides += ", python2-panda3d"
  323. if python3_ver:
  324. depends += ", python%s" % (python3_ver)
  325. recommends += ", python3-tk (>= %s)" % (python3_ver)
  326. provides += ", python3-panda3d"
  327. if not PkgSkip("NVIDIACG"):
  328. depends += ", nvidia-cg-toolkit"
  329. # Write back the dependencies, and delete the dummy set-up.
  330. txt = txt.replace("DEPENDS", depends.strip(', '))
  331. txt = txt.replace("RECOMMENDS", recommends.strip(', '))
  332. txt = txt.replace("PROVIDES", provides.strip(', '))
  333. WriteFile("targetroot/DEBIAN/control", txt)
  334. oscmd("rm -rf targetroot/debian")
  335. # Package it all up into a .deb file.
  336. oscmd("chmod -R 755 targetroot/DEBIAN")
  337. oscmd("chmod 644 targetroot/DEBIAN/control targetroot/DEBIAN/md5sums")
  338. if not runtime:
  339. oscmd("chmod 644 targetroot/DEBIAN/conffiles targetroot/DEBIAN/symbols")
  340. oscmd("fakeroot dpkg-deb -b targetroot %s_%s_%s.deb" % (pkg_name, pkg_version, pkg_arch))
  341. elif rpmbuild_present:
  342. # Invoke installpanda.py to install it into a temporary dir
  343. if runtime:
  344. InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=GetRPMLibDir())
  345. else:
  346. InstallPanda(destdir="targetroot", prefix="/usr",
  347. outputdir=outputdir, libdir=GetRPMLibDir(),
  348. python_versions=install_python_versions)
  349. oscmd("chmod -R 755 targetroot/usr/share/panda3d")
  350. oscmd("rpm -E '%_target_cpu' > "+outputdir+"/tmp/architecture.txt")
  351. arch = ReadFile(outputdir+"/tmp/architecture.txt").strip()
  352. pandasource = os.path.abspath(os.getcwd())
  353. if runtime:
  354. txt = RUNTIME_INSTALLER_SPEC_FILE[1:]
  355. else:
  356. txt = INSTALLER_SPEC_FILE[1:]
  357. # Add the MIME associations if we have pview
  358. if not PkgSkip("PVIEW"):
  359. txt += INSTALLER_SPEC_FILE_PVIEW
  360. # Add the platform-specific Python directories.
  361. dirs = set()
  362. for version_info in install_python_versions:
  363. dirs.add(version_info["platlib"])
  364. dirs.add(version_info["purelib"])
  365. for dir in dirs:
  366. txt += dir + "\n"
  367. # Add the binaries in /usr/bin explicitly to the spec file
  368. for base in os.listdir(outputdir + "/bin"):
  369. if not base.startswith("deploy-stub"):
  370. txt += "/usr/bin/%s\n" % (base)
  371. # Write out the spec file.
  372. txt = txt.replace("VERSION", version)
  373. txt = txt.replace("RPMRELEASE", str(rpmrelease))
  374. txt = txt.replace("PANDASOURCE", pandasource)
  375. WriteFile("panda3d.spec", txt)
  376. oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec")
  377. if runtime:
  378. oscmd("mv "+arch+"/panda3d-runtime-"+version+"-"+rpmrelease+"."+arch+".rpm .")
  379. else:
  380. oscmd("mv "+arch+"/panda3d-"+version+"-"+rpmrelease+"."+arch+".rpm .")
  381. oscmd("rm -rf "+arch, True)
  382. else:
  383. exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
  384. def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
  385. outputdir = GetOutputDir()
  386. if runtime:
  387. # Invoke the make_installer script.
  388. AddToPathEnv("DYLD_LIBRARY_PATH", outputdir + "/plugins")
  389. cmdstr = sys.executable + " "
  390. if sys.version_info >= (2, 6):
  391. cmdstr += "-B "
  392. cmdstr += "direct/src/plugin_installer/make_installer.py --version %s" % version
  393. oscmd(cmdstr)
  394. return
  395. dmg_name = "Panda3D-" + version
  396. if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."):
  397. dmg_name += "-py" + python_versions[0]["version"]
  398. dmg_name += ".dmg"
  399. if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name)
  400. if (os.path.exists("dstroot")): oscmd("rm -rf dstroot")
  401. if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg')
  402. oscmd("mkdir -p dstroot/base/Developer/Panda3D/lib")
  403. oscmd("mkdir -p dstroot/base/Developer/Panda3D/etc")
  404. oscmd("cp %s/etc/Config.prc dstroot/base/Developer/Panda3D/etc/Config.prc" % outputdir)
  405. oscmd("cp %s/etc/Confauto.prc dstroot/base/Developer/Panda3D/etc/Confauto.prc" % outputdir)
  406. oscmd("cp -R %s/models dstroot/base/Developer/Panda3D/models" % outputdir)
  407. oscmd("cp -R doc/LICENSE dstroot/base/Developer/Panda3D/LICENSE")
  408. oscmd("cp -R doc/ReleaseNotes dstroot/base/Developer/Panda3D/ReleaseNotes")
  409. oscmd("cp -R %s/Frameworks dstroot/base/Developer/Panda3D/Frameworks" % outputdir)
  410. if os.path.isdir(outputdir+"/plugins"):
  411. oscmd("cp -R %s/plugins dstroot/base/Developer/Panda3D/plugins" % outputdir)
  412. # Libraries that shouldn't be in base, but are instead in other modules.
  413. no_base_libs = ['libp3ffmpeg', 'libp3fmod_audio', 'libfmodex', 'libfmodexL']
  414. for base in os.listdir(outputdir+"/lib"):
  415. if not base.endswith(".a") and base.split('.')[0] not in no_base_libs:
  416. libname = "dstroot/base/Developer/Panda3D/lib/" + base
  417. # We really need to specify -R in order not to follow symlinks
  418. # On OSX, just specifying -P is not enough to do that.
  419. oscmd("cp -R -P " + outputdir + "/lib/" + base + " " + libname)
  420. oscmd("mkdir -p dstroot/tools/Developer/Panda3D/bin")
  421. oscmd("mkdir -p dstroot/tools/Developer/Tools")
  422. oscmd("ln -s ../Panda3D/bin dstroot/tools/Developer/Tools/Panda3D")
  423. oscmd("mkdir -p dstroot/tools/etc/paths.d")
  424. # Trailing newline is important, works around a bug in OSX
  425. WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Panda3D/bin\n")
  426. oscmd("mkdir -m 0755 -p dstroot/tools/usr/local/share/man/man1")
  427. oscmd("install -m 0644 doc/man/*.1 dstroot/tools/usr/local/share/man/man1/")
  428. for base in os.listdir(outputdir+"/bin"):
  429. if not base.startswith("deploy-stub"):
  430. binname = "dstroot/tools/Developer/Panda3D/bin/" + base
  431. # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird.
  432. oscmd("cp -R " + outputdir + "/bin/" + base + " " + binname)
  433. if python_versions:
  434. # Let's only write a ppython link if there is only one Python version.
  435. if len(python_versions) == 1:
  436. oscmd("mkdir -p dstroot/pythoncode/usr/local/bin")
  437. oscmd("ln -s %s dstroot/pythoncode/usr/local/bin/ppython" % (python_versions[0]["executable"]))
  438. oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d")
  439. oscmd("cp -R %s/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % outputdir)
  440. oscmd("cp -R %s/direct dstroot/pythoncode/Developer/Panda3D/direct" % outputdir)
  441. oscmd("cp -R %s/*.so dstroot/pythoncode/Developer/Panda3D/" % outputdir, True)
  442. oscmd("cp -R %s/*.py dstroot/pythoncode/Developer/Panda3D/" % outputdir, True)
  443. if os.path.isdir(outputdir+"/Pmw"):
  444. oscmd("cp -R %s/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % outputdir)
  445. # Copy over panda3d.dist-info directory.
  446. if os.path.isdir(outputdir + "/panda3d.dist-info"):
  447. oscmd("cp -R %s/panda3d.dist-info dstroot/pythoncode/Developer/Panda3D/panda3d.dist-info" % (outputdir))
  448. for base in os.listdir(outputdir+"/panda3d"):
  449. if base.endswith('.py'):
  450. libname = "dstroot/pythoncode/Developer/Panda3D/panda3d/" + base
  451. oscmd("cp -R " + outputdir + "/panda3d/" + base + " " + libname)
  452. for version_info in python_versions:
  453. pyver = version_info["version"]
  454. oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
  455. oscmd("mkdir -p dstroot/pybindings%s/Developer/Panda3D/panda3d" % (pyver))
  456. # Copy over extension modules.
  457. suffix = version_info["ext_suffix"]
  458. for base in os.listdir(outputdir+"/panda3d"):
  459. if base.endswith(suffix) and '.' not in base[:-len(suffix)]:
  460. libname = "dstroot/pybindings%s/Developer/Panda3D/panda3d/%s" % (pyver, base)
  461. # We really need to specify -R in order not to follow symlinks
  462. # On OSX, just specifying -P is not enough to do that.
  463. oscmd("cp -R -P " + outputdir + "/panda3d/" + base + " " + libname)
  464. # Write a .pth file.
  465. oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
  466. WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D")
  467. # Evidently not all Python 3.7 installations read the above path, so do this for now
  468. # See GitHub #502
  469. if pyver == "3.7":
  470. dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver)
  471. oscmd("mkdir -p %s" % (dir))
  472. WriteFile("%s/Panda3D.pth" % (dir), "/Developer/Panda3D")
  473. if not PkgSkip("FFMPEG"):
  474. oscmd("mkdir -p dstroot/ffmpeg/Developer/Panda3D/lib")
  475. oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/Developer/Panda3D/lib/" % outputdir)
  476. #if not PkgSkip("OPENAL"):
  477. # oscmd("mkdir -p dstroot/openal/Developer/Panda3D/lib")
  478. # oscmd("cp -R %s/lib/libp3openal_audio.* dstroot/openal/Developer/Panda3D/lib/" % outputdir)
  479. if not PkgSkip("FMODEX"):
  480. oscmd("mkdir -p dstroot/fmodex/Developer/Panda3D/lib")
  481. oscmd("cp -R %s/lib/libp3fmod_audio.* dstroot/fmodex/Developer/Panda3D/lib/" % outputdir)
  482. oscmd("cp -R %s/lib/libfmodex* dstroot/fmodex/Developer/Panda3D/lib/" % outputdir)
  483. oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib")
  484. oscmd("cp -R %s/include dstroot/headers/Developer/Panda3D/include" % outputdir)
  485. if os.path.isfile(outputdir + "/lib/libp3pystub.a"):
  486. oscmd("cp -R -P %s/lib/libp3pystub.a dstroot/headers/Developer/Panda3D/lib/" % outputdir)
  487. if os.path.isdir("samples"):
  488. oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D")
  489. oscmd("cp -R samples/* dstroot/samples/Developer/Examples/Panda3D/")
  490. DeleteVCS("dstroot")
  491. DeleteBuildFiles("dstroot")
  492. # Compile Python files. Do this *after* the DeleteVCS step, above, which
  493. # deletes __pycache__ directories.
  494. for version_info in python_versions:
  495. if os.path.isdir("dstroot/pythoncode/Developer/Panda3D/Pmw"):
  496. oscmd("%s -m compileall -q -f -d /Developer/Panda3D/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % (version_info["executable"]), True)
  497. oscmd("%s -m compileall -q -f -d /Developer/Panda3D/direct dstroot/pythoncode/Developer/Panda3D/direct" % (version_info["executable"]))
  498. oscmd("%s -m compileall -q -f -d /Developer/Panda3D/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % (version_info["executable"]))
  499. oscmd("%s -m compileall -q -f -d /Developer/Panda3D/panda3d dstroot/pythoncode/Developer/Panda3D/panda3d" % (version_info["executable"]))
  500. oscmd("chmod -R 0775 dstroot/*")
  501. # We need to be root to perform a chown. Bleh.
  502. # Fortunately PackageMaker does it for us, on 10.5 and above.
  503. #oscmd("chown -R root:admin dstroot/*", True)
  504. oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/")
  505. oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/")
  506. pkgs = ["base", "tools", "headers"]
  507. if python_versions:
  508. pkgs.append("pythoncode")
  509. for version_info in python_versions:
  510. pkgs.append("pybindings" + version_info["version"])
  511. if not PkgSkip("FFMPEG"): pkgs.append("ffmpeg")
  512. #if not PkgSkip("OPENAL"): pkgs.append("openal")
  513. if not PkgSkip("FMODEX"): pkgs.append("fmodex")
  514. if os.path.isdir("samples"): pkgs.append("samples")
  515. for pkg in pkgs:
  516. identifier = "org.panda3d.panda3d.%s.pkg" % pkg
  517. plist = open("/tmp/Info_plist", "w")
  518. plist.write(Info_plist.format(package_id=identifier, version=version))
  519. plist.close()
  520. if not os.path.isdir("dstroot/" + pkg):
  521. os.makedirs("dstroot/" + pkg)
  522. if os.path.exists("/usr/bin/pkgbuild"):
  523. # This new package builder is used in Lion and above.
  524. cmd = '/usr/bin/pkgbuild --identifier ' + identifier + ' --version ' + version + ' --root dstroot/' + pkg + '/ dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg'
  525. # In older versions, we use PackageMaker. Apple keeps changing its location.
  526. elif os.path.exists("/Developer/usr/bin/packagemaker"):
  527. cmd = '/Developer/usr/bin/packagemaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate'
  528. elif os.path.exists("/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"):
  529. cmd = '/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate'
  530. elif os.path.exists("/Developer/Tools/PackageMaker.app/Contents/MacOS/PackageMaker"):
  531. cmd = '/Developer/Tools/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate'
  532. elif os.path.exists("/Developer/Tools/packagemaker"):
  533. cmd = '/Developer/Tools/packagemaker -build -f dstroot/' + pkg + '/ -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg -i /tmp/Info_plist'
  534. elif os.path.exists("/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"):
  535. cmd = '/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + version + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg ' + target + ' --domain system --root dstroot/' + pkg + '/ --no-relocate'
  536. else:
  537. exit("Neither pkgbuild nor PackageMaker could be found!")
  538. oscmd(cmd)
  539. if os.path.isfile("/tmp/Info_plist"):
  540. oscmd("rm -f /tmp/Info_plist")
  541. # Now that we've built all of the individual packages, build the metapackage.
  542. dist = open("dstroot/Panda3D/Panda3D.mpkg/Contents/distribution.dist", "w")
  543. dist.write('<?xml version="1.0" encoding="utf-8"?>\n')
  544. dist.write('<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.3" authoringToolBuild="174">\n')
  545. dist.write(' <title>Panda3D SDK %s</title>\n' % (version))
  546. dist.write(' <options customize="always" allow-external-scripts="no" rootVolumeOnly="false"/>\n')
  547. dist.write(' <license language="en" mime-type="text/plain">%s</license>\n' % ReadFile("doc/LICENSE"))
  548. dist.write(' <script>\n')
  549. dist.write(' function isPythonVersionInstalled(version) {\n')
  550. dist.write(' return system.files.fileExistsAtPath("/usr/bin/python" + version)\n')
  551. dist.write(' || system.files.fileExistsAtPath("/usr/local/bin/python" + version)\n')
  552. dist.write(' || system.files.fileExistsAtPath("/opt/local/bin/python" + version)\n')
  553. dist.write(' || system.files.fileExistsAtPath("/sw/bin/python" + version)\n')
  554. dist.write(' || system.files.fileExistsAtPath("/System/Library/Frameworks/Python.framework/Versions/" + version + "/bin/python")\n')
  555. dist.write(' || system.files.fileExistsAtPath("/Library/Frameworks/Python.framework/Versions/" + version + "/bin/python");\n')
  556. dist.write(' }\n')
  557. dist.write(' </script>\n')
  558. dist.write(' <choices-outline>\n')
  559. dist.write(' <line choice="base"/>\n')
  560. if python_versions:
  561. dist.write(' <line choice="pythoncode">\n')
  562. for version_info in sorted(python_versions, key=lambda info:info["version"], reverse=True):
  563. dist.write(' <line choice="pybindings%s"/>\n' % (version_info["version"]))
  564. dist.write(' </line>\n')
  565. dist.write(' <line choice="tools"/>\n')
  566. if os.path.isdir("samples"):
  567. dist.write(' <line choice="samples"/>\n')
  568. if not PkgSkip("FFMPEG"):
  569. dist.write(' <line choice="ffmpeg"/>\n')
  570. if not PkgSkip("FMODEX"):
  571. dist.write(' <line choice="fmodex"/>\n')
  572. dist.write(' <line choice="headers"/>\n')
  573. dist.write(' </choices-outline>\n')
  574. 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: /Developer/Panda3D/" start_enabled="false">\n')
  575. dist.write(' <pkg-ref id="org.panda3d.panda3d.base.pkg"/>\n')
  576. dist.write(' </choice>\n')
  577. 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: /Developer/Panda3D/bin/">\n')
  578. dist.write(' <pkg-ref id="org.panda3d.panda3d.tools.pkg"/>\n')
  579. dist.write(' </choice>\n')
  580. if python_versions:
  581. 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: /Developer/Panda3D/">\n')
  582. dist.write(' <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg"/>\n')
  583. dist.write(' </choice>\n')
  584. for version_info in python_versions:
  585. pyver = version_info["version"]
  586. if pyver == "2.7":
  587. # Always install Python 2.7 by default; it's included on macOS.
  588. cond = "true"
  589. else:
  590. cond = "isPythonVersionInstalled('%s')" % (pyver)
  591. 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))
  592. dist.write(' <pkg-ref id="org.panda3d.panda3d.pybindings%s.pkg"/>\n' % (pyver))
  593. dist.write(' </choice>\n')
  594. if not PkgSkip("FFMPEG"):
  595. 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.')
  596. if PkgSkip("VORBIS") and PkgSkip("OPUS"):
  597. dist.write(' It is not required for loading .wav files, which Panda3D can read out of the box.">\n')
  598. elif PkgSkip("VORBIS"):
  599. dist.write(' It is not required for loading .wav or .opus files, which Panda3D can read out of the box.">\n')
  600. elif PkgSkip("OPUS"):
  601. dist.write(' It is not required for loading .wav or .ogg files, which Panda3D can read out of the box.">\n')
  602. else:
  603. dist.write(' It is not required for loading .wav, .ogg or .opus files, which Panda3D can read out of the box.">\n')
  604. dist.write(' <pkg-ref id="org.panda3d.panda3d.ffmpeg.pkg"/>\n')
  605. dist.write(' </choice>\n')
  606. #if not PkgSkip("OPENAL"):
  607. # 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')
  608. # dist.write(' <pkg-ref id="org.panda3d.panda3d.openal.pkg"/>\n')
  609. # dist.write(' </choice>\n')
  610. if not PkgSkip("FMODEX"):
  611. 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')
  612. dist.write(' <pkg-ref id="org.panda3d.panda3d.fmodex.pkg"/>\n')
  613. dist.write(' </choice>\n')
  614. if os.path.isdir("samples"):
  615. 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: /Developer/Examples/Panda3D/">\n')
  616. dist.write(' <pkg-ref id="org.panda3d.panda3d.samples.pkg"/>\n')
  617. dist.write(' </choice>\n')
  618. 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: /Developer/Panda3D/include/" start_selected="false">\n')
  619. dist.write(' <pkg-ref id="org.panda3d.panda3d.headers.pkg"/>\n')
  620. dist.write(' </choice>\n')
  621. for pkg in pkgs:
  622. size = GetDirectorySize("dstroot/" + pkg) // 1024
  623. 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))
  624. dist.write('</installer-script>\n')
  625. dist.close()
  626. oscmd('hdiutil create Panda3D-rw.dmg -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (version))
  627. oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o %s' % (dmg_name))
  628. oscmd('rm -f Panda3D-rw.dmg')
  629. def MakeInstallerFreeBSD(version, runtime=False, **kwargs):
  630. outputdir = GetOutputDir()
  631. oscmd("rm -rf targetroot +DESC pkg-plist +MANIFEST")
  632. oscmd("mkdir targetroot")
  633. # Invoke installpanda.py to install it into a temporary dir
  634. if runtime:
  635. InstallRuntime(destdir="targetroot", prefix="/usr/local", outputdir=outputdir)
  636. else:
  637. InstallPanda(destdir="targetroot", prefix="/usr/local", outputdir=outputdir)
  638. if not os.path.exists("/usr/sbin/pkg"):
  639. exit("Cannot create an installer without pkg")
  640. plist_txt = ''
  641. for root, dirs, files in os.walk("targetroot/usr/local/", True):
  642. for f in files:
  643. plist_txt += os.path.join(root, f)[21:] + "\n"
  644. if not runtime:
  645. plist_txt += "@postexec /sbin/ldconfig -m /usr/local/lib/panda3d\n"
  646. plist_txt += "@postunexec /sbin/ldconfig -R\n"
  647. for remdir in ("lib/panda3d", "share/panda3d", "include/panda3d"):
  648. for root, dirs, files in os.walk("targetroot/usr/local/" + remdir, False):
  649. for d in dirs:
  650. plist_txt += "@dir %s\n" % os.path.join(root, d)[21:]
  651. plist_txt += "@dir %s\n" % remdir
  652. oscmd("echo \"`pkg config abi | tr '[:upper:]' '[:lower:]' | cut -d: -f1,2`:*\" > " + outputdir + "/tmp/architecture.txt")
  653. pkg_arch = ReadFile(outputdir+"/tmp/architecture.txt").strip()
  654. dependencies = ''
  655. if not PkgSkip("PYTHON"):
  656. # If this version of Python was installed from a package or ports, let's mark it as dependency.
  657. oscmd("rm -f %s/tmp/python_dep" % outputdir)
  658. if "PYTHONVERSION" in SDK:
  659. pyver_nodot = SDK["PYTHONVERSION"][6:9:2]
  660. else:
  661. pyver_nodot = "%d%d" % (sys.version_info[:2])
  662. 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)
  663. if os.path.isfile(outputdir + "/tmp/python_dep"):
  664. python_pkg = ReadFile(outputdir + "/tmp/python_dep")
  665. if python_pkg:
  666. dependencies += python_pkg
  667. manifest_txt = INSTALLER_PKG_MANIFEST_FILE[1:].replace("NAME", 'panda3d' if not runtime else 'panda3d-runtime')
  668. manifest_txt = manifest_txt.replace("VERSION", version)
  669. manifest_txt = manifest_txt.replace("ARCH", pkg_arch)
  670. manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not runtime else 'graphics/panda3d-runtime')
  671. manifest_txt = manifest_txt.replace("DEPENDS", dependencies)
  672. manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024 // 1024))
  673. WriteFile("pkg-plist", plist_txt)
  674. WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:] if not runtime else RUNTIME_INSTALLER_PKG_DESCR_FILE[1:])
  675. WriteFile("+MANIFEST", manifest_txt)
  676. oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet"))
  677. def MakeInstallerAndroid(version, **kwargs):
  678. outputdir = GetOutputDir()
  679. oscmd("rm -rf apkroot")
  680. oscmd("mkdir apkroot")
  681. # Also remove the temporary apks.
  682. apk_unaligned = os.path.join(outputdir, "tmp", "panda3d-unaligned.apk")
  683. apk_unsigned = os.path.join(outputdir, "tmp", "panda3d-unsigned.apk")
  684. if os.path.exists(apk_unaligned):
  685. os.unlink(apk_unaligned)
  686. if os.path.exists(apk_unsigned):
  687. os.unlink(apk_unsigned)
  688. # Compile the Java classes into a Dalvik executable.
  689. dx_cmd = "dx --dex --output=apkroot/classes.dex "
  690. if GetOptimize() <= 2:
  691. dx_cmd += "--debug "
  692. if GetVerbose():
  693. dx_cmd += "--verbose "
  694. if "ANDROID_API" in SDK:
  695. dx_cmd += "--min-sdk-version=%d " % (SDK["ANDROID_API"])
  696. dx_cmd += os.path.join(outputdir, "classes")
  697. oscmd(dx_cmd)
  698. # Copy the libraries one by one. In case of library dependencies, strip
  699. # off any suffix (eg. libfile.so.1.0), as Android does not support them.
  700. source_dir = os.path.join(outputdir, "lib")
  701. target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
  702. oscmd("mkdir -p %s" % (target_dir))
  703. # Determine the library directories we should look in.
  704. libpath = [source_dir]
  705. for dir in os.environ.get("LD_LIBRARY_PATH", "").split(':'):
  706. dir = os.path.expandvars(dir)
  707. dir = os.path.expanduser(dir)
  708. if os.path.isdir(dir):
  709. dir = os.path.realpath(dir)
  710. if not dir.startswith("/system") and not dir.startswith("/vendor"):
  711. libpath.append(dir)
  712. def copy_library(source, base):
  713. # Copy file to destination, stripping version suffix.
  714. target = os.path.join(target_dir, base)
  715. if not target.endswith('.so'):
  716. target = target.rpartition('.so.')[0] + '.so'
  717. if os.path.isfile(target):
  718. # Already processed.
  719. return
  720. oscmd("cp %s %s" % (source, target))
  721. # Walk through the library dependencies.
  722. oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, outputdir), True)
  723. for line in open(outputdir + "/tmp/otool-libs.txt", "r"):
  724. line = line.strip()
  725. if not line:
  726. continue
  727. if '.so.' in line:
  728. dep = line.rpartition('.so.')[0] + '.so'
  729. oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True)
  730. else:
  731. dep = line
  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. import _ctypes
  763. source_dir = os.path.dirname(_ctypes.__file__)
  764. for base in os.listdir(source_dir):
  765. if not base.endswith('.so'):
  766. continue
  767. modname = base.partition('.')[0]
  768. source = os.path.join(source_dir, base)
  769. copy_library(source, "libpy.{}.so".format(modname))
  770. def copy_python_tree(source_root, target_root):
  771. for source_dir, dirs, files in os.walk(source_root):
  772. if 'site-packages' in dirs:
  773. dirs.remove('site-packages')
  774. if not any(base.endswith('.py') for base in files):
  775. continue
  776. target_dir = os.path.join(target_root, os.path.relpath(source_dir, source_root))
  777. target_dir = os.path.normpath(target_dir)
  778. os.makedirs(target_dir, 0o755)
  779. for base in files:
  780. if base.endswith('.py'):
  781. target = os.path.join(target_dir, base)
  782. shutil.copy(os.path.join(source_dir, base), target)
  783. # Copy the Python standard library to the .apk as well.
  784. from distutils.sysconfig import get_python_lib
  785. stdlib_source = get_python_lib(False, True)
  786. stdlib_target = os.path.join("apkroot", "lib", "python{0}.{1}".format(*sys.version_info))
  787. copy_python_tree(stdlib_source, stdlib_target)
  788. # But also copy over our custom site.py.
  789. shutil.copy("panda/src/android/site.py", os.path.join(stdlib_target, "site.py"))
  790. # And now make a site-packages directory containing our direct/panda3d/pandac modules.
  791. for tree in "panda3d", "direct", "pandac":
  792. copy_python_tree(os.path.join(outputdir, tree), os.path.join(stdlib_target, "site-packages", tree))
  793. # Copy the models and config files to the virtual assets filesystem.
  794. oscmd("mkdir apkroot/assets")
  795. oscmd("cp -R %s apkroot/assets/models" % (os.path.join(outputdir, "models")))
  796. oscmd("cp -R %s apkroot/assets/etc" % (os.path.join(outputdir, "etc")))
  797. # Make an empty res folder. It's needed for the apk to be installable, apparently.
  798. oscmd("mkdir apkroot/res")
  799. # Now package up the application
  800. oscmd("cp panda/src/android/pview_manifest.xml apkroot/AndroidManifest.xml")
  801. aapt_cmd = "aapt package"
  802. aapt_cmd += " -F %s" % (apk_unaligned)
  803. aapt_cmd += " -M apkroot/AndroidManifest.xml"
  804. aapt_cmd += " -A apkroot/assets -S apkroot/res"
  805. aapt_cmd += " -I $PREFIX/share/aapt/android.jar"
  806. oscmd(aapt_cmd)
  807. # And add all the libraries to it.
  808. oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned))
  809. for path, dirs, files in os.walk('apkroot/lib'):
  810. if files:
  811. rel = os.path.relpath(path, 'apkroot')
  812. oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel))
  813. # Now align the .apk, which is necessary for Android to load it.
  814. oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
  815. # Finally, sign it using a debug key. This is generated if it doesn't exist.
  816. oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
  817. # Clean up.
  818. oscmd("rm -rf apkroot")
  819. os.unlink(apk_unaligned)
  820. os.unlink(apk_unsigned)
  821. def MakeInstaller(version, **kwargs):
  822. target = GetTarget()
  823. if target == 'windows':
  824. fn = "Panda3D-"
  825. dir = "Panda3D-" + version
  826. runtime = kwargs.get('runtime', False)
  827. if runtime:
  828. fn += "Runtime-"
  829. title = "Panda3D " + version
  830. else:
  831. title = "Panda3D SDK " + version
  832. fn += version
  833. python_versions = kwargs.get('python_versions', [])
  834. if not runtime and len(python_versions) == 1 and python_versions[0]["version"] != "2.7":
  835. fn += '-py' + python_versions[0]["version"]
  836. if GetOptimize() <= 2:
  837. fn += "-dbg"
  838. if GetTargetArch() == 'x64':
  839. fn += '-x64'
  840. dir += '-x64'
  841. MakeInstallerNSIS(version, fn + '.exe', title, 'C:\\' + dir, **kwargs)
  842. if not runtime:
  843. MakeDebugSymbolArchive(fn + '-pdb.zip', dir)
  844. elif target == 'linux':
  845. MakeInstallerLinux(version, **kwargs)
  846. elif target == 'darwin':
  847. MakeInstallerOSX(version, **kwargs)
  848. elif target == 'freebsd':
  849. MakeInstallerFreeBSD(version, **kwargs)
  850. elif target == 'android':
  851. MakeInstallerAndroid(version, **kwargs)
  852. else:
  853. exit("Do not know how to make an installer for this platform")
  854. if __name__ == "__main__":
  855. version = ParsePandaVersion("dtool/PandaVersion.pp")
  856. parser = OptionParser()
  857. parser.add_option('', '--version', dest='version', help='Panda3D version number (default: %s)' % (version), default=version)
  858. parser.add_option('', '--debversion', dest='debversion', help='Version number for .deb file', default=None)
  859. parser.add_option('', '--rpmrelease', dest='rpmrelease', help='Release number for .rpm file', default='1')
  860. parser.add_option('', '--outputdir', dest='outputdir', help='Makepanda\'s output directory (default: built)', default='built')
  861. parser.add_option('', '--verbose', dest='verbose', help='Enable verbose output', action='store_true', default=False)
  862. parser.add_option('', '--runtime', dest='runtime', help='Runtime instead of SDK', action='store_true', default=False)
  863. parser.add_option('', '--lzma', dest='compressor', help='Use LZMA compression', action='store_const', const='lzma', default='zlib')
  864. (options, args) = parser.parse_args()
  865. SetVerbose(options.verbose)
  866. SetOutputDir(options.outputdir)
  867. # Read out the optimize option.
  868. opt = ReadFile(os.path.join(options.outputdir, "tmp", "optimize.dat"))
  869. SetOptimize(int(opt.strip()))
  870. # Read out whether we should set PkgSkip("PYTHON") and some others.
  871. # Temporary hack; needs better solution.
  872. pkg_list = "PYTHON", "NVIDIACG", "FFMPEG", "OPENAL", "FMODEX", "PVIEW", "NVIDIACG", "VORBIS", "OPUS"
  873. PkgListSet(pkg_list)
  874. for pkg in pkg_list:
  875. dat_path = "dtool_have_%s.dat" % (pkg.lower())
  876. content = ReadFile(os.path.join(options.outputdir, "tmp", dat_path))
  877. if int(content.strip()):
  878. PkgEnable(pkg)
  879. else:
  880. PkgDisable(pkg)
  881. # Parse the version.
  882. match = re.match(r'^\d+\.\d+\.\d+', options.version)
  883. if not match:
  884. exit("version requires three digits")
  885. MakeInstaller(version=match.group(),
  886. outputdir=options.outputdir,
  887. optimize=GetOptimize(),
  888. compressor=options.compressor,
  889. debversion=options.debversion,
  890. rpmrelease=options.rpmrelease,
  891. runtime=options.runtime,
  892. python_versions=ReadPythonVersionInfoFile())