mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
19
dom/canvas/test/webgl-conf/checkout/closure-library/AUTHORS
Normal file
19
dom/canvas/test/webgl-conf/checkout/closure-library/AUTHORS
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# This is a list of contributors to the Closure Library.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name or Organization <email address>
|
||||
|
||||
Google Inc.
|
||||
Stellar Science Ltd.
|
||||
Mohamed Mansour <hello@mohamedmansour.com>
|
||||
Bjorn Tipling <bjorn.tipling@gmail.com>
|
||||
SameGoal LLC <help@samegoal.com>
|
||||
Guido Tapia <guido.tapia@gmail.com>
|
||||
Andrew Mattie <amattie@gmail.com>
|
||||
Ilia Mirkin <ibmirkin@gmail.com>
|
||||
Ivan Kozik <ivan.kozik@gmail.com>
|
||||
Rich Dougherty <rich@rd.gen.nz>
|
||||
Chad Killingsworth <chadkillingsworth@missouristate.edu>
|
||||
Dan Pupius <dan.pupius@gmail.com>
|
||||
Mike Dunn <dunn74@gmail.com>
|
||||
Kengo Toda <skypencil@gmail.com>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
Closure Library welcomes patches/pulls for features and bugfixes.
|
||||
|
||||
For contributors inside Google, follow the instructions given here:
|
||||
http://go/closure-contributors
|
||||
|
||||
For contributors external to Google, follow the instructions given here:
|
||||
|
||||
Notes on Contributions to Closure Library
|
||||
|
||||
Google Individual Contributor License
|
||||
|
||||
In all cases, contributors must sign a contributor license agreement,
|
||||
either for an individual or corporation, before a patch can be
|
||||
accepted. Please fill out the agreement for an individual or a
|
||||
corporation, as appropriate.
|
||||
|
||||
https://developers.google.com/open-source/cla/individual
|
||||
https://developers.google.com/open-source/cla/corporate
|
||||
|
||||
If you or your organization is not listed there already, you should
|
||||
add an entry to the AUTHORS file as part of your patch.
|
||||
|
||||
If you plan to add a significant component or large chunk of code, it
|
||||
is recommended to bring it up on the discussion list for a design
|
||||
discussion before writing code.
|
||||
|
||||
If appropriate, write a unit test that demonstrates your patch. Tests are the
|
||||
best way to ensure that future contributors do not break your code
|
||||
accidentally.
|
||||
|
||||
To change the Closure Library source, you must submit a pull request
|
||||
in GitHub. See the GitHub documentation here:
|
||||
|
||||
https://help.github.com/categories/63/articles
|
||||
|
||||
Closure Library developers monitor outstanding pull requests. They may
|
||||
request changes on the pull request before accepting. They will also
|
||||
verify that the CLA has been signed.
|
||||
|
||||
Oftentimes, the pull request will not be directly merged, but patched to
|
||||
the internal Google codebase to verify that unit and integration tests
|
||||
will Closure pass before submitting (and optionally make changes to
|
||||
the patch to match style, fix text, or to make the code or comments
|
||||
clearer). In this case, the issue associated with the pull request
|
||||
will be closed when the patch pushed to the repository via the MOE
|
||||
(Make Open Easy) system.
|
||||
|
||||
https://code.google.com/p/moe-java/
|
||||
176
dom/canvas/test/webgl-conf/checkout/closure-library/LICENSE
Normal file
176
dom/canvas/test/webgl-conf/checkout/closure-library/LICENSE
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
This is a partial snapshot of the Closure library workspace from:
|
||||
|
||||
https://github.com/google/closure-library
|
||||
|
||||
It contains only the portions needed to type check the ported dEQP
|
||||
tests, namely:
|
||||
|
||||
closure/goog/base.js
|
||||
closure/goog/deps.js
|
||||
|
||||
and supporting scripts in closure/bin/ .
|
||||
|
||||
The current version snapshotted here is:
|
||||
|
||||
-----
|
||||
commit 57bdfe0093cc158fb3a58d2c5f7d75ece8c4b45b
|
||||
Author: Nathan Naze <nanaze@gmail.com>
|
||||
Date: Fri Apr 24 18:38:26 2015 -0400
|
||||
|
||||
fix bad merge
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Closure Library
|
||||
|
||||
Closure Library is a powerful, low-level JavaScript library designed
|
||||
for building complex and scalable web applications. It is used by many
|
||||
Google web applications, such as Gmail and Google Docs.
|
||||
|
||||
For more information, visit the
|
||||
[Google Developers](https://developers.google.com/closure/library) or
|
||||
[GitHub](https://github.com/google/closure-library) sites.
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2009 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 for Closure Library dependency calculation.
|
||||
|
||||
ClosureBuilder scans source files to build dependency info. From the
|
||||
dependencies, the script can produce a manifest in dependency order,
|
||||
a concatenated script, or compiled output from the Closure Compiler.
|
||||
|
||||
Paths to files can be expressed as individual arguments to the tool (intended
|
||||
for use with find and xargs). As a convenience, --root can be used to specify
|
||||
all JS files below a directory.
|
||||
|
||||
usage: %prog [options] [file1.js file2.js ...]
|
||||
"""
|
||||
|
||||
__author__ = 'nnaze@google.com (Nathan Naze)'
|
||||
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import depstree
|
||||
import jscompiler
|
||||
import source
|
||||
import treescan
|
||||
|
||||
|
||||
def _GetOptionsParser():
|
||||
"""Get the options parser."""
|
||||
|
||||
parser = optparse.OptionParser(__doc__)
|
||||
parser.add_option('-i',
|
||||
'--input',
|
||||
dest='inputs',
|
||||
action='append',
|
||||
default=[],
|
||||
help='One or more input files to calculate dependencies '
|
||||
'for. The namespaces in this file will be combined with '
|
||||
'those given with the -n flag to form the set of '
|
||||
'namespaces to find dependencies for.')
|
||||
parser.add_option('-n',
|
||||
'--namespace',
|
||||
dest='namespaces',
|
||||
action='append',
|
||||
default=[],
|
||||
help='One or more namespaces to calculate dependencies '
|
||||
'for. These namespaces will be combined with those given '
|
||||
'with the -i flag to form the set of namespaces to find '
|
||||
'dependencies for. A Closure namespace is a '
|
||||
'dot-delimited path expression declared with a call to '
|
||||
'goog.provide() (e.g. "goog.array" or "foo.bar").')
|
||||
parser.add_option('--root',
|
||||
dest='roots',
|
||||
action='append',
|
||||
default=[],
|
||||
help='The paths that should be traversed to build the '
|
||||
'dependencies.')
|
||||
parser.add_option('-o',
|
||||
'--output_mode',
|
||||
dest='output_mode',
|
||||
type='choice',
|
||||
action='store',
|
||||
choices=['list', 'script', 'compiled'],
|
||||
default='list',
|
||||
help='The type of output to generate from this script. '
|
||||
'Options are "list" for a list of filenames, "script" '
|
||||
'for a single script containing the contents of all the '
|
||||
'files, or "compiled" to produce compiled output with '
|
||||
'the Closure Compiler. Default is "list".')
|
||||
parser.add_option('-c',
|
||||
'--compiler_jar',
|
||||
dest='compiler_jar',
|
||||
action='store',
|
||||
help='The location of the Closure compiler .jar file.')
|
||||
parser.add_option('-f',
|
||||
'--compiler_flags',
|
||||
dest='compiler_flags',
|
||||
default=[],
|
||||
action='append',
|
||||
help='Additional flags to pass to the Closure compiler. '
|
||||
'To pass multiple flags, --compiler_flags has to be '
|
||||
'specified multiple times.')
|
||||
parser.add_option('-j',
|
||||
'--jvm_flags',
|
||||
dest='jvm_flags',
|
||||
default=[],
|
||||
action='append',
|
||||
help='Additional flags to pass to the JVM compiler. '
|
||||
'To pass multiple flags, --jvm_flags has to be '
|
||||
'specified multiple times.')
|
||||
parser.add_option('--output_file',
|
||||
dest='output_file',
|
||||
action='store',
|
||||
help=('If specified, write output to this path instead of '
|
||||
'writing to standard output.'))
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def _GetInputByPath(path, sources):
|
||||
"""Get the source identified by a path.
|
||||
|
||||
Args:
|
||||
path: str, A path to a file that identifies a source.
|
||||
sources: An iterable collection of source objects.
|
||||
|
||||
Returns:
|
||||
The source from sources identified by path, if found. Converts to
|
||||
real paths for comparison.
|
||||
"""
|
||||
for js_source in sources:
|
||||
# Convert both to real paths for comparison.
|
||||
if os.path.realpath(path) == os.path.realpath(js_source.GetPath()):
|
||||
return js_source
|
||||
|
||||
|
||||
def _GetClosureBaseFile(sources):
|
||||
"""Given a set of sources, returns the one base.js file.
|
||||
|
||||
Note that if zero or two or more base.js files are found, an error message
|
||||
will be written and the program will be exited.
|
||||
|
||||
Args:
|
||||
sources: An iterable of _PathSource objects.
|
||||
|
||||
Returns:
|
||||
The _PathSource representing the base Closure file.
|
||||
"""
|
||||
base_files = [
|
||||
js_source for js_source in sources if _IsClosureBaseFile(js_source)]
|
||||
|
||||
if not base_files:
|
||||
logging.error('No Closure base.js file found.')
|
||||
sys.exit(1)
|
||||
if len(base_files) > 1:
|
||||
logging.error('More than one Closure base.js files found at these paths:')
|
||||
for base_file in base_files:
|
||||
logging.error(base_file.GetPath())
|
||||
sys.exit(1)
|
||||
return base_files[0]
|
||||
|
||||
|
||||
def _IsClosureBaseFile(js_source):
|
||||
"""Returns true if the given _PathSource is the Closure base.js source."""
|
||||
return (os.path.basename(js_source.GetPath()) == 'base.js' and
|
||||
js_source.provides == set(['goog']))
|
||||
|
||||
|
||||
class _PathSource(source.Source):
|
||||
"""Source file subclass that remembers its file path."""
|
||||
|
||||
def __init__(self, path):
|
||||
"""Initialize a source.
|
||||
|
||||
Args:
|
||||
path: str, Path to a JavaScript file. The source string will be read
|
||||
from this file.
|
||||
"""
|
||||
super(_PathSource, self).__init__(source.GetFileContents(path))
|
||||
|
||||
self._path = path
|
||||
|
||||
def __str__(self):
|
||||
return 'PathSource %s' % self._path
|
||||
|
||||
def GetPath(self):
|
||||
"""Returns the path."""
|
||||
return self._path
|
||||
|
||||
|
||||
def _WrapGoogModuleSource(src):
|
||||
return ('goog.loadModule(function(exports) {{'
|
||||
'"use strict";'
|
||||
'{0}'
|
||||
'\n' # terminate any trailing single line comment.
|
||||
';return exports'
|
||||
'}});\n').format(src)
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
|
||||
level=logging.INFO)
|
||||
options, args = _GetOptionsParser().parse_args()
|
||||
|
||||
# Make our output pipe.
|
||||
if options.output_file:
|
||||
out = open(options.output_file, 'w')
|
||||
else:
|
||||
out = sys.stdout
|
||||
|
||||
sources = set()
|
||||
|
||||
logging.info('Scanning paths...')
|
||||
for path in options.roots:
|
||||
for js_path in treescan.ScanTreeForJsFiles(path):
|
||||
sources.add(_PathSource(js_path))
|
||||
|
||||
# Add scripts specified on the command line.
|
||||
for js_path in args:
|
||||
sources.add(_PathSource(js_path))
|
||||
|
||||
logging.info('%s sources scanned.', len(sources))
|
||||
|
||||
# Though deps output doesn't need to query the tree, we still build it
|
||||
# to validate dependencies.
|
||||
logging.info('Building dependency tree..')
|
||||
tree = depstree.DepsTree(sources)
|
||||
|
||||
input_namespaces = set()
|
||||
inputs = options.inputs or []
|
||||
for input_path in inputs:
|
||||
js_input = _GetInputByPath(input_path, sources)
|
||||
if not js_input:
|
||||
logging.error('No source matched input %s', input_path)
|
||||
sys.exit(1)
|
||||
input_namespaces.update(js_input.provides)
|
||||
|
||||
input_namespaces.update(options.namespaces)
|
||||
|
||||
if not input_namespaces:
|
||||
logging.error('No namespaces found. At least one namespace must be '
|
||||
'specified with the --namespace or --input flags.')
|
||||
sys.exit(2)
|
||||
|
||||
# The Closure Library base file must go first.
|
||||
base = _GetClosureBaseFile(sources)
|
||||
deps = [base] + tree.GetDependencies(input_namespaces)
|
||||
|
||||
output_mode = options.output_mode
|
||||
if output_mode == 'list':
|
||||
out.writelines([js_source.GetPath() + '\n' for js_source in deps])
|
||||
elif output_mode == 'script':
|
||||
for js_source in deps:
|
||||
src = js_source.GetSource()
|
||||
if js_source.is_goog_module:
|
||||
src = _WrapGoogModuleSource(src)
|
||||
out.write(src + '\n')
|
||||
elif output_mode == 'compiled':
|
||||
logging.warning("""\
|
||||
Closure Compiler now natively understands and orders Closure dependencies and
|
||||
is prefererred over using this script for performing JavaScript compilation.
|
||||
|
||||
Please migrate your codebase.
|
||||
|
||||
See:
|
||||
https://github.com/google/closure-compiler/wiki/Manage-Closure-Dependencies
|
||||
""")
|
||||
|
||||
# Make sure a .jar is specified.
|
||||
if not options.compiler_jar:
|
||||
logging.error('--compiler_jar flag must be specified if --output is '
|
||||
'"compiled"')
|
||||
sys.exit(2)
|
||||
|
||||
# Will throw an error if the compilation fails.
|
||||
compiled_source = jscompiler.Compile(
|
||||
options.compiler_jar,
|
||||
[js_source.GetPath() for js_source in deps],
|
||||
jvm_flags=options.jvm_flags,
|
||||
compiler_flags=options.compiler_flags)
|
||||
|
||||
logging.info('JavaScript compilation succeeded.')
|
||||
out.write(compiled_source)
|
||||
|
||||
else:
|
||||
logging.error('Invalid value for --output flag.')
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
# Copyright 2009 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.
|
||||
|
||||
|
||||
"""Class to represent a full Closure Library dependency tree.
|
||||
|
||||
Offers a queryable tree of dependencies of a given set of sources. The tree
|
||||
will also do logical validation to prevent duplicate provides and circular
|
||||
dependencies.
|
||||
"""
|
||||
|
||||
__author__ = 'nnaze@google.com (Nathan Naze)'
|
||||
|
||||
|
||||
class DepsTree(object):
|
||||
"""Represents the set of dependencies between source files."""
|
||||
|
||||
def __init__(self, sources):
|
||||
"""Initializes the tree with a set of sources.
|
||||
|
||||
Args:
|
||||
sources: A set of JavaScript sources.
|
||||
|
||||
Raises:
|
||||
MultipleProvideError: A namespace is provided by muplitple sources.
|
||||
NamespaceNotFoundError: A namespace is required but never provided.
|
||||
"""
|
||||
|
||||
self._sources = sources
|
||||
self._provides_map = dict()
|
||||
|
||||
# Ensure nothing was provided twice.
|
||||
for source in sources:
|
||||
for provide in source.provides:
|
||||
if provide in self._provides_map:
|
||||
raise MultipleProvideError(
|
||||
provide, [self._provides_map[provide], source])
|
||||
|
||||
self._provides_map[provide] = source
|
||||
|
||||
# Check that all required namespaces are provided.
|
||||
for source in sources:
|
||||
for require in source.requires:
|
||||
if require not in self._provides_map:
|
||||
raise NamespaceNotFoundError(require, source)
|
||||
|
||||
def GetDependencies(self, required_namespaces):
|
||||
"""Get source dependencies, in order, for the given namespaces.
|
||||
|
||||
Args:
|
||||
required_namespaces: A string (for one) or list (for one or more) of
|
||||
namespaces.
|
||||
|
||||
Returns:
|
||||
A list of source objects that provide those namespaces and all
|
||||
requirements, in dependency order.
|
||||
|
||||
Raises:
|
||||
NamespaceNotFoundError: A namespace is requested but doesn't exist.
|
||||
CircularDependencyError: A cycle is detected in the dependency tree.
|
||||
"""
|
||||
if isinstance(required_namespaces, str):
|
||||
required_namespaces = [required_namespaces]
|
||||
|
||||
deps_sources = []
|
||||
|
||||
for namespace in required_namespaces:
|
||||
for source in DepsTree._ResolveDependencies(
|
||||
namespace, [], self._provides_map, []):
|
||||
if source not in deps_sources:
|
||||
deps_sources.append(source)
|
||||
|
||||
return deps_sources
|
||||
|
||||
@staticmethod
|
||||
def _ResolveDependencies(required_namespace, deps_list, provides_map,
|
||||
traversal_path):
|
||||
"""Resolve dependencies for Closure source files.
|
||||
|
||||
Follows the dependency tree down and builds a list of sources in dependency
|
||||
order. This function will recursively call itself to fill all dependencies
|
||||
below the requested namespaces, and then append its sources at the end of
|
||||
the list.
|
||||
|
||||
Args:
|
||||
required_namespace: String of required namespace.
|
||||
deps_list: List of sources in dependency order. This function will append
|
||||
the required source once all of its dependencies are satisfied.
|
||||
provides_map: Map from namespace to source that provides it.
|
||||
traversal_path: List of namespaces of our path from the root down the
|
||||
dependency/recursion tree. Used to identify cyclical dependencies.
|
||||
This is a list used as a stack -- when the function is entered, the
|
||||
current namespace is pushed and popped right before returning.
|
||||
Each recursive call will check that the current namespace does not
|
||||
appear in the list, throwing a CircularDependencyError if it does.
|
||||
|
||||
Returns:
|
||||
The given deps_list object filled with sources in dependency order.
|
||||
|
||||
Raises:
|
||||
NamespaceNotFoundError: A namespace is requested but doesn't exist.
|
||||
CircularDependencyError: A cycle is detected in the dependency tree.
|
||||
"""
|
||||
|
||||
source = provides_map.get(required_namespace)
|
||||
if not source:
|
||||
raise NamespaceNotFoundError(required_namespace)
|
||||
|
||||
if required_namespace in traversal_path:
|
||||
traversal_path.append(required_namespace) # do this *after* the test
|
||||
|
||||
# This must be a cycle.
|
||||
raise CircularDependencyError(traversal_path)
|
||||
|
||||
# If we don't have the source yet, we'll have to visit this namespace and
|
||||
# add the required dependencies to deps_list.
|
||||
if source not in deps_list:
|
||||
traversal_path.append(required_namespace)
|
||||
|
||||
for require in source.requires:
|
||||
|
||||
# Append all other dependencies before we append our own.
|
||||
DepsTree._ResolveDependencies(require, deps_list, provides_map,
|
||||
traversal_path)
|
||||
deps_list.append(source)
|
||||
|
||||
traversal_path.pop()
|
||||
|
||||
return deps_list
|
||||
|
||||
|
||||
class BaseDepsTreeError(Exception):
|
||||
"""Base DepsTree error."""
|
||||
|
||||
def __init__(self):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
||||
class CircularDependencyError(BaseDepsTreeError):
|
||||
"""Raised when a dependency cycle is encountered."""
|
||||
|
||||
def __init__(self, dependency_list):
|
||||
BaseDepsTreeError.__init__(self)
|
||||
self._dependency_list = dependency_list
|
||||
|
||||
def __str__(self):
|
||||
return ('Encountered circular dependency:\n%s\n' %
|
||||
'\n'.join(self._dependency_list))
|
||||
|
||||
|
||||
class MultipleProvideError(BaseDepsTreeError):
|
||||
"""Raised when a namespace is provided more than once."""
|
||||
|
||||
def __init__(self, namespace, sources):
|
||||
BaseDepsTreeError.__init__(self)
|
||||
self._namespace = namespace
|
||||
self._sources = sources
|
||||
|
||||
def __str__(self):
|
||||
source_strs = map(str, self._sources)
|
||||
|
||||
return ('Namespace "%s" provided more than once in sources:\n%s\n' %
|
||||
(self._namespace, '\n'.join(source_strs)))
|
||||
|
||||
|
||||
class NamespaceNotFoundError(BaseDepsTreeError):
|
||||
"""Raised when a namespace is requested but not provided."""
|
||||
|
||||
def __init__(self, namespace, source=None):
|
||||
BaseDepsTreeError.__init__(self)
|
||||
self._namespace = namespace
|
||||
self._source = source
|
||||
|
||||
def __str__(self):
|
||||
msg = 'Namespace "%s" never provided.' % self._namespace
|
||||
if self._source:
|
||||
msg += ' Required in %s' % self._source
|
||||
return msg
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2009 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.
|
||||
|
||||
|
||||
"""Generates out a Closure deps.js file given a list of JavaScript sources.
|
||||
|
||||
Paths can be specified as arguments or (more commonly) specifying trees
|
||||
with the flags (call with --help for descriptions).
|
||||
|
||||
Usage: depswriter.py [path/to/js1.js [path/to/js2.js] ...]
|
||||
"""
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import posixpath
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
import source
|
||||
import treescan
|
||||
|
||||
|
||||
__author__ = 'nnaze@google.com (Nathan Naze)'
|
||||
|
||||
|
||||
def MakeDepsFile(source_map):
|
||||
"""Make a generated deps file.
|
||||
|
||||
Args:
|
||||
source_map: A dict map of the source path to source.Source object.
|
||||
|
||||
Returns:
|
||||
str, A generated deps file source.
|
||||
"""
|
||||
|
||||
# Write in path alphabetical order
|
||||
paths = sorted(source_map.keys())
|
||||
|
||||
lines = []
|
||||
|
||||
for path in paths:
|
||||
js_source = source_map[path]
|
||||
|
||||
# We don't need to add entries that don't provide anything.
|
||||
if js_source.provides:
|
||||
lines.append(_GetDepsLine(path, js_source))
|
||||
|
||||
return ''.join(lines)
|
||||
|
||||
|
||||
def _GetDepsLine(path, js_source):
|
||||
"""Get a deps.js file string for a source."""
|
||||
|
||||
provides = sorted(js_source.provides)
|
||||
requires = sorted(js_source.requires)
|
||||
module = 'true' if js_source.is_goog_module else 'false'
|
||||
|
||||
return 'goog.addDependency(\'%s\', %s, %s, %s);\n' % (
|
||||
path, provides, requires, module)
|
||||
|
||||
|
||||
def _GetOptionsParser():
|
||||
"""Get the options parser."""
|
||||
|
||||
parser = optparse.OptionParser(__doc__)
|
||||
|
||||
parser.add_option('--output_file',
|
||||
dest='output_file',
|
||||
action='store',
|
||||
help=('If specified, write output to this path instead of '
|
||||
'writing to standard output.'))
|
||||
parser.add_option('--root',
|
||||
dest='roots',
|
||||
default=[],
|
||||
action='append',
|
||||
help='A root directory to scan for JS source files. '
|
||||
'Paths of JS files in generated deps file will be '
|
||||
'relative to this path. This flag may be specified '
|
||||
'multiple times.')
|
||||
parser.add_option('--root_with_prefix',
|
||||
dest='roots_with_prefix',
|
||||
default=[],
|
||||
action='append',
|
||||
help='A root directory to scan for JS source files, plus '
|
||||
'a prefix (if either contains a space, surround with '
|
||||
'quotes). Paths in generated deps file will be relative '
|
||||
'to the root, but preceded by the prefix. This flag '
|
||||
'may be specified multiple times.')
|
||||
parser.add_option('--path_with_depspath',
|
||||
dest='paths_with_depspath',
|
||||
default=[],
|
||||
action='append',
|
||||
help='A path to a source file and an alternate path to '
|
||||
'the file in the generated deps file (if either contains '
|
||||
'a space, surround with whitespace). This flag may be '
|
||||
'specified multiple times.')
|
||||
return parser
|
||||
|
||||
|
||||
def _NormalizePathSeparators(path):
|
||||
"""Replaces OS-specific path separators with POSIX-style slashes.
|
||||
|
||||
Args:
|
||||
path: str, A file path.
|
||||
|
||||
Returns:
|
||||
str, The path with any OS-specific path separators (such as backslash on
|
||||
Windows) replaced with URL-compatible forward slashes. A no-op on systems
|
||||
that use POSIX paths.
|
||||
"""
|
||||
return path.replace(os.sep, posixpath.sep)
|
||||
|
||||
|
||||
def _GetRelativePathToSourceDict(root, prefix=''):
|
||||
"""Scans a top root directory for .js sources.
|
||||
|
||||
Args:
|
||||
root: str, Root directory.
|
||||
prefix: str, Prefix for returned paths.
|
||||
|
||||
Returns:
|
||||
dict, A map of relative paths (with prefix, if given), to source.Source
|
||||
objects.
|
||||
"""
|
||||
# Remember and restore the cwd when we're done. We work from the root so
|
||||
# that paths are relative from the root.
|
||||
start_wd = os.getcwd()
|
||||
os.chdir(root)
|
||||
|
||||
path_to_source = {}
|
||||
for path in treescan.ScanTreeForJsFiles('.'):
|
||||
prefixed_path = _NormalizePathSeparators(os.path.join(prefix, path))
|
||||
path_to_source[prefixed_path] = source.Source(source.GetFileContents(path))
|
||||
|
||||
os.chdir(start_wd)
|
||||
|
||||
return path_to_source
|
||||
|
||||
|
||||
def _GetPair(s):
|
||||
"""Return a string as a shell-parsed tuple. Two values expected."""
|
||||
try:
|
||||
# shlex uses '\' as an escape character, so they must be escaped.
|
||||
s = s.replace('\\', '\\\\')
|
||||
first, second = shlex.split(s)
|
||||
return (first, second)
|
||||
except:
|
||||
raise Exception('Unable to parse input line as a pair: %s' % s)
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI frontend to MakeDepsFile."""
|
||||
logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
|
||||
level=logging.INFO)
|
||||
options, args = _GetOptionsParser().parse_args()
|
||||
|
||||
path_to_source = {}
|
||||
|
||||
# Roots without prefixes
|
||||
for root in options.roots:
|
||||
path_to_source.update(_GetRelativePathToSourceDict(root))
|
||||
|
||||
# Roots with prefixes
|
||||
for root_and_prefix in options.roots_with_prefix:
|
||||
root, prefix = _GetPair(root_and_prefix)
|
||||
path_to_source.update(_GetRelativePathToSourceDict(root, prefix=prefix))
|
||||
|
||||
# Source paths
|
||||
for path in args:
|
||||
path_to_source[path] = source.Source(source.GetFileContents(path))
|
||||
|
||||
# Source paths with alternate deps paths
|
||||
for path_with_depspath in options.paths_with_depspath:
|
||||
srcpath, depspath = _GetPair(path_with_depspath)
|
||||
path_to_source[depspath] = source.Source(source.GetFileContents(srcpath))
|
||||
|
||||
# Make our output pipe.
|
||||
if options.output_file:
|
||||
out = open(options.output_file, 'w')
|
||||
else:
|
||||
out = sys.stdout
|
||||
|
||||
out.write('// This file was autogenerated by %s.\n' % sys.argv[0])
|
||||
out.write('// Please do not edit.\n')
|
||||
|
||||
out.write(MakeDepsFile(path_to_source))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
# 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
|
||||
|
||||
|
||||
# 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://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
|
||||
_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, 10) 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, source_paths,
|
||||
jvm_flags, compiler_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]
|
||||
|
||||
for path in source_paths:
|
||||
args += ['--js', path]
|
||||
|
||||
# Add compiler flags, if any.
|
||||
if compiler_flags:
|
||||
args += compiler_flags
|
||||
|
||||
return args
|
||||
|
||||
|
||||
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(_GetJavaVersionString())
|
||||
|
||||
args = _GetJsCompilerArgs(
|
||||
compiler_jar_path, java_version, source_paths, jvm_flags, compiler_flags)
|
||||
|
||||
logging.info('Compiling with the following command: %s', ' '.join(args))
|
||||
|
||||
try:
|
||||
return subprocess.check_output(args)
|
||||
except subprocess.CalledProcessError:
|
||||
raise JsCompilerError('JavaScript compilation failed.')
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# Copyright 2009 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.
|
||||
|
||||
|
||||
"""Scans a source JS file for its provided and required namespaces.
|
||||
|
||||
Simple class to scan a JavaScript file and express its dependencies.
|
||||
"""
|
||||
|
||||
__author__ = 'nnaze@google.com'
|
||||
|
||||
|
||||
import re
|
||||
|
||||
_BASE_REGEX_STRING = r'^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)'
|
||||
_MODULE_REGEX = re.compile(_BASE_REGEX_STRING % 'module')
|
||||
_PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide')
|
||||
|
||||
_REQUIRE_REGEX_STRING = (r'^\s*(?:(?:var|let|const)\s+[a-zA-Z_$][a-zA-Z0-9$_]*'
|
||||
r'\s*=\s*)?goog\.require\(\s*[\'"](.+)[\'"]\s*\)')
|
||||
_REQUIRES_REGEX = re.compile(_REQUIRE_REGEX_STRING)
|
||||
|
||||
|
||||
class Source(object):
|
||||
"""Scans a JavaScript source for its provided and required namespaces."""
|
||||
|
||||
# Matches a "/* ... */" comment.
|
||||
# Note: We can't definitively distinguish a "/*" in a string literal without a
|
||||
# state machine tokenizer. We'll assume that a line starting with whitespace
|
||||
# and "/*" is a comment.
|
||||
_COMMENT_REGEX = re.compile(
|
||||
r"""
|
||||
^\s* # Start of a new line and whitespace
|
||||
/\* # Opening "/*"
|
||||
.*? # Non greedy match of any characters (including newlines)
|
||||
\*/ # Closing "*/""",
|
||||
re.MULTILINE | re.DOTALL | re.VERBOSE)
|
||||
|
||||
def __init__(self, source):
|
||||
"""Initialize a source.
|
||||
|
||||
Args:
|
||||
source: str, The JavaScript source.
|
||||
"""
|
||||
|
||||
self.provides = set()
|
||||
self.requires = set()
|
||||
self.is_goog_module = False
|
||||
|
||||
self._source = source
|
||||
self._ScanSource()
|
||||
|
||||
def GetSource(self):
|
||||
"""Get the source as a string."""
|
||||
return self._source
|
||||
|
||||
@classmethod
|
||||
def _StripComments(cls, source):
|
||||
return cls._COMMENT_REGEX.sub('', source)
|
||||
|
||||
@classmethod
|
||||
def _HasProvideGoogFlag(cls, source):
|
||||
"""Determines whether the @provideGoog flag is in a comment."""
|
||||
for comment_content in cls._COMMENT_REGEX.findall(source):
|
||||
if '@provideGoog' in comment_content:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _ScanSource(self):
|
||||
"""Fill in provides and requires by scanning the source."""
|
||||
|
||||
stripped_source = self._StripComments(self.GetSource())
|
||||
|
||||
source_lines = stripped_source.splitlines()
|
||||
for line in source_lines:
|
||||
match = _PROVIDE_REGEX.match(line)
|
||||
if match:
|
||||
self.provides.add(match.group(1))
|
||||
match = _MODULE_REGEX.match(line)
|
||||
if match:
|
||||
self.provides.add(match.group(1))
|
||||
self.is_goog_module = True
|
||||
match = _REQUIRES_REGEX.match(line)
|
||||
if match:
|
||||
self.requires.add(match.group(1))
|
||||
|
||||
# Closure's base file implicitly provides 'goog'.
|
||||
# This is indicated with the @provideGoog flag.
|
||||
if self._HasProvideGoogFlag(self.GetSource()):
|
||||
|
||||
if len(self.provides) or len(self.requires):
|
||||
raise Exception(
|
||||
'Base file should not provide or require namespaces.')
|
||||
|
||||
self.provides.add('goog')
|
||||
|
||||
|
||||
def GetFileContents(path):
|
||||
"""Get a file's contents as a string.
|
||||
|
||||
Args:
|
||||
path: str, Path to file.
|
||||
|
||||
Returns:
|
||||
str, Contents of file.
|
||||
|
||||
Raises:
|
||||
IOError: An error occurred opening or reading the file.
|
||||
|
||||
"""
|
||||
fileobj = open(path)
|
||||
try:
|
||||
return fileobj.read()
|
||||
finally:
|
||||
fileobj.close()
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
"""Shared utility functions for scanning directory trees."""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
__author__ = 'nnaze@google.com (Nathan Naze)'
|
||||
|
||||
|
||||
# Matches a .js file path.
|
||||
_JS_FILE_REGEX = re.compile(r'^.+\.js$')
|
||||
|
||||
|
||||
def ScanTreeForJsFiles(root):
|
||||
"""Scans a directory tree for JavaScript files.
|
||||
|
||||
Args:
|
||||
root: str, Path to a root directory.
|
||||
|
||||
Returns:
|
||||
An iterable of paths to JS files, relative to cwd.
|
||||
"""
|
||||
return ScanTree(root, path_filter=_JS_FILE_REGEX)
|
||||
|
||||
|
||||
def ScanTree(root, path_filter=None, ignore_hidden=True):
|
||||
"""Scans a directory tree for files.
|
||||
|
||||
Args:
|
||||
root: str, Path to a root directory.
|
||||
path_filter: A regular expression filter. If set, only paths matching
|
||||
the path_filter are returned.
|
||||
ignore_hidden: If True, do not follow or return hidden directories or files
|
||||
(those starting with a '.' character).
|
||||
|
||||
Yields:
|
||||
A string path to files, relative to cwd.
|
||||
"""
|
||||
|
||||
def OnError(os_error):
|
||||
raise os_error
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(root, onerror=OnError):
|
||||
# os.walk allows us to modify dirnames to prevent decent into particular
|
||||
# directories. Avoid hidden directories.
|
||||
for dirname in dirnames:
|
||||
if ignore_hidden and dirname.startswith('.'):
|
||||
dirnames.remove(dirname)
|
||||
|
||||
for filename in filenames:
|
||||
|
||||
# nothing that starts with '.'
|
||||
if ignore_hidden and filename.startswith('.'):
|
||||
continue
|
||||
|
||||
fullpath = os.path.join(dirpath, filename)
|
||||
|
||||
if path_filter and not path_filter.match(fullpath):
|
||||
continue
|
||||
|
||||
yield os.path.normpath(fullpath)
|
||||
|
|
@ -0,0 +1,590 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2006 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.
|
||||
|
||||
|
||||
"""Calculates JavaScript dependencies without requiring Google's build system.
|
||||
|
||||
This tool is deprecated and is provided for legacy users.
|
||||
See build/closurebuilder.py and build/depswriter.py for the current tools.
|
||||
|
||||
It iterates over a number of search paths and builds a dependency tree. With
|
||||
the inputs provided, it walks the dependency tree and outputs all the files
|
||||
required for compilation.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
try:
|
||||
import distutils.version
|
||||
except ImportError:
|
||||
# distutils is not available in all environments
|
||||
distutils = None
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
_BASE_REGEX_STRING = '^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)'
|
||||
req_regex = re.compile(_BASE_REGEX_STRING % 'require')
|
||||
prov_regex = re.compile(_BASE_REGEX_STRING % 'provide')
|
||||
ns_regex = re.compile('^ns:((\w+\.)*(\w+))$')
|
||||
version_regex = re.compile('[\.0-9]+')
|
||||
|
||||
|
||||
def IsValidFile(ref):
|
||||
"""Returns true if the provided reference is a file and exists."""
|
||||
return os.path.isfile(ref)
|
||||
|
||||
|
||||
def IsJsFile(ref):
|
||||
"""Returns true if the provided reference is a Javascript file."""
|
||||
return ref.endswith('.js')
|
||||
|
||||
|
||||
def IsNamespace(ref):
|
||||
"""Returns true if the provided reference is a namespace."""
|
||||
return re.match(ns_regex, ref) is not None
|
||||
|
||||
|
||||
def IsDirectory(ref):
|
||||
"""Returns true if the provided reference is a directory."""
|
||||
return os.path.isdir(ref)
|
||||
|
||||
|
||||
def ExpandDirectories(refs):
|
||||
"""Expands any directory references into inputs.
|
||||
|
||||
Description:
|
||||
Looks for any directories in the provided references. Found directories
|
||||
are recursively searched for .js files, which are then added to the result
|
||||
list.
|
||||
|
||||
Args:
|
||||
refs: a list of references such as files, directories, and namespaces
|
||||
|
||||
Returns:
|
||||
A list of references with directories removed and replaced by any
|
||||
.js files that are found in them. Also, the paths will be normalized.
|
||||
"""
|
||||
result = []
|
||||
for ref in refs:
|
||||
if IsDirectory(ref):
|
||||
# Disable 'Unused variable' for subdirs
|
||||
# pylint: disable=unused-variable
|
||||
for (directory, subdirs, filenames) in os.walk(ref):
|
||||
for filename in filenames:
|
||||
if IsJsFile(filename):
|
||||
result.append(os.path.join(directory, filename))
|
||||
else:
|
||||
result.append(ref)
|
||||
return map(os.path.normpath, result)
|
||||
|
||||
|
||||
class DependencyInfo(object):
|
||||
"""Represents a dependency that is used to build and walk a tree."""
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.provides = []
|
||||
self.requires = []
|
||||
|
||||
def __str__(self):
|
||||
return '%s Provides: %s Requires: %s' % (self.filename,
|
||||
repr(self.provides),
|
||||
repr(self.requires))
|
||||
|
||||
|
||||
def BuildDependenciesFromFiles(files):
|
||||
"""Build a list of dependencies from a list of files.
|
||||
|
||||
Description:
|
||||
Takes a list of files, extracts their provides and requires, and builds
|
||||
out a list of dependency objects.
|
||||
|
||||
Args:
|
||||
files: a list of files to be parsed for goog.provides and goog.requires.
|
||||
|
||||
Returns:
|
||||
A list of dependency objects, one for each file in the files argument.
|
||||
"""
|
||||
result = []
|
||||
filenames = set()
|
||||
for filename in files:
|
||||
if filename in filenames:
|
||||
continue
|
||||
|
||||
# Python 3 requires the file encoding to be specified
|
||||
if (sys.version_info[0] < 3):
|
||||
file_handle = open(filename, 'r')
|
||||
else:
|
||||
file_handle = open(filename, 'r', encoding='utf8')
|
||||
|
||||
try:
|
||||
dep = CreateDependencyInfo(filename, file_handle)
|
||||
result.append(dep)
|
||||
finally:
|
||||
file_handle.close()
|
||||
|
||||
filenames.add(filename)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def CreateDependencyInfo(filename, source):
|
||||
"""Create dependency info.
|
||||
|
||||
Args:
|
||||
filename: Filename for source.
|
||||
source: File-like object containing source.
|
||||
|
||||
Returns:
|
||||
A DependencyInfo object with provides and requires filled.
|
||||
"""
|
||||
dep = DependencyInfo(filename)
|
||||
for line in source:
|
||||
if re.match(req_regex, line):
|
||||
dep.requires.append(re.search(req_regex, line).group(1))
|
||||
if re.match(prov_regex, line):
|
||||
dep.provides.append(re.search(prov_regex, line).group(1))
|
||||
return dep
|
||||
|
||||
|
||||
def BuildDependencyHashFromDependencies(deps):
|
||||
"""Builds a hash for searching dependencies by the namespaces they provide.
|
||||
|
||||
Description:
|
||||
Dependency objects can provide multiple namespaces. This method enumerates
|
||||
the provides of each dependency and adds them to a hash that can be used
|
||||
to easily resolve a given dependency by a namespace it provides.
|
||||
|
||||
Args:
|
||||
deps: a list of dependency objects used to build the hash.
|
||||
|
||||
Raises:
|
||||
Exception: If a multiple files try to provide the same namepace.
|
||||
|
||||
Returns:
|
||||
A hash table { namespace: dependency } that can be used to resolve a
|
||||
dependency by a namespace it provides.
|
||||
"""
|
||||
dep_hash = {}
|
||||
for dep in deps:
|
||||
for provide in dep.provides:
|
||||
if provide in dep_hash:
|
||||
raise Exception('Duplicate provide (%s) in (%s, %s)' % (
|
||||
provide,
|
||||
dep_hash[provide].filename,
|
||||
dep.filename))
|
||||
dep_hash[provide] = dep
|
||||
return dep_hash
|
||||
|
||||
|
||||
def CalculateDependencies(paths, inputs):
|
||||
"""Calculates the dependencies for given inputs.
|
||||
|
||||
Description:
|
||||
This method takes a list of paths (files, directories) and builds a
|
||||
searchable data structure based on the namespaces that each .js file
|
||||
provides. It then parses through each input, resolving dependencies
|
||||
against this data structure. The final output is a list of files,
|
||||
including the inputs, that represent all of the code that is needed to
|
||||
compile the given inputs.
|
||||
|
||||
Args:
|
||||
paths: the references (files, directories) that are used to build the
|
||||
dependency hash.
|
||||
inputs: the inputs (files, directories, namespaces) that have dependencies
|
||||
that need to be calculated.
|
||||
|
||||
Raises:
|
||||
Exception: if a provided input is invalid.
|
||||
|
||||
Returns:
|
||||
A list of all files, including inputs, that are needed to compile the given
|
||||
inputs.
|
||||
"""
|
||||
deps = BuildDependenciesFromFiles(paths + inputs)
|
||||
search_hash = BuildDependencyHashFromDependencies(deps)
|
||||
result_list = []
|
||||
seen_list = []
|
||||
for input_file in inputs:
|
||||
if IsNamespace(input_file):
|
||||
namespace = re.search(ns_regex, input_file).group(1)
|
||||
if namespace not in search_hash:
|
||||
raise Exception('Invalid namespace (%s)' % namespace)
|
||||
input_file = search_hash[namespace].filename
|
||||
if not IsValidFile(input_file) or not IsJsFile(input_file):
|
||||
raise Exception('Invalid file (%s)' % input_file)
|
||||
seen_list.append(input_file)
|
||||
file_handle = open(input_file, 'r')
|
||||
try:
|
||||
for line in file_handle:
|
||||
if re.match(req_regex, line):
|
||||
require = re.search(req_regex, line).group(1)
|
||||
ResolveDependencies(require, search_hash, result_list, seen_list)
|
||||
finally:
|
||||
file_handle.close()
|
||||
result_list.append(input_file)
|
||||
|
||||
# All files depend on base.js, so put it first.
|
||||
base_js_path = FindClosureBasePath(paths)
|
||||
if base_js_path:
|
||||
result_list.insert(0, base_js_path)
|
||||
else:
|
||||
logging.warning('Closure Library base.js not found.')
|
||||
|
||||
return result_list
|
||||
|
||||
|
||||
def FindClosureBasePath(paths):
|
||||
"""Given a list of file paths, return Closure base.js path, if any.
|
||||
|
||||
Args:
|
||||
paths: A list of paths.
|
||||
|
||||
Returns:
|
||||
The path to Closure's base.js file including filename, if found.
|
||||
"""
|
||||
|
||||
for path in paths:
|
||||
pathname, filename = os.path.split(path)
|
||||
|
||||
if filename == 'base.js':
|
||||
f = open(path)
|
||||
|
||||
is_base = False
|
||||
|
||||
# Sanity check that this is the Closure base file. Check that this
|
||||
# is where goog is defined. This is determined by the @provideGoog
|
||||
# flag.
|
||||
for line in f:
|
||||
if '@provideGoog' in line:
|
||||
is_base = True
|
||||
break
|
||||
|
||||
f.close()
|
||||
|
||||
if is_base:
|
||||
return path
|
||||
|
||||
def ResolveDependencies(require, search_hash, result_list, seen_list):
|
||||
"""Takes a given requirement and resolves all of the dependencies for it.
|
||||
|
||||
Description:
|
||||
A given requirement may require other dependencies. This method
|
||||
recursively resolves all dependencies for the given requirement.
|
||||
|
||||
Raises:
|
||||
Exception: when require does not exist in the search_hash.
|
||||
|
||||
Args:
|
||||
require: the namespace to resolve dependencies for.
|
||||
search_hash: the data structure used for resolving dependencies.
|
||||
result_list: a list of filenames that have been calculated as dependencies.
|
||||
This variable is the output for this function.
|
||||
seen_list: a list of filenames that have been 'seen'. This is required
|
||||
for the dependency->dependant ordering.
|
||||
"""
|
||||
if require not in search_hash:
|
||||
raise Exception('Missing provider for (%s)' % require)
|
||||
|
||||
dep = search_hash[require]
|
||||
if not dep.filename in seen_list:
|
||||
seen_list.append(dep.filename)
|
||||
for sub_require in dep.requires:
|
||||
ResolveDependencies(sub_require, search_hash, result_list, seen_list)
|
||||
result_list.append(dep.filename)
|
||||
|
||||
|
||||
def GetDepsLine(dep, base_path):
|
||||
"""Returns a JS string for a dependency statement in the deps.js file.
|
||||
|
||||
Args:
|
||||
dep: The dependency that we're printing.
|
||||
base_path: The path to Closure's base.js including filename.
|
||||
"""
|
||||
return 'goog.addDependency("%s", %s, %s);' % (
|
||||
GetRelpath(dep.filename, base_path), dep.provides, dep.requires)
|
||||
|
||||
|
||||
def GetRelpath(path, start):
|
||||
"""Return a relative path to |path| from |start|."""
|
||||
# NOTE: Python 2.6 provides os.path.relpath, which has almost the same
|
||||
# functionality as this function. Since we want to support 2.4, we have
|
||||
# to implement it manually. :(
|
||||
path_list = os.path.abspath(os.path.normpath(path)).split(os.sep)
|
||||
start_list = os.path.abspath(
|
||||
os.path.normpath(os.path.dirname(start))).split(os.sep)
|
||||
|
||||
common_prefix_count = 0
|
||||
for i in range(0, min(len(path_list), len(start_list))):
|
||||
if path_list[i] != start_list[i]:
|
||||
break
|
||||
common_prefix_count += 1
|
||||
|
||||
# Always use forward slashes, because this will get expanded to a url,
|
||||
# not a file path.
|
||||
return '/'.join(['..'] * (len(start_list) - common_prefix_count) +
|
||||
path_list[common_prefix_count:])
|
||||
|
||||
|
||||
def PrintLine(msg, out):
|
||||
out.write(msg)
|
||||
out.write('\n')
|
||||
|
||||
|
||||
def PrintDeps(source_paths, deps, out):
|
||||
"""Print out a deps.js file from a list of source paths.
|
||||
|
||||
Args:
|
||||
source_paths: Paths that we should generate dependency info for.
|
||||
deps: Paths that provide dependency info. Their dependency info should
|
||||
not appear in the deps file.
|
||||
out: The output file.
|
||||
|
||||
Returns:
|
||||
True on success, false if it was unable to find the base path
|
||||
to generate deps relative to.
|
||||
"""
|
||||
base_path = FindClosureBasePath(source_paths + deps)
|
||||
if not base_path:
|
||||
return False
|
||||
|
||||
PrintLine('// This file was autogenerated by calcdeps.py', out)
|
||||
excludesSet = set(deps)
|
||||
|
||||
for dep in BuildDependenciesFromFiles(source_paths + deps):
|
||||
if not dep.filename in excludesSet:
|
||||
PrintLine(GetDepsLine(dep, base_path), out)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def PrintScript(source_paths, out):
|
||||
for index, dep in enumerate(source_paths):
|
||||
PrintLine('// Input %d' % index, out)
|
||||
f = open(dep, 'r')
|
||||
PrintLine(f.read(), out)
|
||||
f.close()
|
||||
|
||||
|
||||
def GetJavaVersion():
|
||||
"""Returns the string for the current version of Java installed."""
|
||||
proc = subprocess.Popen(['java', '-version'], stderr=subprocess.PIPE)
|
||||
proc.wait()
|
||||
version_line = proc.stderr.read().splitlines()[0]
|
||||
return version_regex.search(version_line).group()
|
||||
|
||||
|
||||
def FilterByExcludes(options, files):
|
||||
"""Filters the given files by the exlusions specified at the command line.
|
||||
|
||||
Args:
|
||||
options: The flags to calcdeps.
|
||||
files: The files to filter.
|
||||
Returns:
|
||||
A list of files.
|
||||
"""
|
||||
excludes = []
|
||||
if options.excludes:
|
||||
excludes = ExpandDirectories(options.excludes)
|
||||
|
||||
excludesSet = set(excludes)
|
||||
return [i for i in files if not i in excludesSet]
|
||||
|
||||
|
||||
def GetPathsFromOptions(options):
|
||||
"""Generates the path files from flag options.
|
||||
|
||||
Args:
|
||||
options: The flags to calcdeps.
|
||||
Returns:
|
||||
A list of files in the specified paths. (strings).
|
||||
"""
|
||||
|
||||
search_paths = options.paths
|
||||
if not search_paths:
|
||||
search_paths = ['.'] # Add default folder if no path is specified.
|
||||
|
||||
search_paths = ExpandDirectories(search_paths)
|
||||
return FilterByExcludes(options, search_paths)
|
||||
|
||||
|
||||
def GetInputsFromOptions(options):
|
||||
"""Generates the inputs from flag options.
|
||||
|
||||
Args:
|
||||
options: The flags to calcdeps.
|
||||
Returns:
|
||||
A list of inputs (strings).
|
||||
"""
|
||||
inputs = options.inputs
|
||||
if not inputs: # Parse stdin
|
||||
logging.info('No inputs specified. Reading from stdin...')
|
||||
inputs = filter(None, [line.strip('\n') for line in sys.stdin.readlines()])
|
||||
|
||||
logging.info('Scanning files...')
|
||||
inputs = ExpandDirectories(inputs)
|
||||
|
||||
return FilterByExcludes(options, inputs)
|
||||
|
||||
|
||||
def Compile(compiler_jar_path, source_paths, out, 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.
|
||||
"""
|
||||
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, stderrdata) = proc.communicate()
|
||||
if proc.returncode != 0:
|
||||
logging.error('JavaScript compilation failed.')
|
||||
sys.exit(1)
|
||||
else:
|
||||
out.write(stdoutdata)
|
||||
|
||||
|
||||
def main():
|
||||
"""The entrypoint for this script."""
|
||||
|
||||
logging.basicConfig(format='calcdeps.py: %(message)s', level=logging.INFO)
|
||||
|
||||
usage = 'usage: %prog [options] arg'
|
||||
parser = optparse.OptionParser(usage)
|
||||
parser.add_option('-i',
|
||||
'--input',
|
||||
dest='inputs',
|
||||
action='append',
|
||||
help='The inputs to calculate dependencies for. Valid '
|
||||
'values can be files, directories, or namespaces '
|
||||
'(ns:goog.net.XhrIo). Only relevant to "list" and '
|
||||
'"script" output.')
|
||||
parser.add_option('-p',
|
||||
'--path',
|
||||
dest='paths',
|
||||
action='append',
|
||||
help='The paths that should be traversed to build the '
|
||||
'dependencies.')
|
||||
parser.add_option('-d',
|
||||
'--dep',
|
||||
dest='deps',
|
||||
action='append',
|
||||
help='Directories or files that should be traversed to '
|
||||
'find required dependencies for the deps file. '
|
||||
'Does not generate dependency information for names '
|
||||
'provided by these files. Only useful in "deps" mode.')
|
||||
parser.add_option('-e',
|
||||
'--exclude',
|
||||
dest='excludes',
|
||||
action='append',
|
||||
help='Files or directories to exclude from the --path '
|
||||
'and --input flags')
|
||||
parser.add_option('-o',
|
||||
'--output_mode',
|
||||
dest='output_mode',
|
||||
action='store',
|
||||
default='list',
|
||||
help='The type of output to generate from this script. '
|
||||
'Options are "list" for a list of filenames, "script" '
|
||||
'for a single script containing the contents of all the '
|
||||
'file, "deps" to generate a deps.js file for all '
|
||||
'paths, or "compiled" to produce compiled output with '
|
||||
'the Closure compiler.')
|
||||
parser.add_option('-c',
|
||||
'--compiler_jar',
|
||||
dest='compiler_jar',
|
||||
action='store',
|
||||
help='The location of the Closure compiler .jar file.')
|
||||
parser.add_option('-f',
|
||||
'--compiler_flag',
|
||||
'--compiler_flags', # for backwards compatability
|
||||
dest='compiler_flags',
|
||||
action='append',
|
||||
help='Additional flag to pass to the Closure compiler. '
|
||||
'May be specified multiple times to pass multiple flags.')
|
||||
parser.add_option('--output_file',
|
||||
dest='output_file',
|
||||
action='store',
|
||||
help=('If specified, write output to this path instead of '
|
||||
'writing to standard output.'))
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
search_paths = GetPathsFromOptions(options)
|
||||
|
||||
if options.output_file:
|
||||
out = open(options.output_file, 'w')
|
||||
else:
|
||||
out = sys.stdout
|
||||
|
||||
if options.output_mode == 'deps':
|
||||
result = PrintDeps(search_paths, ExpandDirectories(options.deps or []), out)
|
||||
if not result:
|
||||
logging.error('Could not find Closure Library in the specified paths')
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
inputs = GetInputsFromOptions(options)
|
||||
|
||||
logging.info('Finding Closure dependencies...')
|
||||
deps = CalculateDependencies(search_paths, inputs)
|
||||
output_mode = options.output_mode
|
||||
|
||||
if output_mode == 'script':
|
||||
PrintScript(deps, out)
|
||||
elif output_mode == 'list':
|
||||
# Just print out a dep per line
|
||||
for dep in deps:
|
||||
PrintLine(dep, out)
|
||||
elif output_mode == 'compiled':
|
||||
# Make sure a .jar is specified.
|
||||
if not options.compiler_jar:
|
||||
logging.error('--compiler_jar flag must be specified if --output is '
|
||||
'"compiled"')
|
||||
sys.exit(1)
|
||||
|
||||
# User friendly version check.
|
||||
if distutils and not (distutils.version.LooseVersion(GetJavaVersion()) >
|
||||
distutils.version.LooseVersion('1.6')):
|
||||
logging.error('Closure Compiler requires Java 1.6 or higher.')
|
||||
logging.error('Please visit http://www.java.com/getjava')
|
||||
sys.exit(1)
|
||||
|
||||
Compile(options.compiler_jar, deps, out, options.compiler_flags)
|
||||
|
||||
else:
|
||||
logging.error('Invalid value for --output flag.')
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
"""Automatically converts codebases over to goog.scope.
|
||||
|
||||
Usage:
|
||||
cd path/to/my/dir;
|
||||
../../../../javascript/closure/bin/scopify.py
|
||||
|
||||
Scans every file in this directory, recursively. Looks for existing
|
||||
goog.scope calls, and goog.require'd symbols. If it makes sense to
|
||||
generate a goog.scope call for the file, then we will do so, and
|
||||
try to auto-generate some aliases based on the goog.require'd symbols.
|
||||
|
||||
Known Issues:
|
||||
|
||||
When a file is goog.scope'd, the file contents will be indented +2.
|
||||
This may put some lines over 80 chars. These will need to be fixed manually.
|
||||
|
||||
We will only try to create aliases for capitalized names. We do not check
|
||||
to see if those names will conflict with any existing locals.
|
||||
|
||||
This creates merge conflicts for every line of every outstanding change.
|
||||
If you intend to run this on your codebase, make sure your team members
|
||||
know. Better yet, send them this script so that they can scopify their
|
||||
outstanding changes and "accept theirs".
|
||||
|
||||
When an alias is "captured", it can no longer be stubbed out for testing.
|
||||
Run your tests.
|
||||
|
||||
"""
|
||||
|
||||
__author__ = 'nicksantos@google.com (Nick Santos)'
|
||||
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
||||
REQUIRES_RE = re.compile(r"goog.require\('([^']*)'\)")
|
||||
|
||||
# Edit this manually if you want something to "always" be aliased.
|
||||
# TODO(nicksantos): Add a flag for this.
|
||||
DEFAULT_ALIASES = {}
|
||||
|
||||
def Transform(lines):
|
||||
"""Converts the contents of a file into javascript that uses goog.scope.
|
||||
|
||||
Arguments:
|
||||
lines: A list of strings, corresponding to each line of the file.
|
||||
Returns:
|
||||
A new list of strings, or None if the file was not modified.
|
||||
"""
|
||||
requires = []
|
||||
|
||||
# Do an initial scan to be sure that this file can be processed.
|
||||
for line in lines:
|
||||
# Skip this file if it has already been scopified.
|
||||
if line.find('goog.scope') != -1:
|
||||
return None
|
||||
|
||||
# If there are any global vars or functions, then we also have
|
||||
# to skip the whole file. We might be able to deal with this
|
||||
# more elegantly.
|
||||
if line.find('var ') == 0 or line.find('function ') == 0:
|
||||
return None
|
||||
|
||||
for match in REQUIRES_RE.finditer(line):
|
||||
requires.append(match.group(1))
|
||||
|
||||
if len(requires) == 0:
|
||||
return None
|
||||
|
||||
# Backwards-sort the requires, so that when one is a substring of another,
|
||||
# we match the longer one first.
|
||||
for val in DEFAULT_ALIASES.values():
|
||||
if requires.count(val) == 0:
|
||||
requires.append(val)
|
||||
|
||||
requires.sort()
|
||||
requires.reverse()
|
||||
|
||||
# Generate a map of requires to their aliases
|
||||
aliases_to_globals = DEFAULT_ALIASES.copy()
|
||||
for req in requires:
|
||||
index = req.rfind('.')
|
||||
if index == -1:
|
||||
alias = req
|
||||
else:
|
||||
alias = req[(index + 1):]
|
||||
|
||||
# Don't scopify lowercase namespaces, because they may conflict with
|
||||
# local variables.
|
||||
if alias[0].isupper():
|
||||
aliases_to_globals[alias] = req
|
||||
|
||||
aliases_to_matchers = {}
|
||||
globals_to_aliases = {}
|
||||
for alias, symbol in aliases_to_globals.items():
|
||||
globals_to_aliases[symbol] = alias
|
||||
aliases_to_matchers[alias] = re.compile('\\b%s\\b' % symbol)
|
||||
|
||||
# Insert a goog.scope that aliases all required symbols.
|
||||
result = []
|
||||
|
||||
START = 0
|
||||
SEEN_REQUIRES = 1
|
||||
IN_SCOPE = 2
|
||||
|
||||
mode = START
|
||||
aliases_used = set()
|
||||
insertion_index = None
|
||||
num_blank_lines = 0
|
||||
for line in lines:
|
||||
if mode == START:
|
||||
result.append(line)
|
||||
|
||||
if re.search(REQUIRES_RE, line):
|
||||
mode = SEEN_REQUIRES
|
||||
|
||||
elif mode == SEEN_REQUIRES:
|
||||
if (line and
|
||||
not re.search(REQUIRES_RE, line) and
|
||||
not line.isspace()):
|
||||
# There should be two blank lines before goog.scope
|
||||
result += ['\n'] * 2
|
||||
result.append('goog.scope(function() {\n')
|
||||
insertion_index = len(result)
|
||||
result += ['\n'] * num_blank_lines
|
||||
mode = IN_SCOPE
|
||||
elif line.isspace():
|
||||
# Keep track of the number of blank lines before each block of code so
|
||||
# that we can move them after the goog.scope line if necessary.
|
||||
num_blank_lines += 1
|
||||
else:
|
||||
# Print the blank lines we saw before this code block
|
||||
result += ['\n'] * num_blank_lines
|
||||
num_blank_lines = 0
|
||||
result.append(line)
|
||||
|
||||
if mode == IN_SCOPE:
|
||||
for symbol in requires:
|
||||
if not symbol in globals_to_aliases:
|
||||
continue
|
||||
|
||||
alias = globals_to_aliases[symbol]
|
||||
matcher = aliases_to_matchers[alias]
|
||||
for match in matcher.finditer(line):
|
||||
# Check to make sure we're not in a string.
|
||||
# We do this by being as conservative as possible:
|
||||
# if there are any quote or double quote characters
|
||||
# before the symbol on this line, then bail out.
|
||||
before_symbol = line[:match.start(0)]
|
||||
if before_symbol.count('"') > 0 or before_symbol.count("'") > 0:
|
||||
continue
|
||||
|
||||
line = line.replace(match.group(0), alias)
|
||||
aliases_used.add(alias)
|
||||
|
||||
if line.isspace():
|
||||
# Truncate all-whitespace lines
|
||||
result.append('\n')
|
||||
else:
|
||||
result.append(line)
|
||||
|
||||
if len(aliases_used):
|
||||
aliases_used = [alias for alias in aliases_used]
|
||||
aliases_used.sort()
|
||||
aliases_used.reverse()
|
||||
for alias in aliases_used:
|
||||
symbol = aliases_to_globals[alias]
|
||||
result.insert(insertion_index,
|
||||
'var %s = %s;\n' % (alias, symbol))
|
||||
result.append('}); // goog.scope\n')
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
def TransformFileAt(path):
|
||||
"""Converts a file into javascript that uses goog.scope.
|
||||
|
||||
Arguments:
|
||||
path: A path to a file.
|
||||
"""
|
||||
f = open(path)
|
||||
lines = Transform(f.readlines())
|
||||
if lines:
|
||||
f = open(path, 'w')
|
||||
for l in lines:
|
||||
f.write(l)
|
||||
f.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = sys.argv[1:]
|
||||
if not len(args):
|
||||
args = '.'
|
||||
|
||||
for file_name in args:
|
||||
if os.path.isdir(file_name):
|
||||
for root, dirs, files in os.walk(file_name):
|
||||
for name in files:
|
||||
if name.endswith('.js') and \
|
||||
not os.path.islink(os.path.join(root, name)):
|
||||
TransformFileAt(os.path.join(root, name))
|
||||
else:
|
||||
if file_name.endswith('.js') and \
|
||||
not os.path.islink(file_name):
|
||||
TransformFileAt(file_name)
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue