Hi,
I'd like to share another possible way of building qooxdoo and dependent
libraries. I guess at least Petr might be interested :)
The thing is that classical qooxdoo generator doesn't fit our
application toolchain/process well. I.e. we have quite different urls
and resources structure/path resolving. Another problem is that we can't
gather all qooxdoo classes at build time, hence common build and
optimization through the whole application is not possible.
The solution I came up with is building every library to its own file:
qx.js, dbgui.js, some_plugin.js, and gather those files myself (without
qxloader). And in this case I need to extract and save all related data:
resources, translation, locales. So I need all the functionality qooxdoo
generator has but without the last step of generating JS files.
I created the python package called pyqooxdoo, put all qooxdoo trunk
there (as resources) and added couple of scripts to automate build
process. I'm attaching main of them to illustrate words above.
Thanks,
Oleksiy
import sys, os, re, tempfile, pkg_resources, subprocess, shutil
import yaml, simplejson
from StringIO import StringIO
from copy import deepcopy
from patch import Generator
TEMPLATE = {
"export": ['build', 'source'],
"jobs": {
"build": {
"cache": {'compile': ''},
"library": [{}],
"exclude": ["qx.legacy.*",
"qx.ui.treevirtual.*",
"qx.theme.Classic",
"qx.theme.classic.*",
"qx.dev.*",
"qx.test.*",
"qx.xml.*"
],
"variants": {
"qx.debug": ["off"],
"qx.aspects": ["off"]
}
}
}
}
def get_generator(name, manifest, cache):
assert isinstance(name, str)
assert isinstance(manifest, str) or isinstance(manifest, dict)
assert isinstance(cache, str)
config = deepcopy(TEMPLATE)
job = config['jobs']['build']
if name != 'qx':
del job['exclude']
library = job['library']
if isinstance(manifest, str):
library[0]['manifest'] = manifest
else:
tmpdir = tempfile.mkdtemp(prefix='qooxdoo-%s-build' % name)
filename = os.path.join(tmpdir, 'Manifest.json')
f = open(filename, 'w')
simplejson.dump(manifest, f, indent=2)
f.close()
library[0]['manifest'] = filename
job['cache']['compile'] = cache
return Generator(config)
def build(name, manifest, cache, source=False, uri=None):
assert source and uri or not source and uri is None
generator = get_generator(name, manifest, cache)
resources = generator.get_resources()
translations = generator.get_translations()
locales = generator.get_locales()
content = generator.compile_source(uri) if source \
else generator.compile_dist()
return resources, translations, locales, content
def get_qx_prop():
manifest = pkg_resources.resource_filename('pyqooxdoo',
'/data/qooxdoo/framework/Manifest.json')
import pyqooxdoo
cache = '/tmp/qx-%s-cache' % pyqooxdoo.VERSION
return manifest, cache
def build_qx(source=False, uri=None):
manifest, cache = get_qx_prop()
return build('qx', manifest, cache, source, uri)
def copy_qx_resources(destination):
manifest, cache = get_qx_prop()
get_generator('qx', manifest, cache).copy_resources(destination)
import sys, pkg_resources, re, os, simplejson
qooxdoo_generator_path = pkg_resources.resource_filename('pyqooxdoo',
'/data/qooxdoo/tool/pylib')
sys.path.append(qooxdoo_generator_path)
from generator.Generator import Generator as QxGenerator, _ResourceHandler
from generator.config.Config import Config
from generator.code.DependencyLoader import DependencyLoader
from generator.code.TreeLoader import TreeLoader
from generator.code.TreeCompiler import TreeCompiler
from generator.action.ImageInfo import ImageInfo, ImgInfoFmt
from generator.action.Locale import Locale
from generator.runtime.Log import Log
from misc import filetool, textutil, idlist, Path
def _progress_patch(self, pos, length):
pass
Log.progress = _progress_patch
class Generator(QxGenerator):
__variants = {'qx.debug': 'off', 'qx.aspects': 'off'}
__optimize = ["basecalls", "variables", "privates", "strings"]
__format = True
__locales = ["C"]
def __init__(self, config):
job = 'build'
console = Log(None, 'error')
config = Config(console, config)
config.resolveIncludes()
config.resolveMacros([job])
config.resolveLibs([job])
QxGenerator.__init__(self, config, job, console)
self._preprocess()
def _preprocess(self):
self._namespaces, self._classes, self._docs, self._translations, \
self._libs = self.scanLibrary(self._config.get("library"))
self._treeLoader = TreeLoader(self._classes, self._cache,
self._console)
self._depLoader = DependencyLoader(self._classes, self._cache,
self._console,
self._treeLoader, {}, {})
self._treeCompiler = TreeCompiler(self._classes, self._cache,
self._console, self._treeLoader)
self._locale = Locale(self._classes, self._translations,
self._cache, self._console,
self._treeLoader)
self._resourceHandler= _ResourceHandler(self)
self.runUpdateTranslation()
smartInclude, explicitInclude = \
self.getIncludes(self._config.get("include", []))
smartExclude, explicitExclude = \
self.getExcludes(self._config.get("exclude", []))
self._classList = self._depLoader.getClassList(smartInclude,
smartExclude,
explicitInclude,
explicitExclude,
self.__variants)
def compile_dist(self):
return self._treeCompiler.compileClasses(self._classList,
self.__variants,
self.__optimize,
self.__format)
def compile_source(self, uri):
if not uri.endswith('/'):
uri += '/'
template = '<script language="JavaScript" src="%s%%s.js"></script>'\
% uri
uris = [template % cls.replace('.', '/') for cls in self._classList]
return "document.write('%s');" % "\\\n".join(uris)
def get_resources(self):
"""Pre-calculate image information (e.g. sizes)"""
data = {}
resdata = data
result = ""
imgpatt = re.compile(r'\.(png|jpeg|jpg|gif)$', re.I)
skippatt = re.compile(r'\.(meta|py)$', re.I)
self._imageInfo = ImageInfo(self._console, self._cache)
# some helper functions
def replaceWithNamespace(imguri, liburi, libns):
pre,libsfx,imgsfx = Path.getCommonPrefix(liburi, imguri)
if imgsfx[0] == os.sep: imgsfx = imgsfx[1:] # strip leading '/'
imgshorturi = os.path.join("${%s}" % libns, imgsfx)
return imgshorturi
def extractAssetPart(libresuri, imguri):
pre,libsfx,imgsfx = Path.getCommonPrefix(libresuri, imguri) # split libresuri from imguri
if imgsfx[0] == os.sep: imgsfx = imgsfx[1:] # strip leading '/'
return imgsfx # use the bare img suffix as its asset Id
def normalizeImgUri(uriFromMetafile, trueCombinedUri, combinedUriFromMetafile):
# normalize paths (esp. "./x" -> "x")
(uriFromMetafile, trueCombinedUri, combinedUriFromMetafile) = map(os.path.normpath,(uriFromMetafile, trueCombinedUri, combinedUriFromMetafile))
# get the "wrong" prefix (in mappedUriPrefix)
trueUriPrefix, mappedUriPrefix, sfx = Path.getCommonSuffix(trueCombinedUri, combinedUriFromMetafile)
# ...and strip it from contained image uri, to get a correct suffix (in uriSuffix)
pre, mappedUriSuffix, uriSuffix = Path.getCommonPrefix(mappedUriPrefix, uriFromMetafile)
# ...then compose the correct prefix with the correct suffix
normalUri = os.path.normpath(os.path.join(trueUriPrefix, uriSuffix))
return normalUri
def processCombinedImg(data, meta_fname, cimguri, cimgshorturi, cimgfmt):
assert cimgfmt.lib, cimgfmt.type
# read meta file
mfile = open(meta_fname)
imgDict = simplejson.loads(mfile.read())
mfile.close()
for mimg, mimgs in imgDict.iteritems():
# sort of like this: mimg : [width, height, type, combinedUri, off-x, off-y]
mimgspec = ImgInfoFmt(mimgs)
# have to normalize the uri's from the meta file
# cimguri is relevant, like: "../../framework/source/resource/qx/decoration/Modern/panel-combined.png"
# mimg is an uri from when the meta file was generated, like: "./source/resource/qx/decoration/Modern/..."
mimguri = normalizeImgUri(mimg, cimguri, mimgspec.mappedId)
## replace lib uri with lib namespace in mimguri
##mimgshorturi = replaceWithNamespace(mimguri, libresuri, cimgfmt.lib)
mimgshorturi = extractAssetPart(libresuri, mimguri)
mimgshorturi = Path.posifyPath(mimgshorturi)
mimgspec.mappedId = cimgshorturi # correct the mapped uri of the combined image
mimgspec.lib = cimgfmt.lib
mimgspec.mtype = cimgfmt.type
mimgspec.mlib = cimgfmt.lib
data[mimgshorturi] = mimgspec.flatten() # this information takes precedence over existing
# main
for lib in self._libs.values():
libresuri = os.path.join(lib['uri'],lib['resource'])
resourceList = self._resourceHandler.findAllResources([lib],
self._resourceHandler.filterResourcesByClasslist(self._classList))
for resource in resourceList:
##assetId = replaceWithNamespace(imguri, libresuri, lib['namespace'])
assetId = extractAssetPart(libresuri, resource[1])
assetId = Path.posifyPath(assetId)
if imgpatt.search(resource[0]): # handle images
imgpath= resource[0]
imguri = resource[1]
imageInfo = self._imageInfo.getImageInfo(imgpath)
# use an ImgInfoFmt object, to abstract from flat format
imgfmt = ImgInfoFmt()
imgfmt.lib = lib['namespace']
if not 'type' in imageInfo:
raise RuntimeError, "Unable to get image info from file: %s" % imgpath
imgfmt.type = imageInfo['type']
# check for a combined image and process the contained images
meta_fname = os.path.splitext(imgpath)[0]+'.meta'
if os.path.exists(meta_fname): # add included imgs
processCombinedImg(data, meta_fname, imguri, assetId, imgfmt)
# add this image directly
# imageInfo = {width, height, filetype}
if not 'width' in imageInfo or not 'height' in imageInfo:
raise RuntimeError, "Unable to get image info from file: %s" % imgpath
imgfmt.width, imgfmt.height, imgfmt.type = (
imageInfo['width'], imageInfo['height'], imageInfo['type'])
# check if img is already registered as part of a combined image
if assetId in data:
x = ImgInfoFmt()
x.fromFlat(data[assetId])
if x.mappedId:
continue # don't overwrite the combined entry
data[assetId] = imgfmt.flatten()
elif skippatt.search(resource[0]):
continue
else: # handle other resources
resdata[assetId] = lib['namespace']
return resdata
def get_translations(self):
return self._locale.generatePackageData(self._classes,
self.__variants,
self.__locales)
def get_locales(self):
return self._locale.getLocalizationData(self.__locales)
def copy_resources(self, destination):
resList = self._resourceHandler.findAllResources(self._libs.values(),
self._resourceHandler.filterResourcesByClasslist(self._classList))
lib = self._libs.values()[0]
libpath = os.path.join(lib['path'], lib['resource'])
libpath = os.path.normpath(libpath)
for res, _ in resList:
dst = os.path.dirname(res.replace(libpath, destination))
self._copyResources(res, dst)
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
qooxdoo-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel