pdeploy.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. tokens are width, height, log_basename, auto_start and hidden.
  53. -P platform
  54. If this option is provided, it should specify a comma-
  55. separated list of platforms that the p3d package will be
  56. deployed for. If omitted, it will be built for all platforms.
  57. This option may be specified multiple times.
  58. Examples of valid platforms are win32, linux_amd64 and osx_ppc.
  59. -c
  60. If this option is provided, any -P options are ignored and
  61. the p3d package is only deployed for the current platform.
  62. Furthermore, no per-platform subdirectories will be created
  63. inside the output dirctory.
  64. -s
  65. This option only has effect in 'installer' mode. If it is
  66. provided, the resulting installers will be fully self-contained,
  67. will not require an internet connection to run, and start up
  68. much faster. Note that pdeploy will also take a very long time
  69. to finish when -s is provided.
  70. If it is omitted, pdeploy will finish much quicker, and the
  71. resulting installers will be smaller, but they will require
  72. an internet connection for the first run, and the load time
  73. will be considerably longer.
  74. -l "License Name"
  75. Specifies the name of the software license that the game
  76. or application is licensed under.
  77. Only relevant when generating a graphical installer.
  78. -L licensefile.txt
  79. This should point to a file that contains the full text
  80. describing the software license that the game or application
  81. is licensed under.
  82. Only relevant when generating a graphical installer.
  83. -a com.your_company
  84. Short identifier of the author of the application. The default
  85. is "org.panda3d", but you will most likely want to change
  86. it to your own name or that of your organization or company.
  87. Only relevant when generating a graphical installer.
  88. -A "Your Company"
  89. Full name of the author of the application. The default is
  90. determined from the GECOS information of the current user if
  91. available; if not, your username is used.
  92. Only relevant when generating a graphical installer.
  93. -e "you@your_company.com"
  94. E-mail address of the maintainer of the application. The default
  95. is username@hostname.
  96. Only relevant when generating a graphical installer.
  97. -h
  98. Display this help
  99. """
  100. DEPLOY_MODES = ["standalone", "installer", "html"]
  101. import sys
  102. import os
  103. import getopt
  104. from direct.p3d.DeploymentTools import Standalone, Installer
  105. from pandac.PandaModules import Filename, PandaSystem
  106. def usage(code, msg = ''):
  107. print >> sys.stderr, usageText % {'prog' : os.path.split(sys.argv[0])[1]}
  108. print >> sys.stderr, msg
  109. sys.exit(code)
  110. shortname = ""
  111. fullname = ""
  112. version = ""
  113. outputDir = Filename("./")
  114. tokens = {}
  115. platforms = []
  116. currentPlatform = False
  117. licensename = ""
  118. licensefile = Filename()
  119. authorid = ""
  120. authorname = ""
  121. authoremail = ""
  122. includeRequires = False
  123. try:
  124. opts, args = getopt.getopt(sys.argv[1:], 'n:N:v:o:t:P:csl:L:a:A:e:h')
  125. except getopt.error, msg:
  126. usage(1, msg)
  127. for opt, arg in opts:
  128. if opt == '-n':
  129. shortname = arg.strip()
  130. elif opt == '-N':
  131. fullname = arg.strip()
  132. elif opt == '-v':
  133. version = arg.strip()
  134. elif opt == '-o':
  135. outputDir = Filename.fromOsSpecific(arg)
  136. elif opt == '-t':
  137. token = arg.strip().split("=", 1)
  138. tokens[token[0]] = token[1]
  139. elif opt == '-P':
  140. platforms.append(arg)
  141. elif opt == '-c':
  142. currentPlatform = True
  143. elif opt == '-s':
  144. includeRequires = True
  145. elif opt == '-l':
  146. licensename = arg.strip()
  147. elif opt == '-L':
  148. licensefile = Filename.fromOsSpecific(arg)
  149. elif opt == '-a':
  150. authorid = arg.strip()
  151. elif opt == '-A':
  152. authorname = arg.strip()
  153. elif opt == '-e':
  154. authoremail = arg.strip()
  155. elif opt == '-h':
  156. usage(0)
  157. else:
  158. print 'illegal option: ' + flag
  159. sys.exit(1)
  160. if not args or len(args) != 2:
  161. usage(1)
  162. appFilename = Filename.fromOsSpecific(args[0])
  163. if appFilename.getExtension().lower() != 'p3d':
  164. print 'Application filename must end in ".p3d".'
  165. sys.exit(1)
  166. deploy_mode = args[1].lower()
  167. if not appFilename.exists():
  168. print 'Application filename does not exist!'
  169. sys.exit(1)
  170. if shortname == '':
  171. shortname = appFilename.getBasenameWoExtension()
  172. if shortname.lower() != shortname or ' ' in shortname:
  173. print '\nProvided short name should be lowercase, and may not contain spaces!\n'
  174. if version == '' and deploy_mode == 'installer':
  175. print '\nA version number is required in "installer" mode.\n'
  176. sys.exit(1)
  177. if not outputDir:
  178. print '\nYou must name the output directory with the -o parameter.\n'
  179. sys.exit(1)
  180. if not outputDir.exists():
  181. print '\nThe specified output directory does not exist!\n'
  182. sys.exit(1)
  183. elif not outputDir.isDirectory():
  184. print '\nThe specified output directory is a file!\n'
  185. sys.exit(1)
  186. if deploy_mode == 'standalone':
  187. s = Standalone(appFilename, tokens)
  188. s.basename = shortname
  189. if currentPlatform:
  190. platform = PandaSystem.getPlatform()
  191. if platform.startswith("win"):
  192. s.build(Filename(outputDir, shortname + ".exe"), platform)
  193. else:
  194. s.build(Filename(outputDir, shortname), platform)
  195. elif len(platforms) == 0:
  196. s.buildAll(outputDir)
  197. else:
  198. for platform in platforms:
  199. if platform.startswith("win"):
  200. s.build(Filename(outputDir, platform + "/" + shortname + ".exe"), platform)
  201. else:
  202. s.build(Filename(outputDir, platform + "/" + shortname), platform)
  203. elif deploy_mode == 'installer':
  204. if includeRequires:
  205. tokens["verify_contents"] = "never"
  206. i = Installer(appFilename, shortname, fullname, version, tokens = tokens)
  207. i.licensename = licensename
  208. i.licensefile = licensefile
  209. if authorid:
  210. i.authorid = authorid
  211. if authorname:
  212. i.authorname = authorname
  213. if authoremail:
  214. i.authoremail = authoremail
  215. i.includeRequires = includeRequires
  216. if currentPlatform:
  217. platform = PandaSystem.getPlatform()
  218. if platform.startswith("win"):
  219. i.build(outputDir, platform)
  220. else:
  221. i.build(outputDir, platform)
  222. elif len(platforms) == 0:
  223. i.buildAll(outputDir)
  224. else:
  225. for platform in platforms:
  226. output = Filename(outputDir, platform + "/")
  227. output.makeDir()
  228. i.build(output, platform)
  229. elif deploy_mode == 'html':
  230. w, h = tokens.get("width", 640), tokens.get("height", 480)
  231. if "data" not in tokens:
  232. tokens["data"] = appFilename.getBasename()
  233. print "Creating %s.html..." % shortname
  234. html = open(Filename(outputDir, shortname + ".html").toOsSpecific(), "w")
  235. print >>html, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
  236. print >>html, "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
  237. print >>html, " <head>"
  238. print >>html, " <title>%s</title>" % fullname
  239. print >>html, " <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />"
  240. if authorname:
  241. print >>html, " <meta name=\"Author\" content=\"%s\" />" % authorname.replace('"', '\\"')
  242. print >>html, " </head>"
  243. print >>html, " <body>"
  244. print >>html, " <object",
  245. for key, value in tokens.items():
  246. print >>html, "%s=\"%s\"" % (key, value.replace('"', '\\"')),
  247. if "width" not in tokens:
  248. print >>html, "width=\"%s\"" % w,
  249. if "height" not in tokens:
  250. print >>html, "height=\"%s\"" % h,
  251. print >>html, "type=\"application/x-panda3d\">"
  252. print >>html, " <object width=\"%s\" height=\"%s\" classid=\"CLSID:924B4927-D3BA-41EA-9F7E-8A89194AB3AC\">" % (w, h)
  253. for key, value in tokens.items():
  254. print >>html, " <param name=\"%s\" value=\"%s\" />" % (key, value.replace('"', '\\"'))
  255. print >>html, " </object>"
  256. print >>html, " </object>"
  257. print >>html, " </body>"
  258. print >>html, "</html>"
  259. html.close()
  260. else:
  261. usage(1, 'Invalid deployment mode!')
  262. # An explicit call to exit() is required to exit the program, when
  263. # this module is packaged in a p3d file.
  264. sys.exit(0)