123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- # 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 logging
- import os
- import re
- import subprocess
- import tempfile
- # Pulls just the major and minor version numbers from the first line of
- # 'java -version'. Versions are in the format of [0-9]+(\.[0-9]+)? See:
- # http://openjdk.java.net/jeps/223
- _VERSION_REGEX = re.compile(r'"([0-9]+)(?:\.([0-9]+))?')
- class JsCompilerError(Exception):
- """Raised if there's an error in calling the compiler."""
- pass
- def _GetJavaVersionString():
- """Get the version string from the Java VM."""
- return subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT)
- def _ParseJavaVersion(version_string):
- """Returns a 2-tuple for the current version of Java installed.
- Args:
- version_string: String of the Java version (e.g. '1.7.2-ea').
- Returns:
- The major and minor versions, as a 2-tuple (e.g. (1, 7)).
- """
- match = _VERSION_REGEX.search(version_string)
- if match:
- version = tuple(int(x or 0) for x in match.groups())
- assert len(version) == 2
- return version
- def _JavaSupports32BitMode():
- """Determines whether the JVM supports 32-bit mode on the platform."""
- # Suppresses process output to stderr and stdout from showing up in the
- # console as we're only trying to determine 32-bit JVM support.
- supported = False
- try:
- devnull = open(os.devnull, 'wb')
- return subprocess.call(
- ['java', '-d32', '-version'], stdout=devnull, stderr=devnull) == 0
- except IOError:
- pass
- else:
- devnull.close()
- return supported
- def _GetJsCompilerArgs(compiler_jar_path, java_version, jvm_flags):
- """Assembles arguments for call to JsCompiler."""
- if java_version < (1, 7):
- raise JsCompilerError('Closure Compiler requires Java 1.7 or higher. '
- 'Please visit http://www.java.com/getjava')
- args = ['java']
- # Add JVM flags we believe will produce the best performance. See
- # https://groups.google.com/forum/#!topic/closure-library-discuss/7w_O9-vzlj4
- # Attempt 32-bit mode if available (Java 7 on Mac OS X does not support 32-bit
- # mode, for example).
- if _JavaSupports32BitMode():
- args += ['-d32']
- # Prefer the "client" VM.
- args += ['-client']
- # Add JVM flags, if any
- if jvm_flags:
- args += jvm_flags
- # Add the application JAR.
- args += ['-jar', compiler_jar_path]
- return args
- def _GetFlagFile(source_paths, compiler_flags):
- """Writes given source paths and compiler flags to a --flagfile.
- The given source_paths will be written as '--js' flags and the compiler_flags
- are written as-is.
- Args:
- source_paths: List of string js source paths.
- compiler_flags: List of string compiler flags.
- Returns:
- The file to which the flags were written.
- """
- args = []
- for path in source_paths:
- args += ['--js', path]
- # Add compiler flags, if any.
- if compiler_flags:
- args += compiler_flags
- flags_file = tempfile.NamedTemporaryFile(delete=False)
- flags_file.write(' '.join(args))
- flags_file.close()
- return flags_file
- def Compile(compiler_jar_path,
- source_paths,
- jvm_flags=None,
- compiler_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.
- jvm_flags: A list of additional flags to pass on to JVM.
- compiler_flags: A list of additional flags to pass on to Closure Compiler.
- Returns:
- The compiled source, as a string, or None if compilation failed.
- """
- java_version = _ParseJavaVersion(str(_GetJavaVersionString()))
- args = _GetJsCompilerArgs(compiler_jar_path, java_version, jvm_flags)
- # Write source path arguments to flag file for avoiding "The filename or
- # extension is too long" error in big projects. See
- # https://github.com/google/closure-library/pull/678
- flags_file = _GetFlagFile(source_paths, compiler_flags)
- args += ['--flagfile', flags_file.name]
- logging.info('Compiling with the following command: %s', ' '.join(args))
- try:
- return subprocess.check_output(args)
- except subprocess.CalledProcessError:
- raise JsCompilerError('JavaScript compilation failed.')
- finally:
- os.remove(flags_file.name)
|