1 # Copyright 2010 The Closure Library Authors. All Rights Reserved.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 """Utility to use the Closure Compiler CLI from Python."""
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]+)')
30 class JsCompilerError(Exception):
31 """Raised if there's an error in calling the compiler."""
35 def _GetJavaVersionString():
36 """Get the version string from the Java VM."""
37 return subprocess
.check_output(['java', '-version'], stderr
=subprocess
.STDOUT
)
40 def _ParseJavaVersion(version_string
):
41 """Returns a 2-tuple for the current version of Java installed.
44 version_string: String of the Java version (e.g. '1.7.2-ea').
47 The major and minor versions, as a 2-tuple (e.g. (1, 7)).
49 match
= _VERSION_REGEX
.search(version_string
)
51 version
= tuple(int(x
, 10) for x
in match
.groups())
52 assert len(version
) == 2
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.
62 devnull
= open(os
.devnull
, 'wb')
63 return subprocess
.call(
64 ['java', '-d32', '-version'], stdout
=devnull
, stderr
=devnull
) == 0
72 def _GetJsCompilerArgs(compiler_jar_path
, java_version
, source_paths
,
73 jvm_flags
, compiler_flags
):
74 """Assembles arguments for call to JsCompiler."""
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')
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
85 # Attempt 32-bit mode if available (Java 7 on Mac OS X does not support 32-bit
87 if _JavaSupports32BitMode():
90 # Prefer the "client" VM.
93 # Add JVM flags, if any
97 # Add the application JAR.
98 args
+= ['-jar', compiler_jar_path
]
100 for path
in source_paths
:
101 args
+= ['--js', path
]
103 # Add compiler flags, if any.
105 args
+= compiler_flags
110 def Compile(compiler_jar_path
, source_paths
,
112 compiler_flags
=None):
113 """Prepares command-line call to Closure Compiler.
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.
122 The compiled source, as a string, or None if compilation failed.
125 java_version
= _ParseJavaVersion(_GetJavaVersionString())
127 args
= _GetJsCompilerArgs(
128 compiler_jar_path
, java_version
, source_paths
, jvm_flags
, compiler_flags
)
130 logging
.info('Compiling with the following command: %s', ' '.join(args
))
133 return subprocess
.check_output(args
)
134 except subprocess
.CalledProcessError
:
135 raise JsCompilerError('JavaScript compilation failed.')