2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
7 from checker
import Checker
as Checker
14 import simplejson
as json
18 def __init__(self
, name
, sources
, depends
=[], externs
=[]):
20 self
.sources
= sources
21 # TODO(dbeam): support depending on other modules/dependency flattening.
22 self
.depends
= depends
23 self
.externs
= externs
29 required
= ["name", "sources"]
30 assert all(r
in keys
for r
in required
), "Module missing name or sources"
32 allowed
= required
+ ["depends", "externs"]
33 assert all(k
in allowed
for k
in keys
), "Module has unknown key"
35 depends
= d
["depends"] if "depends" in d
else []
36 externs
= d
["externs"] if "externs" in d
else []
37 return Module(d
["name"], d
["sources"], depends
=depends
, externs
=externs
)
40 # TODO(dbeam): should ModuleParser be internal to ModuleCompiler or should we
41 # pass Modules into ModuleCompiler.compile()? Maybe this is fine?
42 class ModuleParser(object):
45 def __init__(self
, verbose
=False):
46 self
._verbose
= verbose
48 def parse(self
, file_path
):
49 if file_path
in self
._cache
:
50 print "(INFO) Found module file %s in the cache" % file_path
51 return self
._cache
[file_path
]
53 file = open(file_path
, "r")
54 data
= json
.load(file)
58 pretty_json
= json
.dumps(data
, indent
=2, separators
=(',', ': ')).strip()
59 print "(INFO) Layout: " + os
.linesep
+ pretty_json
+ os
.linesep
61 self
._cache
[file_path
] = [Module
.from_dict(m
) for m
in data
]
62 return self
._cache
[file_path
]
65 class ModuleCompiler(object):
69 def __init__(self
, verbose
=False):
70 self
._verbose
= verbose
72 def _debug(self
, msg
, prefix
="(INFO) ", suffix
=""):
74 print prefix
+ msg
.strip() + suffix
76 def compile(self
, module_file
):
77 self
._debug
("MODULE FILE: " + module_file
, prefix
="")
79 # NOTE: It's possible but unlikely that |_checker| or |_parser|'s verbosity
80 # isn't the same as |self._verbose| due to this class being called with
81 # verbose=False then verbose=True in the same program.
82 self
._parser
= self
._parser
or ModuleParser(verbose
=self
._verbose
)
83 self
._checker
= self
._checker
or Checker(verbose
=self
._verbose
)
85 current_dir
= os
.getcwd()
86 module_dir
= os
.path
.dirname(module_file
)
87 rel_path
= lambda f
: f
89 if current_dir
and module_dir
:
90 here_to_module_dir
= os
.path
.relpath(module_dir
, current_dir
)
91 if here_to_module_dir
:
92 rel_path
= lambda f
: os
.path
.join(here_to_module_dir
, f
)
94 modules
= self
._parser
.parse(module_file
)
97 self
._debug
("MODULE: " + m
.name
, prefix
="", suffix
=os
.linesep
)
100 depends
= [rel_path(d
) for d
in m
.depends
]
101 externs
= [rel_path(e
) for e
in m
.externs
]
102 exit_code
, _
= self
._checker
.check(rel_path(s
), depends
=depends
,
107 if s
!= m
.sources
[-1]:
108 self
._debug
(os
.linesep
, prefix
="")
111 self
._debug
(os
.linesep
, prefix
="")
115 module_compiler
= ModuleCompiler(verbose
=opts
.verbose
)
116 for module_file
in opts
.module_file
:
117 module_compiler
.compile(module_file
)
120 if __name__
== "__main__":
121 parser
= argparse
.ArgumentParser(
122 description
="Typecheck JavaScript using Closure compiler")
123 parser
.add_argument("-v", "--verbose", action
="store_true",
124 help="Show more information as this script runs")
125 parser
.add_argument("module_file", nargs
=argparse
.ONE_OR_MORE
,
126 help="Path to a modules file to check")
127 main(parser
.parse_args())