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
5 # found in the LICENSE file.
7 """Instruments classes and jar files.
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
11 call one of the instrument commands, or the copy command.
13 Possible commands are:
14 - instrument_jar: Accepts a jar and instruments it using emma.jar.
15 - instrument_classes: Accepts a directory containing java classes and
16 instruments it using emma.jar.
17 - copy: Called when EMMA coverage is not enabled. This allows us to make
18 this a required step without necessarily instrumenting on every build.
19 Also removes any stale coverage files.
29 sys
.path
.append(os
.path
.join(os
.path
.dirname(__file__
), os
.pardir
))
30 from pylib
.utils
import command_option_parser
32 from util
import build_utils
35 def _AddCommonOptions(option_parser
):
36 """Adds common options to |option_parser|."""
37 option_parser
.add_option('--input-path',
38 help=('Path to input file(s). Either the classes '
39 'directory, or the path to a jar.'))
40 option_parser
.add_option('--output-path',
41 help=('Path to output final file(s) to. Either the '
42 'final classes directory, or the directory in '
43 'which to place the instrumented/copied jar.'))
44 option_parser
.add_option('--stamp', help='Path to touch when done.')
45 option_parser
.add_option('--coverage-file',
46 help='File to create with coverage metadata.')
47 option_parser
.add_option('--sources-file',
48 help='File to create with the list of sources.')
51 def _AddInstrumentOptions(option_parser
):
52 """Adds options related to instrumentation to |option_parser|."""
53 _AddCommonOptions(option_parser
)
54 option_parser
.add_option('--sources',
55 help='Space separated list of sources.')
56 option_parser
.add_option('--src-root',
57 help='Root of the src repository.')
58 option_parser
.add_option('--emma-jar',
59 help='Path to emma.jar.')
60 option_parser
.add_option(
61 '--filter-string', default
='',
62 help=('Filter string consisting of a list of inclusion/exclusion '
63 'patterns separated with whitespace and/or comma.'))
66 def _RunCopyCommand(_command
, options
, _
, option_parser
):
67 """Copies the jar from input to output locations.
69 Also removes any old coverage/sources file.
72 command: String indicating the command that was received to trigger
74 options: optparse options dictionary.
75 args: List of extra args from optparse.
76 option_parser: optparse.OptionParser object.
81 if not (options
.input_path
and options
.output_path
and
82 options
.coverage_file
and options
.sources_file
):
83 option_parser
.error('All arguments are required.')
85 coverage_file
= os
.path
.join(os
.path
.dirname(options
.output_path
),
86 options
.coverage_file
)
87 sources_file
= os
.path
.join(os
.path
.dirname(options
.output_path
),
89 if os
.path
.exists(coverage_file
):
90 os
.remove(coverage_file
)
91 if os
.path
.exists(sources_file
):
92 os
.remove(sources_file
)
94 if os
.path
.isdir(options
.input_path
):
95 shutil
.rmtree(options
.output_path
, ignore_errors
=True)
96 shutil
.copytree(options
.input_path
, options
.output_path
)
98 shutil
.copy(options
.input_path
, options
.output_path
)
101 build_utils
.Touch(options
.stamp
)
104 def _CreateSourcesFile(sources_string
, sources_file
, src_root
):
105 """Adds all normalized source directories to |sources_file|.
108 sources_string: String generated from gyp containing the list of sources.
109 sources_file: File into which to write the JSON list of sources.
110 src_root: Root which sources added to the file should be relative to.
115 src_root
= os
.path
.abspath(src_root
)
116 sources
= build_utils
.ParseGypList(sources_string
)
117 relative_sources
= []
119 abs_source
= os
.path
.abspath(s
)
120 if abs_source
[:len(src_root
)] != src_root
:
121 print ('Error: found source directory not under repository root: %s %s'
122 % (abs_source
, src_root
))
124 rel_source
= os
.path
.relpath(abs_source
, src_root
)
126 relative_sources
.append(rel_source
)
128 with
open(sources_file
, 'w') as f
:
129 json
.dump(relative_sources
, f
)
132 def _RunInstrumentCommand(command
, options
, _
, option_parser
):
133 """Instruments the classes/jar files using EMMA.
136 command: 'instrument_jar' or 'instrument_classes'. This distinguishes
137 whether we copy the output from the created lib/ directory, or classes/
139 options: optparse options dictionary.
140 args: List of extra args from optparse.
141 option_parser: optparse.OptionParser object.
146 if not (options
.input_path
and options
.output_path
and
147 options
.coverage_file
and options
.sources_file
and options
.sources
and
148 options
.src_root
and options
.emma_jar
):
149 option_parser
.error('All arguments are required.')
151 coverage_file
= os
.path
.join(os
.path
.dirname(options
.output_path
),
152 options
.coverage_file
)
153 sources_file
= os
.path
.join(os
.path
.dirname(options
.output_path
),
154 options
.sources_file
)
155 if os
.path
.exists(coverage_file
):
156 os
.remove(coverage_file
)
157 temp_dir
= tempfile
.mkdtemp()
159 cmd
= ['java', '-cp', options
.emma_jar
,
161 '-ip', options
.input_path
,
162 '-ix', options
.filter_string
,
164 '-out', coverage_file
,
166 build_utils
.CheckOutput(cmd
)
168 if command
== 'instrument_jar':
169 for jar
in os
.listdir(os
.path
.join(temp_dir
, 'lib')):
170 shutil
.copy(os
.path
.join(temp_dir
, 'lib', jar
),
172 else: # 'instrument_classes'
173 if os
.path
.isdir(options
.output_path
):
174 shutil
.rmtree(options
.output_path
, ignore_errors
=True)
175 shutil
.copytree(os
.path
.join(temp_dir
, 'classes'),
178 shutil
.rmtree(temp_dir
)
180 _CreateSourcesFile(options
.sources
, sources_file
, options
.src_root
)
183 build_utils
.Touch(options
.stamp
)
188 CommandFunctionTuple
= collections
.namedtuple(
189 'CommandFunctionTuple', ['add_options_func', 'run_command_func'])
191 'copy': CommandFunctionTuple(_AddCommonOptions
,
193 'instrument_jar': CommandFunctionTuple(_AddInstrumentOptions
,
194 _RunInstrumentCommand
),
195 'instrument_classes': CommandFunctionTuple(_AddInstrumentOptions
,
196 _RunInstrumentCommand
),
201 option_parser
= command_option_parser
.CommandOptionParser(
202 commands_dict
=VALID_COMMANDS
)
203 command_option_parser
.ParseAndExecute(option_parser
)
206 if __name__
== '__main__':