OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2013 The Chromium Authors. All rights reserved. | 3 # Copyright 2013 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Instruments classes and jar files. | 7 """Instruments classes and jar files. |
8 | 8 |
9 This script corresponds to the 'emma_instr' action in the java build process. | 9 This script corresponds to the 'emma_instr' action in the java build process. |
10 Depending on whether emma_instrument is set, the 'emma_instr' action will either | 10 Depending on whether emma_instrument is set, the 'emma_instr' action will either |
(...skipping 14 matching lines...) Expand all Loading... | |
25 import tempfile | 25 import tempfile |
26 | 26 |
27 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) | 27 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) |
28 from pylib.utils import command_option_parser | 28 from pylib.utils import command_option_parser |
29 | 29 |
30 from util import build_utils | 30 from util import build_utils |
31 | 31 |
32 | 32 |
33 def _AddCommonOptions(option_parser): | 33 def _AddCommonOptions(option_parser): |
34 """Adds common options to |option_parser|.""" | 34 """Adds common options to |option_parser|.""" |
35 build_utils.AddDepfileOption(option_parser) | |
35 option_parser.add_option('--input-path', | 36 option_parser.add_option('--input-path', |
36 help=('Path to input file(s). Either the classes ' | 37 help=('Path to input file(s). Either the classes ' |
37 'directory, or the path to a jar.')) | 38 'directory, or the path to a jar.')) |
38 option_parser.add_option('--output-path', | 39 option_parser.add_option('--output-path', |
39 help=('Path to output final file(s) to. Either the ' | 40 help=('Path to output final file(s) to. Either the ' |
40 'final classes directory, or the directory in ' | 41 'final classes directory, or the directory in ' |
41 'which to place the instrumented/copied jar.')) | 42 'which to place the instrumented/copied jar.')) |
42 option_parser.add_option('--stamp', help='Path to touch when done.') | 43 option_parser.add_option('--stamp', help='Path to touch when done.') |
43 option_parser.add_option('--coverage-file', | 44 option_parser.add_option('--coverage-file', |
44 help='File to create with coverage metadata.') | 45 help='File to create with coverage metadata.') |
45 option_parser.add_option('--sources-file', | 46 option_parser.add_option('--sources-list-file', |
46 help='File to create with the list of sources.') | 47 help='File to create with the list of sources.') |
47 | 48 |
48 | 49 |
49 def _AddInstrumentOptions(option_parser): | 50 def _AddInstrumentOptions(option_parser): |
50 """Adds options related to instrumentation to |option_parser|.""" | 51 """Adds options related to instrumentation to |option_parser|.""" |
51 _AddCommonOptions(option_parser) | 52 _AddCommonOptions(option_parser) |
52 option_parser.add_option('--sources', | 53 option_parser.add_option('--source-dirs', |
53 help='Space separated list of sources.') | 54 help='Space separated list of source directories. ' |
55 'source-files should not be specified if ' | |
56 'source-dirs is specified') | |
57 option_parser.add_option('--source-files', | |
58 help='Space separated list of source files. ' | |
59 'source-dirs should not be specified if ' | |
60 'source-files is specified') | |
54 option_parser.add_option('--src-root', | 61 option_parser.add_option('--src-root', |
55 help='Root of the src repository.') | 62 help='Root of the src repository.') |
56 option_parser.add_option('--emma-jar', | 63 option_parser.add_option('--emma-jar', |
57 help='Path to emma.jar.') | 64 help='Path to emma.jar.') |
58 option_parser.add_option( | 65 option_parser.add_option( |
59 '--filter-string', default='', | 66 '--filter-string', default='', |
60 help=('Filter string consisting of a list of inclusion/exclusion ' | 67 help=('Filter string consisting of a list of inclusion/exclusion ' |
61 'patterns separated with whitespace and/or comma.')) | 68 'patterns separated with whitespace and/or comma.')) |
62 | 69 |
63 | 70 |
64 def _RunCopyCommand(_command, options, _, option_parser): | 71 def _RunCopyCommand(_command, options, _, option_parser): |
65 """Copies the jar from input to output locations. | 72 """Copies the jar from input to output locations. |
66 | 73 |
67 Also removes any old coverage/sources file. | 74 Also removes any old coverage/sources file. |
68 | 75 |
69 Args: | 76 Args: |
70 command: String indicating the command that was received to trigger | 77 command: String indicating the command that was received to trigger |
71 this function. | 78 this function. |
72 options: optparse options dictionary. | 79 options: optparse options dictionary. |
73 args: List of extra args from optparse. | 80 args: List of extra args from optparse. |
74 option_parser: optparse.OptionParser object. | 81 option_parser: optparse.OptionParser object. |
75 | 82 |
76 Returns: | 83 Returns: |
77 An exit code. | 84 An exit code. |
78 """ | 85 """ |
79 if not (options.input_path and options.output_path and | 86 if not (options.input_path and options.output_path and |
80 options.coverage_file and options.sources_file): | 87 options.coverage_file and options.sources_list_file): |
81 option_parser.error('All arguments are required.') | 88 option_parser.error('All arguments are required.') |
82 | 89 |
83 coverage_file = os.path.join(os.path.dirname(options.output_path), | 90 if os.path.exists(options.coverage_file): |
84 options.coverage_file) | 91 os.remove(options.coverage_file) |
85 sources_file = os.path.join(os.path.dirname(options.output_path), | 92 if os.path.exists(options.sources_list_file): |
86 options.sources_file) | 93 os.remove(options.sources_list_file) |
87 if os.path.exists(coverage_file): | |
88 os.remove(coverage_file) | |
89 if os.path.exists(sources_file): | |
90 os.remove(sources_file) | |
91 | 94 |
92 shutil.copy(options.input_path, options.output_path) | 95 shutil.copy(options.input_path, options.output_path) |
93 | 96 |
94 if options.stamp: | 97 if options.stamp: |
95 build_utils.Touch(options.stamp) | 98 build_utils.Touch(options.stamp) |
96 | 99 |
100 if options.depfile: | |
101 build_utils.WriteDepfile(options.depfile, | |
102 build_utils.GetPythonDependencies()) | |
97 | 103 |
98 def _CreateSourcesFile(sources_string, sources_file, src_root): | 104 |
99 """Adds all normalized source directories to |sources_file|. | 105 def _GetSourceDirsFromSourceFiles(source_files_string): |
106 """Returns list of directories for the files in |source_files_string|. | |
100 | 107 |
101 Args: | 108 Args: |
102 sources_string: String generated from gyp containing the list of sources. | 109 source_files_string: String generated from GN or GYP containing the list |
103 sources_file: File into which to write the JSON list of sources. | 110 of source files. |
111 | |
112 Returns: | |
113 List of source directories. | |
114 """ | |
115 source_files = build_utils.ParseGypList(source_files_string) | |
116 source_dirs = set() | |
agrieve
2015/11/18 15:30:46
nit: More concise: return list(set(os.path.dirname
pkotwicz
2015/11/18 15:59:28
It might be because I am relatively new to Python,
agrieve
2015/11/18 16:06:18
In general, List comprehensions are preferred when
pkotwicz
2015/11/18 17:03:02
According to the Google style guide, "List compreh
| |
117 for source_file in source_files: | |
118 source_dirs.add(os.path.dirname(source_file)) | |
119 return list(source_dirs) | |
120 | |
121 | |
122 def _CreateSourcesListFile(source_dirs, sources_list_file, src_root): | |
123 """Adds all normalized source directories to |sources_list_file|. | |
124 | |
125 Args: | |
126 source_dirs: List of source directories. | |
127 sources_list_file: File into which to write the JSON list of sources. | |
104 src_root: Root which sources added to the file should be relative to. | 128 src_root: Root which sources added to the file should be relative to. |
105 | 129 |
106 Returns: | 130 Returns: |
107 An exit code. | 131 An exit code. |
108 """ | 132 """ |
109 src_root = os.path.abspath(src_root) | 133 src_root = os.path.abspath(src_root) |
110 sources = build_utils.ParseGypList(sources_string) | |
111 relative_sources = [] | 134 relative_sources = [] |
112 for s in sources: | 135 for s in source_dirs: |
113 abs_source = os.path.abspath(s) | 136 abs_source = os.path.abspath(s) |
114 if abs_source[:len(src_root)] != src_root: | 137 if abs_source[:len(src_root)] != src_root: |
115 print ('Error: found source directory not under repository root: %s %s' | 138 print ('Error: found source directory not under repository root: %s %s' |
116 % (abs_source, src_root)) | 139 % (abs_source, src_root)) |
117 return 1 | 140 return 1 |
118 rel_source = os.path.relpath(abs_source, src_root) | 141 rel_source = os.path.relpath(abs_source, src_root) |
119 | 142 |
120 relative_sources.append(rel_source) | 143 relative_sources.append(rel_source) |
121 | 144 |
122 with open(sources_file, 'w') as f: | 145 with open(sources_list_file, 'w') as f: |
123 json.dump(relative_sources, f) | 146 json.dump(relative_sources, f) |
124 | 147 |
125 | 148 |
126 def _RunInstrumentCommand(_command, options, _, option_parser): | 149 def _RunInstrumentCommand(_command, options, _, option_parser): |
127 """Instruments jar files using EMMA. | 150 """Instruments jar files using EMMA. |
128 | 151 |
129 Args: | 152 Args: |
130 command: String indicating the command that was received to trigger | 153 command: String indicating the command that was received to trigger |
131 this function. | 154 this function. |
132 options: optparse options dictionary. | 155 options: optparse options dictionary. |
133 args: List of extra args from optparse. | 156 args: List of extra args from optparse. |
134 option_parser: optparse.OptionParser object. | 157 option_parser: optparse.OptionParser object. |
135 | 158 |
136 Returns: | 159 Returns: |
137 An exit code. | 160 An exit code. |
138 """ | 161 """ |
139 if not (options.input_path and options.output_path and | 162 if not (options.input_path and options.output_path and |
140 options.coverage_file and options.sources_file and options.sources and | 163 options.coverage_file and options.sources_list_file and |
164 (options.source_files or options.source_dirs) and | |
141 options.src_root and options.emma_jar): | 165 options.src_root and options.emma_jar): |
142 option_parser.error('All arguments are required.') | 166 option_parser.error('All arguments are required.') |
143 | 167 |
144 coverage_file = os.path.join(os.path.dirname(options.output_path), | 168 if os.path.exists(options.coverage_file): |
145 options.coverage_file) | 169 os.remove(options.coverage_file) |
146 sources_file = os.path.join(os.path.dirname(options.output_path), | |
147 options.sources_file) | |
148 if os.path.exists(coverage_file): | |
149 os.remove(coverage_file) | |
150 temp_dir = tempfile.mkdtemp() | 170 temp_dir = tempfile.mkdtemp() |
151 try: | 171 try: |
152 cmd = ['java', '-cp', options.emma_jar, | 172 cmd = ['java', '-cp', options.emma_jar, |
153 'emma', 'instr', | 173 'emma', 'instr', |
154 '-ip', options.input_path, | 174 '-ip', options.input_path, |
155 '-ix', options.filter_string, | 175 '-ix', options.filter_string, |
156 '-d', temp_dir, | 176 '-d', temp_dir, |
157 '-out', coverage_file, | 177 '-out', options.coverage_file, |
158 '-m', 'fullcopy'] | 178 '-m', 'fullcopy'] |
159 build_utils.CheckOutput(cmd) | 179 build_utils.CheckOutput(cmd) |
160 | 180 |
161 for jar in os.listdir(os.path.join(temp_dir, 'lib')): | 181 temp_jar_dir = os.path.join(temp_dir, 'lib') |
162 shutil.copy(os.path.join(temp_dir, 'lib', jar), | 182 jars = os.listdir(temp_jar_dir) |
163 options.output_path) | 183 if len(jars) != 1: |
184 print('Error: multiple output files in: %s' % (temp_jar_dir)) | |
agrieve
2015/11/18 15:30:46
you should raise an exception rather than explicit
pkotwicz
2015/11/18 15:59:28
This way of handling exceptions is consistent with
| |
185 return 1 | |
186 | |
187 shutil.copy(os.path.join(temp_jar_dir, jars[0]), options.output_path) | |
164 finally: | 188 finally: |
165 shutil.rmtree(temp_dir) | 189 shutil.rmtree(temp_dir) |
166 | 190 |
167 _CreateSourcesFile(options.sources, sources_file, options.src_root) | 191 source_dirs = [] |
agrieve
2015/11/18 15:30:46
no need to initialize since it's re-assigned in bo
| |
192 if options.source_dirs: | |
193 source_dirs = build_utils.ParseGypList(options.source_dirs) | |
194 else: | |
195 source_dirs = _GetSourceDirsFromSourceFiles(options.source_files) | |
196 _CreateSourcesListFile(source_dirs, options.sources_list_file, | |
197 options.src_root) | |
agrieve
2015/11/18 15:30:46
nit: indent is off
pkotwicz
2015/11/18 15:59:28
I am confused. There is an '_' before the 'C" in _
| |
168 | 198 |
169 if options.stamp: | 199 if options.stamp: |
170 build_utils.Touch(options.stamp) | 200 build_utils.Touch(options.stamp) |
171 | 201 |
202 if options.depfile: | |
203 build_utils.WriteDepfile(options.depfile, | |
204 build_utils.GetPythonDependencies()) | |
205 | |
172 return 0 | 206 return 0 |
173 | 207 |
174 | 208 |
175 CommandFunctionTuple = collections.namedtuple( | 209 CommandFunctionTuple = collections.namedtuple( |
176 'CommandFunctionTuple', ['add_options_func', 'run_command_func']) | 210 'CommandFunctionTuple', ['add_options_func', 'run_command_func']) |
177 VALID_COMMANDS = { | 211 VALID_COMMANDS = { |
178 'copy': CommandFunctionTuple(_AddCommonOptions, | 212 'copy': CommandFunctionTuple(_AddCommonOptions, |
179 _RunCopyCommand), | 213 _RunCopyCommand), |
180 'instrument_jar': CommandFunctionTuple(_AddInstrumentOptions, | 214 'instrument_jar': CommandFunctionTuple(_AddInstrumentOptions, |
181 _RunInstrumentCommand), | 215 _RunInstrumentCommand), |
182 } | 216 } |
183 | 217 |
184 | 218 |
185 def main(): | 219 def main(): |
186 option_parser = command_option_parser.CommandOptionParser( | 220 option_parser = command_option_parser.CommandOptionParser( |
187 commands_dict=VALID_COMMANDS) | 221 commands_dict=VALID_COMMANDS) |
188 command_option_parser.ParseAndExecute(option_parser) | 222 command_option_parser.ParseAndExecute(option_parser) |
189 | 223 |
190 | 224 |
191 if __name__ == '__main__': | 225 if __name__ == '__main__': |
192 sys.exit(main()) | 226 sys.exit(main()) |
OLD | NEW |