jscompiler.py (4102B)
1 # Copyright 2010 The Closure Library Authors. All Rights Reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS-IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 """Utility to use the Closure Compiler CLI from Python.""" 16 17 18 import logging 19 import os 20 import re 21 import subprocess 22 23 24 # Pulls just the major and minor version numbers from the first line of 25 # 'java -version'. Versions are in the format of [0-9]+\.[0-9]+\..* See: 26 # http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html 27 _VERSION_REGEX = re.compile(r'"([0-9]+)\.([0-9]+)') 28 29 30 class JsCompilerError(Exception): 31 """Raised if there's an error in calling the compiler.""" 32 pass 33 34 35 def _GetJavaVersionString(): 36 """Get the version string from the Java VM.""" 37 return subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT) 38 39 40 def _ParseJavaVersion(version_string): 41 """Returns a 2-tuple for the current version of Java installed. 42 43 Args: 44 version_string: String of the Java version (e.g. '1.7.2-ea'). 45 46 Returns: 47 The major and minor versions, as a 2-tuple (e.g. (1, 7)). 48 """ 49 match = _VERSION_REGEX.search(version_string) 50 if match: 51 version = tuple(int(x, 10) for x in match.groups()) 52 assert len(version) == 2 53 return version 54 55 56 def _JavaSupports32BitMode(): 57 """Determines whether the JVM supports 32-bit mode on the platform.""" 58 # Suppresses process output to stderr and stdout from showing up in the 59 # console as we're only trying to determine 32-bit JVM support. 60 supported = False 61 try: 62 devnull = open(os.devnull, 'wb') 63 return subprocess.call( 64 ['java', '-d32', '-version'], stdout=devnull, stderr=devnull) == 0 65 except IOError: 66 pass 67 else: 68 devnull.close() 69 return supported 70 71 72 def _GetJsCompilerArgs(compiler_jar_path, java_version, source_paths, 73 jvm_flags, compiler_flags): 74 """Assembles arguments for call to JsCompiler.""" 75 76 if java_version < (1, 7): 77 raise JsCompilerError('Closure Compiler requires Java 1.7 or higher. ' 78 'Please visit http://www.java.com/getjava') 79 80 args = ['java'] 81 82 # Add JVM flags we believe will produce the best performance. See 83 # https://groups.google.com/forum/#!topic/closure-library-discuss/7w_O9-vzlj4 84 85 # Attempt 32-bit mode if available (Java 7 on Mac OS X does not support 32-bit 86 # mode, for example). 87 if _JavaSupports32BitMode(): 88 args += ['-d32'] 89 90 # Prefer the "client" VM. 91 args += ['-client'] 92 93 # Add JVM flags, if any 94 if jvm_flags: 95 args += jvm_flags 96 97 # Add the application JAR. 98 args += ['-jar', compiler_jar_path] 99 100 for path in source_paths: 101 args += ['--js', path] 102 103 # Add compiler flags, if any. 104 if compiler_flags: 105 args += compiler_flags 106 107 return args 108 109 110 def Compile(compiler_jar_path, source_paths, 111 jvm_flags=None, 112 compiler_flags=None): 113 """Prepares command-line call to Closure Compiler. 114 115 Args: 116 compiler_jar_path: Path to the Closure compiler .jar file. 117 source_paths: Source paths to build, in order. 118 jvm_flags: A list of additional flags to pass on to JVM. 119 compiler_flags: A list of additional flags to pass on to Closure Compiler. 120 121 Returns: 122 The compiled source, as a string, or None if compilation failed. 123 """ 124 125 java_version = _ParseJavaVersion(_GetJavaVersionString()) 126 127 args = _GetJsCompilerArgs( 128 compiler_jar_path, java_version, source_paths, jvm_flags, compiler_flags) 129 130 logging.info('Compiling with the following command: %s', ' '.join(args)) 131 132 try: 133 return subprocess.check_output(args) 134 except subprocess.CalledProcessError: 135 raise JsCompilerError('JavaScript compilation failed.')