OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright 2009 The Closure Library Authors. All Rights Reserved. |
| 4 # |
| 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 |
| 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # |
| 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. |
| 16 |
| 17 |
| 18 """Generates out a Closure deps.js file given a list of JavaScript sources. |
| 19 |
| 20 Paths can be specified as arguments or (more commonly) specifying trees |
| 21 with the flags (call with --help for descriptions). |
| 22 |
| 23 Usage: depswriter.py [path/to/js1.js [path/to/js2.js] ...] |
| 24 """ |
| 25 |
| 26 import logging |
| 27 import optparse |
| 28 import os |
| 29 import posixpath |
| 30 import shlex |
| 31 import sys |
| 32 |
| 33 import source |
| 34 import treescan |
| 35 |
| 36 |
| 37 __author__ = 'nnaze@google.com (Nathan Naze)' |
| 38 |
| 39 |
| 40 def MakeDepsFile(source_map): |
| 41 """Make a generated deps file. |
| 42 |
| 43 Args: |
| 44 source_map: A dict map of the source path to source.Source object. |
| 45 |
| 46 Returns: |
| 47 str, A generated deps file source. |
| 48 """ |
| 49 |
| 50 # Write in path alphabetical order |
| 51 paths = sorted(source_map.keys()) |
| 52 |
| 53 lines = [] |
| 54 |
| 55 for path in paths: |
| 56 js_source = source_map[path] |
| 57 |
| 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)) |
| 61 |
| 62 return ''.join(lines) |
| 63 |
| 64 |
| 65 def _GetDepsLine(path, js_source): |
| 66 """Get a deps.js file string for a source.""" |
| 67 |
| 68 provides = sorted(js_source.provides) |
| 69 requires = sorted(js_source.requires) |
| 70 |
| 71 return 'goog.addDependency(\'%s\', %s, %s);\n' % (path, provides, requires) |
| 72 |
| 73 |
| 74 def _GetOptionsParser(): |
| 75 """Get the options parser.""" |
| 76 |
| 77 parser = optparse.OptionParser(__doc__) |
| 78 |
| 79 parser.add_option('--output_file', |
| 80 dest='output_file', |
| 81 action='store', |
| 82 help=('If specified, write output to this path instead of ' |
| 83 'writing to standard output.')) |
| 84 parser.add_option('--root', |
| 85 dest='roots', |
| 86 default=[], |
| 87 action='append', |
| 88 help='A root directory to scan for JS source files. ' |
| 89 'Paths of JS files in generated deps file will be ' |
| 90 'relative to this path. This flag may be specified ' |
| 91 'multiple times.') |
| 92 parser.add_option('--root_with_prefix', |
| 93 dest='roots_with_prefix', |
| 94 default=[], |
| 95 action='append', |
| 96 help='A root directory to scan for JS source files, plus ' |
| 97 'a prefix (if either contains a space, surround with ' |
| 98 'quotes). Paths in generated deps file will be relative ' |
| 99 'to the root, but preceded by the prefix. This flag ' |
| 100 'may be specified multiple times.') |
| 101 parser.add_option('--path_with_depspath', |
| 102 dest='paths_with_depspath', |
| 103 default=[], |
| 104 action='append', |
| 105 help='A path to a source file and an alternate path to ' |
| 106 'the file in the generated deps file (if either contains ' |
| 107 'a space, surround with whitespace). This flag may be ' |
| 108 'specified multiple times.') |
| 109 return parser |
| 110 |
| 111 |
| 112 def _NormalizePathSeparators(path): |
| 113 """Replaces OS-specific path separators with POSIX-style slashes. |
| 114 |
| 115 Args: |
| 116 path: str, A file path. |
| 117 |
| 118 Returns: |
| 119 str, The path with any OS-specific path separators (such as backslash on |
| 120 Windows) replaced with URL-compatible forward slashes. A no-op on systems |
| 121 that use POSIX paths. |
| 122 """ |
| 123 return path.replace(os.sep, posixpath.sep) |
| 124 |
| 125 |
| 126 def _GetRelativePathToSourceDict(root, prefix=''): |
| 127 """Scans a top root directory for .js sources. |
| 128 |
| 129 Args: |
| 130 root: str, Root directory. |
| 131 prefix: str, Prefix for returned paths. |
| 132 |
| 133 Returns: |
| 134 dict, A map of relative paths (with prefix, if given), to source.Source |
| 135 objects. |
| 136 """ |
| 137 # Remember and restore the cwd when we're done. We work from the root so |
| 138 # that paths are relative from the root. |
| 139 start_wd = os.getcwd() |
| 140 os.chdir(root) |
| 141 |
| 142 path_to_source = {} |
| 143 for path in treescan.ScanTreeForJsFiles('.'): |
| 144 prefixed_path = _NormalizePathSeparators(os.path.join(prefix, path)) |
| 145 path_to_source[prefixed_path] = source.Source(source.GetFileContents(path)) |
| 146 |
| 147 os.chdir(start_wd) |
| 148 |
| 149 return path_to_source |
| 150 |
| 151 |
| 152 def _GetPair(s): |
| 153 """Return a string as a shell-parsed tuple. Two values expected.""" |
| 154 try: |
| 155 # shlex uses '\' as an escape character, so they must be escaped. |
| 156 s = s.replace('\\', '\\\\') |
| 157 first, second = shlex.split(s) |
| 158 return (first, second) |
| 159 except: |
| 160 raise Exception('Unable to parse input line as a pair: %s' % s) |
| 161 |
| 162 |
| 163 def main(): |
| 164 """CLI frontend to MakeDepsFile.""" |
| 165 logging.basicConfig(format=(sys.argv[0] + ': %(message)s'), |
| 166 level=logging.INFO) |
| 167 options, args = _GetOptionsParser().parse_args() |
| 168 |
| 169 path_to_source = {} |
| 170 |
| 171 # Roots without prefixes |
| 172 for root in options.roots: |
| 173 path_to_source.update(_GetRelativePathToSourceDict(root)) |
| 174 |
| 175 # Roots with prefixes |
| 176 for root_and_prefix in options.roots_with_prefix: |
| 177 root, prefix = _GetPair(root_and_prefix) |
| 178 path_to_source.update(_GetRelativePathToSourceDict(root, prefix=prefix)) |
| 179 |
| 180 # Source paths |
| 181 for path in args: |
| 182 path_to_source[path] = source.Source(source.GetFileContents(path)) |
| 183 |
| 184 # Source paths with alternate deps paths |
| 185 for path_with_depspath in options.paths_with_depspath: |
| 186 srcpath, depspath = _GetPair(path_with_depspath) |
| 187 path_to_source[depspath] = source.Source(source.GetFileContents(srcpath)) |
| 188 |
| 189 # Make our output pipe. |
| 190 if options.output_file: |
| 191 out = open(options.output_file, 'w') |
| 192 else: |
| 193 out = sys.stdout |
| 194 |
| 195 out.write('// This file was autogenerated by %s.\n' % sys.argv[0]) |
| 196 out.write('// Please do not edit.\n') |
| 197 |
| 198 out.write(MakeDepsFile(path_to_source)) |
| 199 |
| 200 |
| 201 if __name__ == '__main__': |
| 202 main() |
OLD | NEW |