diff --git a/build/build.py b/build/build.py index d688a31e35..ebb07d358f 100755 --- a/build/build.py +++ b/build/build.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import sys +import os sys.path.append("../tools") import mergejs import optparse @@ -13,7 +14,9 @@ def build(config_file = None, output_file = None, options = None): except ImportError: print "No jsmin" try: - import closure + # tools/closure_library_jscompiler.py from: + # http://code.google.com/p/closure-library/source/browse/trunk/closure/bin/build/jscompiler.py + import closure_library_jscompiler as closureCompiler have_compressor.append("closure") except Exception, E: print "No closure (%s)" % E @@ -48,7 +51,11 @@ def build(config_file = None, output_file = None, options = None): outputFilename = output_file print "Merging libraries." - merged = mergejs.run(sourceDirectory, None, configFilename) + if use_compressor == "closure": + sourceFiles = mergejs.getNames(sourceDirectory, configFilename) + else: + merged = mergejs.run(sourceDirectory, None, configFilename) + print "Compressing using %s" % use_compressor if use_compressor == "jsmin": minimized = jsmin.jsmin(merged) @@ -68,12 +75,29 @@ def build(config_file = None, output_file = None, options = None): print "\nAbnormal termination due to compilation errors." sys.exit("ERROR: Closure Compilation using Web service failed!") else: - print '\nClosure Compilation using Web service has completed successfully.' + print "Closure Compilation using Web service has completed successfully." elif use_compressor == "closure": - minimized = closure.minimize(merged) + jscompilerJar = "../tools/closure-compiler.jar" + if not os.path.isfile(jscompilerJar): + print "\nNo closure-compiler.jar; read README.txt!" + sys.exit("ERROR: Closure Compiler \"%s\" does not exist! Read README.txt" % jscompilerJar) + minimized = closureCompiler.Compile( + jscompilerJar, + sourceFiles, [ + "--externs", "closure-compiler/Externs.js", + "--jscomp_warning", "checkVars", # To enable "undefinedVars" + "--jscomp_error", "checkRegExp", # Also necessary to enable "undefinedVars" + "--jscomp_error", "undefinedVars" + ] + ) + if minimized is None: + print "\nAbnormal termination due to compilation errors." + sys.exit("ERROR: Closure Compilation failed! See compilation errors.") + print "Closure Compilation has completed successfully." else: # fallback minimized = merged - print "Adding license file." + + print "\nAdding license file." minimized = file("license.txt").read() + minimized print "Writing to %s." % outputFilename @@ -92,4 +116,4 @@ if __name__ == '__main__': elif len(args) == 2: build(args[0], args[1], options=options) else: - print "Wrong number of arguments" + print "Wrong number of arguments" \ No newline at end of file diff --git a/tools/closure.py b/tools/closure.py deleted file mode 100644 index 1f788edb5d..0000000000 --- a/tools/closure.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys -import os -import tempfile - -path = "../tools/closure-compiler.jar" -if not os.path.exists(path): - raise Exception("No closure-compiler.jar at %s; read README.txt!" % path) - -def minimize(code): - ntf = tempfile.NamedTemporaryFile() - ntf.close() - open(ntf.name, "w").write(code) - - ntf2 = tempfile.NamedTemporaryFile() - ntf2.close() - - os.system(("java -jar %s --js %s --js_output_file %s" + - " --externs closure-compiler/Externs.js" + - " --jscomp_warning checkVars" + - " --jscomp_error checkRegExp" + - " --jscomp_error undefinedVars") % (path, ntf.name, ntf2.name)) - data = open(ntf2.name).read() - os.unlink(ntf.name) - os.unlink(ntf2.name) - return data diff --git a/tools/closure_library_jscompiler.py b/tools/closure_library_jscompiler.py new file mode 100644 index 0000000000..fd1882fdb3 --- /dev/null +++ b/tools/closure_library_jscompiler.py @@ -0,0 +1,71 @@ +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utility to use the Closure Compiler CLI from Python.""" + +import distutils.version +import logging +import re +import subprocess + + +# Pulls a version number from the first line of 'java -version' +# See http://java.sun.com/j2se/versioning_naming.html to learn more about the +# command's output format. +_VERSION_REGEX = re.compile('"([0-9][.0-9]*)') + + +def _GetJavaVersion(): + """Returns the string for the current version of Java installed.""" + proc = subprocess.Popen(['java', '-version'], stderr=subprocess.PIPE) + unused_stdoutdata, stderrdata = proc.communicate() + version_line = stderrdata.splitlines()[0] + return _VERSION_REGEX.search(version_line).group(1) + + +def Compile(compiler_jar_path, source_paths, flags=None): + """Prepares command-line call to Closure Compiler. + + Args: + compiler_jar_path: Path to the Closure compiler .jar file. + source_paths: Source paths to build, in order. + flags: A list of additional flags to pass on to Closure Compiler. + + Returns: + The compiled source, as a string, or None if compilation failed. + """ + + # User friendly version check. + if not (distutils.version.LooseVersion(_GetJavaVersion()) >= + distutils.version.LooseVersion('1.6')): + logging.error('Closure Compiler requires Java 1.6 or higher. ' + 'Please visit http://www.java.com/getjava') + return + + args = ['java', '-jar', compiler_jar_path] + for path in source_paths: + args += ['--js', path] + + if flags: + args += flags + + logging.info('Compiling with the following command: %s', ' '.join(args)) + + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + stdoutdata, unused_stderrdata = proc.communicate() + + if proc.returncode != 0: + return + + return stdoutdata diff --git a/tools/mergejs.py b/tools/mergejs.py index 6403072e0d..504f78b653 100755 --- a/tools/mergejs.py +++ b/tools/mergejs.py @@ -139,9 +139,14 @@ def undesired(filepath, excludes): exclude = True break return exclude + + +def getNames (sourceDirectory, configFile = None): + return run(sourceDirectory, None, configFile, True) -def run (sourceDirectory, outputFilename = None, configFile = None): +def run (sourceDirectory, outputFilename = None, configFile = None, + returnAsListOfNames = False): cfg = None if configFile: cfg = Config(configFile) @@ -219,6 +224,16 @@ def run (sourceDirectory, outputFilename = None, configFile = None): ## Output the files in the determined order result = [] + # Return as a list of filenames + if returnAsListOfNames: + for fp in order: + fName = os.path.normpath(os.path.join(sourceDirectory, fp)).replace("\\","/") + print "Append: ", fName + result.append(fName) + print "\nTotal files: %d " % len(result) + return result + + # Return as merged source code for fp in order: f = files[fp] print "Exporting: ", f.filepath