pdeploy.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. #! /usr/bin/env python
  2. usageText = """
  3. This command will help you to distribute your Panda application,
  4. consisting of a .p3d package, into a standalone executable, graphical
  5. installer or an HTML webpage. It will attempt to create packages
  6. for every platform, if possible.
  7. Note that pdeploy requires an internet connection to run.
  8. When used with the 'installer' option, it is strongly advisable
  9. to specify most if not all of the descriptive information that can
  10. be passed on the command-line.
  11. Usage:
  12. %(prog)s [opts] app.p3d standalone|installer|html
  13. Modes:
  14. standalone
  15. A standalone executable will be created that embeds the given
  16. p3d file. The resulting executable will require an
  17. internet connection in order to run properly.
  18. installer
  19. In this mode, installable packages will be created for as many
  20. platforms as possible. To create Windows installers on
  21. non-Windows platforms, you need to have the "makensis" utility
  22. on your system PATH environment variable.
  23. html
  24. An HTML webpage will be generated that can be used to view
  25. the provided p3d file in a browser.
  26. Options:
  27. -n your_app
  28. Short, lowercase name of the application or game. May only
  29. contain alphanumeric characters, underscore or dash. This
  30. name will also define the output file(s) of the process.
  31. If omitted, the basename of the p3d file is used.
  32. -N "Your Application"
  33. Full name of the application or game. This is the
  34. name that will be displayed to the end-user.
  35. The 'display_name' config is used by default. If it
  36. is missing, the short name is used.
  37. -v version_number
  38. This should define the version number of your application
  39. or game. In some deploy modes, this argument is required.
  40. This should only contain alphanumeric characters, dots and
  41. dashes, as otherwise the result of the deployment may be
  42. invalid on some platforms.
  43. -o output_dir
  44. Indicates the directory where the output will be stored.
  45. Within this directory, subdirectories will be created
  46. for every platform, unless -t is provided.
  47. If omitted, the current working directory is assumed.
  48. -t token=value
  49. Defines a web token or parameter to pass to the application.
  50. Use this to configure how the application will be run.
  51. You can pass as many -t options as you need. Some examples of
  52. useful token names are width, height, log_basename, auto_start,
  53. hidden and console_environment.
  54. -P platform
  55. If this option is provided, it should specify a comma-
  56. separated list of platforms that the p3d package will be
  57. deployed for. If omitted, it will be built for all platforms.
  58. This option may be specified multiple times.
  59. Examples of valid platforms are win32, linux_amd64 and osx_ppc.
  60. -c
  61. If this option is provided, any -P options are ignored and
  62. the p3d package is only deployed for the current platform.
  63. Furthermore, no per-platform subdirectories will be created
  64. inside the output dirctory.
  65. -s
  66. This option only has effect in 'installer' mode. If it is
  67. provided, the resulting installers will be fully self-contained,
  68. will not require an internet connection to run, and start up
  69. much faster. Note that pdeploy will also take a very long time
  70. to finish when -s is provided.
  71. If it is omitted, pdeploy will finish much quicker, and the
  72. resulting installers will be smaller, but they will require
  73. an internet connection for the first run, and the load time
  74. will be considerably longer.
  75. -l "License Name"
  76. Specifies the name of the software license that the game
  77. or application is licensed under.
  78. Only relevant when generating a graphical installer.
  79. -L licensefile.txt
  80. This should point to a file that contains the full text
  81. describing the software license that the game or application
  82. is licensed under.
  83. Only relevant when generating a graphical installer.
  84. -a com.your_company
  85. Short identifier of the author of the application. The default
  86. is "org.panda3d", but you will most likely want to change
  87. it to your own name or that of your organization or company.
  88. Only relevant when generating a graphical installer.
  89. -A "Your Company"
  90. Full name of the author of the application. The default is
  91. determined from the GECOS information of the current user if
  92. available; if not, your username is used.
  93. Only relevant when generating a graphical installer.
  94. -e "you@your_company.com"
  95. E-mail address of the maintainer of the application. The default
  96. is username@hostname.
  97. Only relevant when generating a graphical installer.
  98. -h
  99. Display this help
  100. """
  101. DEPLOY_MODES = ["standalone", "installer", "html"]
  102. import sys
  103. import os
  104. import getopt
  105. from direct.p3d.DeploymentTools import Standalone, Installer
  106. from pandac.PandaModules import Filename, PandaSystem
  107. def usage(code, msg = ''):
  108. if not msg:
  109. print >> sys.stderr, usageText % {'prog' : os.path.split(sys.argv[0])[1]}
  110. print >> sys.stderr, msg
  111. sys.exit(code)
  112. shortname = ""
  113. fullname = ""
  114. version = ""
  115. outputDir = Filename("./")
  116. tokens = {}
  117. platforms = []
  118. currentPlatform = False
  119. licensename = ""
  120. licensefile = Filename()
  121. authorid = ""
  122. authorname = ""
  123. authoremail = ""
  124. includeRequires = False
  125. try:
  126. opts, args = getopt.getopt(sys.argv[1:], 'n:N:v:o:t:P:csl:L:a:A:e:h')
  127. except getopt.error, msg:
  128. usage(1, msg or 'Invalid option')
  129. for opt, arg in opts:
  130. if opt == '-n':
  131. shortname = arg.strip()
  132. elif opt == '-N':
  133. fullname = arg.strip()
  134. elif opt == '-v':
  135. version = arg.strip()
  136. elif opt == '-o':
  137. outputDir = Filename.fromOsSpecific(arg)
  138. elif opt == '-t':
  139. token = arg.strip().split("=", 1)
  140. tokens[token[0]] = token[1]
  141. elif opt == '-P':
  142. platforms.append(arg)
  143. elif opt == '-c':
  144. currentPlatform = True
  145. elif opt == '-s':
  146. includeRequires = True
  147. elif opt == '-l':
  148. licensename = arg.strip()
  149. elif opt == '-L':
  150. licensefile = Filename.fromOsSpecific(arg)
  151. elif opt == '-a':
  152. authorid = arg.strip()
  153. elif opt == '-A':
  154. authorname = arg.strip()
  155. elif opt == '-e':
  156. authoremail = arg.strip()
  157. elif opt == '-h':
  158. usage(0)
  159. else:
  160. msg = 'illegal option: ' + flag
  161. print msg
  162. sys.exit(1, msg)
  163. if not args or len(args) != 2:
  164. msg = 'Wrong number of arguments, received %s, expected 2' % (
  165. len(args or []))
  166. usage(1, msg)
  167. appFilename = Filename.fromOsSpecific(args[0])
  168. if appFilename.getExtension().lower() != 'p3d':
  169. print 'Application filename must end in ".p3d".'
  170. sys.exit(1)
  171. deploy_mode = args[1].lower()
  172. if not appFilename.exists():
  173. print 'Application filename does not exist!'
  174. sys.exit(1)
  175. if shortname == '':
  176. shortname = appFilename.getBasenameWoExtension()
  177. if shortname.lower() != shortname or ' ' in shortname:
  178. print '\nProvided short name should be lowercase, and may not contain spaces!\n'
  179. if version == '' and deploy_mode == 'installer':
  180. print '\nA version number is required in "installer" mode.\n'
  181. sys.exit(1)
  182. if not outputDir:
  183. print '\nYou must name the output directory with the -o parameter.\n'
  184. sys.exit(1)
  185. if not outputDir.exists():
  186. print '\nThe specified output directory does not exist!\n'
  187. sys.exit(1)
  188. elif not outputDir.isDirectory():
  189. print '\nThe specified output directory is a file!\n'
  190. sys.exit(1)
  191. if deploy_mode == 'standalone':
  192. s = Standalone(appFilename, tokens)
  193. s.basename = shortname
  194. if currentPlatform:
  195. platform = PandaSystem.getPlatform()
  196. if platform.startswith("win"):
  197. s.build(Filename(outputDir, shortname + ".exe"), platform)
  198. else:
  199. s.build(Filename(outputDir, shortname), platform)
  200. elif len(platforms) == 0:
  201. s.buildAll(outputDir)
  202. else:
  203. for platform in platforms:
  204. if platform.startswith("win"):
  205. s.build(Filename(outputDir, platform + "/" + shortname + ".exe"), platform)
  206. else:
  207. s.build(Filename(outputDir, platform + "/" + shortname), platform)
  208. elif deploy_mode == 'installer':
  209. if includeRequires:
  210. tokens["verify_contents"] = "never"
  211. i = Installer(appFilename, shortname, fullname, version, tokens = tokens)
  212. i.includeRequires = includeRequires
  213. i.licensename = licensename
  214. i.licensefile = licensefile
  215. if authorid:
  216. i.authorid = authorid
  217. if authorname:
  218. i.authorname = authorname
  219. if authoremail:
  220. i.authoremail = authoremail
  221. if not authorname or not authoremail or not authorid:
  222. print "Using author \"%s\" <%s> with ID %s" % \
  223. (i.authorname, i.authoremail, i.authorid)
  224. if currentPlatform:
  225. platform = PandaSystem.getPlatform()
  226. if platform.startswith("win"):
  227. i.build(outputDir, platform)
  228. else:
  229. i.build(outputDir, platform)
  230. elif len(platforms) == 0:
  231. i.buildAll(outputDir)
  232. else:
  233. for platform in platforms:
  234. output = Filename(outputDir, platform + "/")
  235. output.makeDir()
  236. i.build(output, platform)
  237. elif deploy_mode == 'html':
  238. w, h = tokens.get("width", 640), tokens.get("height", 480)
  239. if "data" not in tokens:
  240. tokens["data"] = appFilename.getBasename()
  241. print "Creating %s.html..." % shortname
  242. html = open(Filename(outputDir, shortname + ".html").toOsSpecific(), "w")
  243. print >>html, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
  244. print >>html, "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
  245. print >>html, " <head>"
  246. print >>html, " <title>%s</title>" % fullname
  247. print >>html, " <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />"
  248. if authorname:
  249. print >>html, " <meta name=\"Author\" content=\"%s\" />" % authorname.replace('"', '\\"')
  250. print >>html, " </head>"
  251. print >>html, " <body>"
  252. print >>html, " <object",
  253. for key, value in tokens.items():
  254. print >>html, "%s=\"%s\"" % (key, value.replace('"', '\\"')),
  255. if "width" not in tokens:
  256. print >>html, "width=\"%s\"" % w,
  257. if "height" not in tokens:
  258. print >>html, "height=\"%s\"" % h,
  259. print >>html, "type=\"application/x-panda3d\">"
  260. print >>html, " <object width=\"%s\" height=\"%s\" classid=\"CLSID:924B4927-D3BA-41EA-9F7E-8A89194AB3AC\">" % (w, h)
  261. for key, value in tokens.items():
  262. print >>html, " <param name=\"%s\" value=\"%s\" />" % (key, value.replace('"', '\\"'))
  263. print >>html, " </object>"
  264. print >>html, " </object>"
  265. print >>html, " </body>"
  266. print >>html, "</html>"
  267. html.close()
  268. else:
  269. usage(1, 'Invalid deployment mode!')
  270. # An explicit call to exit() is required to exit the program, when
  271. # this module is packaged in a p3d file.
  272. sys.exit(0)