PageRenderTime 66ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/media/qooxdoo-1.4-sdk/tool/bin/create-application.py

https://github.com/vuuvv/vshop1
Python | 353 lines | 269 code | 50 blank | 34 comment | 53 complexity | 6a7db36ac5025bafb4d7c3ef42a4f1c4 MD5 | raw file
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ################################################################################
  4. #
  5. # qooxdoo - the new era of web development
  6. #
  7. # http://qooxdoo.org
  8. #
  9. # Copyright:
  10. # 2008 - 2010 1&1 Internet AG, Germany, http://www.1und1.de
  11. #
  12. # License:
  13. # LGPL: http://www.gnu.org/licenses/lgpl.html
  14. # EPL: http://www.eclipse.org/org/documents/epl-v10.php
  15. # See the LICENSE file in the project's top-level directory for details.
  16. #
  17. # Authors:
  18. # * Fabian Jakobs (fjakobs)
  19. # * Andreas Ecker (ecker)
  20. #
  21. ################################################################################
  22. import re, os, sys, optparse, shutil, errno, stat, codecs, glob
  23. from string import Template
  24. import qxenviron
  25. from ecmascript.frontend import lang
  26. from generator.runtime.Log import Log
  27. from misc import Path
  28. SCRIPT_DIR = qxenviron.scriptDir
  29. FRAMEWORK_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
  30. SKELETON_DIR = unicode(os.path.normpath(os.path.join(FRAMEWORK_DIR, "component", "skeleton")))
  31. APP_DIRS = [x for x in os.listdir(SKELETON_DIR) if not re.match(r'^\.',x)]
  32. R_ILLEGAL_NS_CHAR = re.compile(r'(?u)[^\.\w]') # allow unicode, but disallow $
  33. R_SHORT_DESC = re.compile(r'(?m)^short:: (.*)$') # to search "short:: ..." in skeleton's 'readme.txt'
  34. QOOXDOO_VERSION = '' # will be filled later
  35. def getAppInfos():
  36. appInfos = {}
  37. for dir in APP_DIRS:
  38. readme = os.path.join(SKELETON_DIR, dir, "readme.txt")
  39. appinfo = ""
  40. if os.path.isfile(readme):
  41. cont = open(readme, "r").read()
  42. mo = R_SHORT_DESC.search(cont)
  43. if mo:
  44. appinfo = mo.group(1)
  45. appInfos[dir] = appinfo
  46. return appInfos
  47. APP_INFOS = getAppInfos()
  48. def getQxVersion():
  49. global QOOXDOO_VERSION
  50. versionFile = os.path.join(FRAMEWORK_DIR, "version.txt")
  51. version = codecs.open(versionFile,"r", "utf-8").read()
  52. version = version.strip()
  53. QOOXDOO_VERSION = version
  54. return
  55. def createApplication(options):
  56. out = options.out
  57. if sys.platform == 'win32' and re.match( r'^[a-zA-Z]:$', out):
  58. out = out + '\\'
  59. else:
  60. out = os.path.expanduser(out)
  61. if not os.path.isdir(out):
  62. if os.path.isdir(normalizePath(out)):
  63. out = normalizePath(out)
  64. else:
  65. console.error("Output directory '%s' does not exist" % out)
  66. sys.exit(1)
  67. outDir = os.path.join(out, options.name)
  68. copySkeleton(options.skeleton_path, options.type, outDir, options.namespace)
  69. if options.type == "contribution":
  70. patchSkeleton(os.path.join(outDir, "trunk"), FRAMEWORK_DIR, options)
  71. else:
  72. patchSkeleton(outDir, FRAMEWORK_DIR, options)
  73. return
  74. def copySkeleton(skeleton_path, app_type, dir, namespace):
  75. console.log("Copy skeleton into the output directory: %s" % dir)
  76. def rename_folders(root_dir):
  77. # rename name space parts of paths
  78. # rename in class path
  79. source_dir = os.path.join(root_dir, "source", "class", "custom")
  80. out_dir = os.path.join(root_dir, "source", "class")
  81. expand_dir(source_dir, out_dir, namespace)
  82. # rename in resource path
  83. resource_dir = os.path.join(root_dir, "source", "resource", "custom")
  84. out_dir = os.path.join(root_dir, "source", "resource")
  85. expand_dir(resource_dir, out_dir, namespace)
  86. # rename in script path
  87. script_dir = os.path.join(root_dir, "source", "script")
  88. script_files = glob.glob(os.path.join(script_dir, "custom.*js"))
  89. if script_files:
  90. for script_file in script_files:
  91. os.rename(script_file, script_file.replace("custom", namespace))
  92. template = os.path.join(skeleton_path, app_type)
  93. if not os.path.isdir(template):
  94. console.error("Unknown application type '%s'." % app_type)
  95. sys.exit(1)
  96. try:
  97. shutil.copytree(template, dir)
  98. except OSError:
  99. console.error("Failed to copy skeleton, maybe the directory already exists")
  100. sys.exit(1)
  101. if app_type == "contribution":
  102. app_dir = os.path.join(dir, "trunk")
  103. else:
  104. app_dir = dir
  105. rename_folders(app_dir)
  106. if app_type == "contribution":
  107. rename_folders(os.path.join(app_dir, "demo", "default"))
  108. #clean svn directories
  109. for root, dirs, files in os.walk(dir, topdown=False):
  110. if ".svn" in dirs:
  111. filename = os.path.join(root, ".svn")
  112. shutil.rmtree(filename, ignore_errors=False, onerror=handleRemoveReadonly)
  113. def expand_dir(indir, outroot, namespace):
  114. "appends namespace parts to outroot, and renames indir to the last part"
  115. if not (os.path.isdir(indir) and os.path.isdir(outroot)):
  116. return
  117. ns_parts = namespace.split('.')
  118. target = outroot
  119. for part in ns_parts:
  120. target = os.path.join(target, part)
  121. if part == ns_parts[-1]: # it's the last part
  122. os.rename(indir, target)
  123. else:
  124. os.mkdir(target)
  125. def patchSkeleton(dir, framework_dir, options):
  126. absPath = normalizePath(framework_dir)
  127. if absPath[-1] == "/":
  128. absPath = absPath[:-1]
  129. if sys.platform == 'cygwin':
  130. if re.match( r'^\.{1,2}\/', dir ):
  131. relPath = Path.rel_from_to(normalizePath(dir), framework_dir)
  132. elif re.match( r'^/cygdrive\b', dir):
  133. relPath = Path.rel_from_to(dir, framework_dir)
  134. else:
  135. relPath = Path.rel_from_to(normalizePath(dir), normalizePath(framework_dir))
  136. else:
  137. relPath = Path.rel_from_to(normalizePath(dir), normalizePath(framework_dir))
  138. relPath = re.sub(r'\\', "/", relPath)
  139. if relPath[-1] == "/":
  140. relPath = relPath[:-1]
  141. if not os.path.isdir(os.path.join(dir, relPath)):
  142. console.error("Relative path to qooxdoo directory is not correct: '%s'" % relPath)
  143. sys.exit(1)
  144. if options.type == "contribution":
  145. relPath = os.path.join(os.pardir, os.pardir, "qooxdoo", QOOXDOO_VERSION)
  146. relPath = re.sub(r'\\', "/", relPath)
  147. for root, dirs, files in os.walk(dir):
  148. for file in files:
  149. split = file.split(".")
  150. if len(split) >= 3 and split[1] == "tmpl":
  151. outFile = os.path.join(root, split[0] + "." + ".".join(split[2:]))
  152. inFile = os.path.join(root, file)
  153. console.log("Patching file '%s'" % outFile)
  154. config = Template(open(inFile).read())
  155. out = open(outFile, "w")
  156. out.write(
  157. config.substitute({
  158. "Name": options.name,
  159. "Namespace": options.namespace,
  160. "NamespacePath" : (options.namespace).replace('.', '/'),
  161. "REL_QOOXDOO_PATH": relPath,
  162. "ABS_QOOXDOO_PATH": absPath,
  163. "QOOXDOO_VERSION": QOOXDOO_VERSION,
  164. "Cache" : options.cache,
  165. }).encode('utf-8')
  166. )
  167. out.close()
  168. os.remove(inFile)
  169. for root, dirs, files in os.walk(dir):
  170. for file in [file for file in files if file.endswith(".py")]:
  171. os.chmod(os.path.join(root, file), (stat.S_IRWXU
  172. |stat.S_IRGRP |stat.S_IXGRP
  173. |stat.S_IROTH |stat.S_IXOTH)) # 0755
  174. def handleRemoveReadonly(func, path, exc):
  175. # For Windows the 'readonly' must not be set for resources to be removed
  176. excvalue = exc[1]
  177. if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
  178. os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
  179. func(path)
  180. else:
  181. raise
  182. def normalizePath(path):
  183. # Fix Windows annoyance to randomly return drive letters uppercase or lowercase.
  184. # Under Cygwin the user could also supply a lowercase drive letter. For those
  185. # two systems, the drive letter is always converted to uppercase, the remaining
  186. # path to lowercase
  187. if not sys.platform == 'win32' and not sys.platform == 'cygwin':
  188. return path
  189. path = re.sub(r'\\+', "/", path)
  190. if sys.platform == 'cygwin':
  191. search = re.match( r'^/cygdrive/([a-zA-Z])(/.*)$', path)
  192. if search:
  193. return search.group(1).upper() + ":" + search.group(2).lower()
  194. search = re.match( r'^([a-zA-Z])(:.*)$', path )
  195. if search:
  196. return search.group(1).upper() + search.group(2).lower()
  197. return path
  198. def checkNamespace(options):
  199. # check availability and spelling
  200. if not options.namespace:
  201. if R_ILLEGAL_NS_CHAR.search(options.name):
  202. convertedName = R_ILLEGAL_NS_CHAR.sub("_", options.name)
  203. console.log("WARNING: Converted illegal characters in name (from %s to %s)" % (options.name, convertedName))
  204. options.name = convertedName
  205. options.namespace = convertedName.lower()
  206. else:
  207. options.namespace = options.name.lower()
  208. else:
  209. options.namespace = options.namespace.decode('utf-8')
  210. if R_ILLEGAL_NS_CHAR.search(options.namespace):
  211. convertedNamespace = R_ILLEGAL_NS_CHAR.sub("_", options.namespace)
  212. console.log("WARNING: Converted illegal characters in namespace (from %s to %s)" % (options.namespace, convertedNamespace))
  213. options.namespace = convertedNamespace
  214. # check well-formed identifier
  215. if not re.match(lang.IDENTIFIER_REGEXP, options.namespace):
  216. console.error("Name space must be a legal JS identifier, but is not: '%s'" % options.namespace)
  217. sys.exit(1)
  218. # check reserved words
  219. if options.namespace in (lang.GLOBALS + lang.RESERVED.keys()):
  220. console.error("JS reserved word '%s' is not allowed as name space" % options.namespace)
  221. sys.exit(1)
  222. def main():
  223. parser = optparse.OptionParser()
  224. parser.set_usage('''\
  225. %prog --name APPLICATIONNAME [--out DIRECTORY]
  226. [--namespace NAMESPACE] [--type TYPE]
  227. [-logfile LOGFILE] [--skeleton-path PATH]
  228. Script to create a new qooxdoo application.
  229. Example: For creating a regular GUI application \'myapp\' you could execute:
  230. %prog --name myapp''')
  231. parser.add_option(
  232. "-n", "--name", dest="name", metavar="APPLICATIONNAME",
  233. help="Name of the application. An application folder with identical name will be created. (Required)"
  234. )
  235. parser.add_option(
  236. "-o", "--out", dest="out", metavar="DIRECTORY", default=".",
  237. help="Output directory for the application folder. (Default: %default)"
  238. )
  239. parser.add_option(
  240. "-s", "--namespace", dest="namespace", metavar="NAMESPACE", default=None,
  241. help="Applications's top-level namespace. (Default: APPLICATIONNAME)"
  242. )
  243. parser.add_option(
  244. "-t", "--type", dest="type", metavar="TYPE", default="gui",
  245. help="Type of the application to create, one of: "+str(map(str, sorted(APP_INFOS.keys())))+"." +
  246. str(", ".join(["'%s' %s" % (x, y) for x,y in sorted(APP_INFOS.items())])) +
  247. ". (Default: %default)"
  248. )
  249. parser.add_option(
  250. "-l", "--logfile", dest="logfile", metavar="LOGFILE",
  251. default=None, type="string", help="Log file"
  252. )
  253. parser.add_option(
  254. "-p", "--skeleton-path", dest="skeleton_path", metavar="PATH", default=SKELETON_DIR,
  255. help="(Advanced) Path where the script looks for skeletons. " +
  256. "The directory must contain sub directories named by " +
  257. "the application types. (Default: %default)"
  258. )
  259. parser.add_option(
  260. "--cache", dest="cache", metavar="PATH", default="${TMPDIR}/qx${QOOXDOO_VERSION}/cache",
  261. help="Path to the cache directory; will be entered into config.json's CACHE macro (Default: %default)"
  262. )
  263. (options, args) = parser.parse_args(sys.argv[1:])
  264. if not options.name:
  265. parser.print_help()
  266. sys.exit(1)
  267. else:
  268. options.name = options.name.decode('utf-8')
  269. # Initialize console
  270. global console
  271. console = Log(options.logfile, "info")
  272. checkNamespace(options)
  273. getQxVersion()
  274. createApplication(options)
  275. console.log("DONE")
  276. if __name__ == '__main__':
  277. try:
  278. main()
  279. except KeyboardInterrupt:
  280. print
  281. print "Keyboard interrupt!"
  282. sys.exit(1)