adm-ntuh-net/ntuh/dojango/management/commands/dojobuild.py
2024-12-12 10:19:16 +08:00

319 lines
16 KiB
Python
Executable file

from optparse import make_option
import os
import re
import shutil
import subprocess # since python 2.4
import sys
from dojango.conf import settings
try:
from django.core.management.base import BaseCommand, CommandError
except ImportError:
# Fake BaseCommand out so imports on django 0.96 don't fail.
BaseCommand = object
class CommandError(Exception):
pass
class Command(BaseCommand):
'''This command is used to create your own dojo build. To start a build, you just
have to type:
./manage.py dojobuild
in your django project path. With this call, the default build profile "dojango" is used
and dojango.profile.js will act as its dojo build configuration. You can also add the
option --build_version=dev1.1.1 (for example) to mark the build with it.
If you want to call a specific build profile from DOJO_BUILD_PROFILES, you just have to
append the profile name to this commandline call:
./manage.py dojobuild profilename
'''
option_list = BaseCommand.option_list + (
make_option('--build_version', dest='build_version',
help='Set the version of the build release (e.g. dojango_1.1.1).'),
make_option('--minify', dest='minify', action="store_true", default=False,
help='Does a dojo mini build (mainly removing unneeded files (tests/templates/...)'),
make_option('--minify_extreme', dest='minify_extreme', action="store_true", default=False,
help='Does a dojo extreme-mini build (keeps only what is defined in build profile and all media files)'),
make_option('--prepare_zipserve', dest='prepare_zipserve', action="store_true", default=False,
help='Zips everything you have built, so it can be deployed to Google AppEngine'),
)
help = "Builds a dojo release."
args = '[dojo build profile name]'
dojo_base_dir = None
dojo_release_dir = None
skip_files = None
def handle(self, *args, **options):
if len(args)==0:
# with no param, we use the default profile, that is defined in the settings
profile_name = settings.DOJO_BUILD_PROFILE
else:
profile_name = args[0]
profile = self._get_profile(profile_name)
used_src_version = profile['used_src_version'] % {'DOJO_BUILD_VERSION': settings.DOJO_BUILD_VERSION} # no dependencies to project's settings.py file!
# used by minify_extreme!
self.skip_files = profile.get("minify_extreme_skip_files", ())
self.dojo_base_dir = "%(dojo_root)s/%(version)s" % \
{'dojo_root':settings.BASE_DOJO_ROOT,
'version':used_src_version}
# does the defined dojo-directory exist?
util_base_dir = "%(dojo_base_dir)s/util" % {'dojo_base_dir':self.dojo_base_dir}
if not os.path.exists(util_base_dir):
raise CommandError('Put the the dojo source files (version \'%(version)s\') in the folder \'%(folder)s/%(version)s\' or set a different version in settings.DOJANGO_DOJO_BUILD_VERSION' % \
{'version':used_src_version,
'folder':settings.BASE_DOJO_ROOT})
# check, if java is installed
try:
# ignoring output of the java call
subprocess.call(settings.DOJO_BUILD_JAVA_EXEC, stdout=subprocess.PIPE) # will work with python >= 2.4
except:
raise CommandError('Please install java. You need it for building dojo.')
buildscript_dir = os.path.abspath('%s/buildscripts' % util_base_dir)
if settings.DOJO_BUILD_USED_VERSION < '1.2.0':
executable = '%(java_exec)s -jar ../shrinksafe/custom_rhino.jar build.js' % \
{'java_exec':settings.DOJO_BUILD_JAVA_EXEC}
else:
# use the new build command line call!
if(os.path.sep == "\\"):
executable = 'build.bat'
else:
executable = './build.sh'
# force executable rights!
os.chmod(os.path.join(buildscript_dir, 'build.sh'), 0755)
# use the passed version for building
version = options.get('build_version', None)
if not version:
# if no option --build_version was passed, we use the default build version
version = profile['build_version'] % {'DOJO_BUILD_VERSION': settings.DOJO_BUILD_VERSION} # no dependencies to project's settings.py file!
# we add the version to our destination base path
self.dojo_release_dir = '%(base_path)s/%(version)s' % {
'base_path':profile['base_root'] % {'BASE_MEDIA_ROOT':settings.BASE_MEDIA_ROOT},
'version':version} # we don't want to have a dependancy to the project's settings file!
release_dir = os.path.abspath(os.path.join(self.dojo_release_dir, "../"))
# the build command handling is so different between the versions!
# sometimes we need to add /, sometimes not :-(
if settings.DOJO_BUILD_USED_VERSION < '1.2.0':
release_dir = release_dir + os.path.sep
# setting up the build command
build_addons = ""
if settings.DOJO_BUILD_USED_VERSION >= '1.2.0':
# since version 1.2.0 there is an additional commandline option that does the mini build (solved within js!)
build_addons = "mini=true"
exe_command = 'cd %(buildscript_dir)s && %(executable)s version=%(version)s releaseName="%(version)s" releaseDir="%(release_dir)s" %(options)s %(build_addons)s' % \
{'buildscript_dir':buildscript_dir,
'executable':executable,
'version':version,
'release_dir':release_dir,
'options':profile['options'] % {'BASE_MEDIA_ROOT':settings.BASE_MEDIA_ROOT},
'build_addons':build_addons}
# print exe_command
minify = options['minify']
minify_extreme = options['minify_extreme']
prepare_zipserve = options['prepare_zipserve']
if settings.DOJO_BUILD_USED_VERSION < '1.2.0' and (minify or minify_extreme):
self._dojo_mini_before_build()
if sys.platform == 'win32': # fixing issue #39, if dojango is installed on a different drive
exe_command = os.path.splitdrive(buildscript_dir)[0] + ' && ' + exe_command
# do the build
os.system(exe_command)
if settings.DOJO_BUILD_USED_VERSION < '1.2.0':
if minify or minify_extreme:
self._dojo_mini_after_build()
if minify_extreme:
self._dojo_mini_extreme()
if prepare_zipserve:
self._dojo_prepare_zipserve()
def _get_profile(self, name):
default_profile_settings = settings.DOJO_BUILD_PROFILES_DEFAULT
try:
profile = settings.DOJO_BUILD_PROFILES[name]
# mixing in the default settings for the build profiles!
default_profile_settings.update(profile)
return default_profile_settings
except KeyError:
raise CommandError('The profile \'%s\' does not exist in DOJO_BUILD_PROFILES' % name)
def _dojo_mini_before_build(self):
# FIXME: refs #6616 - could be able to set a global copyright file and null out build_release.txt
shutil.move("%s/util/buildscripts/copyright.txt" % self.dojo_base_dir, "%s/util/buildscripts/_copyright.txt" % self.dojo_base_dir)
if not os.path.exists("%s/util/buildscripts/copyright_mini.txt" % self.dojo_base_dir):
f = open("%s/util/buildscripts/copyright.txt" % self.dojo_base_dir, 'w')
f.write('''/*
Copyright (c) 2004-2008, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/''')
f.close()
else:
shutil.copyfile("%s/util/buildscripts/copyright_mini.txt" % self.dojo_base_dir, "%s/util/buildscripts/copyright.txt" % self.dojo_base_dir)
shutil.move("%s/util/buildscripts/build_notice.txt" % self.dojo_base_dir, "%s/util/buildscripts/_build_notice.txt" % self.dojo_base_dir)
# create an empty build-notice-file
f = open("%s/util/buildscripts/build_notice.txt" % self.dojo_base_dir, 'w')
f.close()
def _dojo_mini_after_build(self):
try:
'''Copied from the build_mini.sh shell script (thank you Pete Higgins :-))'''
if not os.path.exists(self.dojo_release_dir):
raise CommandError('The dojo build failed! Check messages above!')
else:
# remove dojox tests and demos - they all follow this convetion
self._remove_files('%s/dojox' % self.dojo_release_dir, ('^tests$', '^demos$'))
# removed dijit tests
dijit_tests = ("dijit/tests", "dijit/demos", "dijit/bench",
"dojo/tests", "util",
"dijit/themes/themeTesterImages")
self._remove_folders(dijit_tests)
# noir isn't worth including yet
noir_theme_path = ("%s/dijit/themes/noir" % self.dojo_release_dir,)
self._remove_folders(noir_theme_path)
# so the themes are there, lets assume that, piggyback on noir: FIXME later
self._remove_files('%s/dijit/themes' % self.dojo_release_dir, ('^.*\.html$',))
self._remove_files(self.dojo_release_dir, ('^.*\.uncompressed\.js$',))
# WARNING: templates have been inlined into the .js -- if you are using dynamic templates,
# or other build trickery, these lines might not work!
self._remove_files("dijit/templates", ("^\.html$",))
self._remove_files("dijit/form/templates", ("^\.html$",))
self._remove_files("dijit/layout/templates", ("^\.html$",))
# .. assume you didn't, and clean up all the README's (leaving LICENSE, mind you)
self._remove_files('%s/dojo/dojox' % self.dojo_release_dir, ('^README$',))
dojo_folders = ("dojo/_base",)
self._remove_folders(dojo_folders)
os.remove("%s/dojo/_base.js" % self.dojo_release_dir)
os.remove("%s/dojo/build.txt" % self.dojo_release_dir)
os.remove("%s/dojo/tests.js" % self.dojo_release_dir)
except Exception, e:
print e
# cleanup from above, refs #6616
shutil.move("%s/util/buildscripts/_copyright.txt" % self.dojo_base_dir, "%s/util/buildscripts/copyright.txt" % self.dojo_base_dir)
shutil.move("%s/util/buildscripts/_build_notice.txt" % self.dojo_base_dir, "%s/util/buildscripts/build_notice.txt" % self.dojo_base_dir)
def _remove_folders(self, folders):
for folder in folders:
if os.path.exists("%s/%s" % (self.dojo_release_dir, folder)):
shutil.rmtree("%s/%s" % (self.dojo_release_dir, folder))
def _remove_files(self, base_folder, regexp_list):
for root, dirs, files in os.walk(base_folder):
for file in files:
# remove all html-files
for regexp in regexp_list:
my_re = re.compile(regexp)
if my_re.match(file):
os.remove(os.path.join(root, file))
for dir in dirs:
for regexp in regexp_list:
my_re = re.compile(regexp)
if my_re.match(dir):
shutil.rmtree(os.path.join(root, dir))
SKIP_FILES = (
'(.*\.png)',
'(.*\.gif)',
'(.*\.jpg)',
'(.*\.svg)',
'(.*\.swf)',
'(.*\.fla)',
'(.*\.mov)',
'(.*\.smd)',
'(dojo/_firebug/firebug\..*)',
'(dojo/dojo\.(xd\.)?js)',
'(dojo/nls/.*)',
'(dojo/resources/dojo\.css)',
'(dojo/resources/blank\.html)',
'(dojo/resources/iframe_history\.html)',
'(dijit/themes/tundra/tundra\.css)',
'(dijit/themes/soria/soria\.css)',
'(dijit/themes/nihilo/nihilo\.css)',
'(dojox/dtl/contrib/.*)',
'(dojox/dtl/ext-dojo/.*)',
'(dojox/dtl/filter/.*)',
'(dojox/dtl/render/.*)',
'(dojox/dtl/tag/.*)',
'(dojox/dtl/utils/.*)',
'(dojox/io/proxy/xip_.*\.html)',
)
def _dojo_mini_extreme(self):
"""
This method removes all js files and just leaves all layer dojo files and static files (like "png", "gif", "svg", "swf", ...)
"""
# prepare the regexp of files not to be removed!
# mixin the profile specific skip files
skip_files = self.SKIP_FILES + self.skip_files
my_re = re.compile('^(.*/)?(%s)$' % "|".join(skip_files))
try:
'''Copied from the build_mini.sh shell script'''
if not os.path.exists(self.dojo_release_dir):
raise CommandError('The dojo build failed! Check messages above!')
else:
for root, dirs, files in os.walk(self.dojo_release_dir):
for file in files:
# remove all html-files
my_file = os.path.abspath(os.path.join(root, file))
if not my_re.match(my_file):
os.remove(my_file)
# now remove all empty directories
for root, dirs, files in os.walk(self.dojo_release_dir):
for dir in dirs:
try:
# just empty directories will be removed!
os.removedirs(os.path.join(root, dir))
except OSError:
pass
except Exception, e:
print e
DOJO_ZIP_SPECIAL = {'dojox': ['form', 'widget', 'grid']} # these modules will be zipped separately
def _dojo_prepare_zipserve(self):
"""
Creates zip packages for each dojo module within the current release folder.
It splits the module dojox into several modules, so it fits the 1000 files limit of
Google AppEngine.
"""
for folder in os.listdir(self.dojo_release_dir):
module_dir = '%s/%s' % (self.dojo_release_dir, folder)
if os.path.isdir(module_dir):
if folder in self.DOJO_ZIP_SPECIAL.keys():
for special_module in self.DOJO_ZIP_SPECIAL[folder]:
special_module_dir = os.path.join(module_dir, special_module)
create_zip(special_module_dir,
'%(base_module)s/%(special_module)s' % {
'base_module': folder,
'special_module': special_module
},
'%(module_dir)s.%(special_module)s.zip' % {
'module_dir': module_dir,
'special_module': special_module
}
)
# remove the whole special module
shutil.rmtree(special_module_dir)
# now add the
create_zip(module_dir, folder, module_dir + ".zip")
shutil.rmtree(module_dir)
def zipfolder(path, relname, archive):
paths = os.listdir(path)
for p in paths:
p1 = os.path.join(path, p)
p2 = os.path.join(relname, p)
if os.path.isdir(p1):
zipfolder(p1, p2, archive)
else:
archive.write(p1, p2)
def create_zip(path, relname, archname):
import zipfile
archive = zipfile.ZipFile(archname, "w", zipfile.ZIP_DEFLATED)
if os.path.isdir(path):
zipfolder(path, relname, archive)
else:
archive.write(path, relname)
archive.close()