3 # Copyright 2009 The Closure Library Authors. All Rights Reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS-IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 """Generates out a Closure deps.js file given a list of JavaScript sources.
20 Paths can be specified as arguments or (more commonly) specifying trees
21 with the flags (call with --help for descriptions).
23 Usage: depswriter.py [path/to/js1.js [path/to/js2.js] ...]
37 __author__
= 'nnaze@google.com (Nathan Naze)'
40 def MakeDepsFile(source_map
):
41 """Make a generated deps file.
44 source_map: A dict map of the source path to source.Source object.
47 str, A generated deps file source.
50 # Write in path alphabetical order
51 paths
= sorted(source_map
.keys())
56 js_source
= source_map
[path
]
58 # We don't need to add entries that don't provide anything.
59 if js_source
.provides
:
60 lines
.append(_GetDepsLine(path
, js_source
))
65 def _GetDepsLine(path
, js_source
):
66 """Get a deps.js file string for a source."""
68 provides
= sorted(js_source
.provides
)
69 requires
= sorted(js_source
.requires
)
70 module
= 'true' if js_source
.is_goog_module
else 'false'
72 return 'goog.addDependency(\'%s\', %s, %s, %s);\n' % (
73 path
, provides
, requires
, module
)
76 def _GetOptionsParser():
77 """Get the options parser."""
79 parser
= optparse
.OptionParser(__doc__
)
81 parser
.add_option('--output_file',
84 help=('If specified, write output to this path instead of '
85 'writing to standard output.'))
86 parser
.add_option('--root',
90 help='A root directory to scan for JS source files. '
91 'Paths of JS files in generated deps file will be '
92 'relative to this path. This flag may be specified '
94 parser
.add_option('--root_with_prefix',
95 dest
='roots_with_prefix',
98 help='A root directory to scan for JS source files, plus '
99 'a prefix (if either contains a space, surround with '
100 'quotes). Paths in generated deps file will be relative '
101 'to the root, but preceded by the prefix. This flag '
102 'may be specified multiple times.')
103 parser
.add_option('--path_with_depspath',
104 dest
='paths_with_depspath',
107 help='A path to a source file and an alternate path to '
108 'the file in the generated deps file (if either contains '
109 'a space, surround with whitespace). This flag may be '
110 'specified multiple times.')
114 def _NormalizePathSeparators(path
):
115 """Replaces OS-specific path separators with POSIX-style slashes.
118 path: str, A file path.
121 str, The path with any OS-specific path separators (such as backslash on
122 Windows) replaced with URL-compatible forward slashes. A no-op on systems
123 that use POSIX paths.
125 return path
.replace(os
.sep
, posixpath
.sep
)
128 def _GetRelativePathToSourceDict(root
, prefix
=''):
129 """Scans a top root directory for .js sources.
132 root: str, Root directory.
133 prefix: str, Prefix for returned paths.
136 dict, A map of relative paths (with prefix, if given), to source.Source
139 # Remember and restore the cwd when we're done. We work from the root so
140 # that paths are relative from the root.
141 start_wd
= os
.getcwd()
145 for path
in treescan
.ScanTreeForJsFiles('.'):
146 prefixed_path
= _NormalizePathSeparators(os
.path
.join(prefix
, path
))
147 path_to_source
[prefixed_path
] = source
.Source(source
.GetFileContents(path
))
151 return path_to_source
155 """Return a string as a shell-parsed tuple. Two values expected."""
157 # shlex uses '\' as an escape character, so they must be escaped.
158 s
= s
.replace('\\', '\\\\')
159 first
, second
= shlex
.split(s
)
160 return (first
, second
)
162 raise Exception('Unable to parse input line as a pair: %s' % s
)
166 """CLI frontend to MakeDepsFile."""
167 logging
.basicConfig(format
=(sys
.argv
[0] + ': %(message)s'),
169 options
, args
= _GetOptionsParser().parse_args()
173 # Roots without prefixes
174 for root
in options
.roots
:
175 path_to_source
.update(_GetRelativePathToSourceDict(root
))
177 # Roots with prefixes
178 for root_and_prefix
in options
.roots_with_prefix
:
179 root
, prefix
= _GetPair(root_and_prefix
)
180 path_to_source
.update(_GetRelativePathToSourceDict(root
, prefix
=prefix
))
184 path_to_source
[path
] = source
.Source(source
.GetFileContents(path
))
186 # Source paths with alternate deps paths
187 for path_with_depspath
in options
.paths_with_depspath
:
188 srcpath
, depspath
= _GetPair(path_with_depspath
)
189 path_to_source
[depspath
] = source
.Source(source
.GetFileContents(srcpath
))
191 # Make our output pipe.
192 if options
.output_file
:
193 out
= open(options
.output_file
, 'w')
197 out
.write('// This file was autogenerated by %s.\n' % sys
.argv
[0])
198 out
.write('// Please do not edit.\n')
200 out
.write(MakeDepsFile(path_to_source
))
203 if __name__
== '__main__':