]>
git.proxmox.com Git - ceph.git/blob - ceph/src/arrow/cpp/build-support/iwyu/iwyu_tool.py
3 # This file has been imported into the apache source tree from
4 # the IWYU source tree as of version 0.8
5 # https://github.com/include-what-you-use/include-what-you-use/blob/master/iwyu_tool.py
6 # and corresponding license has been added:
7 # https://github.com/include-what-you-use/include-what-you-use/blob/master/LICENSE.TXT
9 # ==============================================================================
10 # LLVM Release License
11 # ==============================================================================
12 # University of Illinois/NCSA
15 # Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign.
16 # All rights reserved.
22 # University of Illinois at Urbana-Champaign
26 # Permission is hereby granted, free of charge, to any person obtaining a copy of
27 # this software and associated documentation files (the "Software"), to deal with
28 # the Software without restriction, including without limitation the rights to
29 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
30 # of the Software, and to permit persons to whom the Software is furnished to do
31 # so, subject to the following conditions:
33 # * Redistributions of source code must retain the above copyright notice,
34 # this list of conditions and the following disclaimers.
36 # * Redistributions in binary form must reproduce the above copyright notice,
37 # this list of conditions and the following disclaimers in the
38 # documentation and/or other materials provided with the distribution.
40 # * Neither the names of the LLVM Team, University of Illinois at
41 # Urbana-Champaign, nor the names of its contributors may be used to
42 # endorse or promote products derived from this Software without specific
43 # prior written permission.
45 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
47 # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48 # CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
53 """ Driver to consume a Clang compilation database and invoke IWYU.
55 Example usage with CMake:
58 $ mkdir build && cd build
59 $ CC="clang" CXX="clang++" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ...
63 $ mkdir build && cd build
64 $ cmake -DCMAKE_CXX_COMPILER="%VCINSTALLDIR%/bin/cl.exe" \
65 -DCMAKE_C_COMPILER="%VCINSTALLDIR%/VC/bin/cl.exe" \
66 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
68 $ python iwyu_tool.py -p .
70 See iwyu_tool.py -h for more details on command-line arguments.
82 logging
.basicConfig(filename
='iwyu.log')
83 LOGGER
= logging
.getLogger("iwyu")
86 def iwyu_formatter(output
):
87 """ Process iwyu's output, basically a no-op. """
88 print('\n'.join(output
))
91 CORRECT_RE
= re
.compile(r
'^\((.*?) has correct #includes/fwd-decls\)$')
92 SHOULD_ADD_RE
= re
.compile(r
'^(.*?) should add these lines:$')
93 SHOULD_REMOVE_RE
= re
.compile(r
'^(.*?) should remove these lines:$')
94 FULL_LIST_RE
= re
.compile(r
'The full include-list for (.*?):$')
95 END_RE
= re
.compile(r
'^---$')
96 LINES_RE
= re
.compile(r
'^- (.*?) // lines ([0-9]+)-[0-9]+$')
99 GENERAL
, ADD
, REMOVE
, LIST
= range(4)
102 def clang_formatter(output
):
103 """ Process iwyu's output into something clang-like. """
104 state
= (GENERAL
, None)
106 match
= CORRECT_RE
.match(line
)
108 print('%s:1:1: note: #includes/fwd-decls are correct', match
.groups(1))
110 match
= SHOULD_ADD_RE
.match(line
)
112 state
= (ADD
, match
.group(1))
114 match
= SHOULD_REMOVE_RE
.match(line
)
116 state
= (REMOVE
, match
.group(1))
118 match
= FULL_LIST_RE
.match(line
)
120 state
= (LIST
, match
.group(1))
121 elif END_RE
.match(line
):
122 state
= (GENERAL
, None)
123 elif not line
.strip():
125 elif state
[0] == GENERAL
:
127 elif state
[0] == ADD
:
128 print('%s:1:1: error: add the following line', state
[1])
130 elif state
[0] == REMOVE
:
131 match
= LINES_RE
.match(line
)
132 line_no
= match
.group(2) if match
else '1'
133 print('%s:%s:1: error: remove the following line', state
[1], line_no
)
134 print(match
.group(1))
137 DEFAULT_FORMAT
= 'iwyu'
139 'iwyu': iwyu_formatter
,
140 'clang': clang_formatter
144 def get_output(cwd
, command
):
145 """ Run the given command and return its output as a string. """
146 process
= subprocess
.Popen(command
,
149 stdout
=subprocess
.PIPE
,
150 stderr
=subprocess
.STDOUT
)
151 return process
.communicate()[0].decode("utf-8").splitlines()
154 def run_iwyu(cwd
, compile_command
, iwyu_args
, verbose
, formatter
):
155 """ Rewrite compile_command to an IWYU command, and run it. """
156 compiler
, _
, args
= compile_command
.partition(' ')
157 if compiler
.endswith('cl.exe'):
158 # If the compiler name is cl.exe, let IWYU be cl-compatible
159 clang_args
= ['--driver-mode=cl']
163 iwyu_args
= ['-Xiwyu ' + a
for a
in iwyu_args
]
164 command
= ['include-what-you-use'] + clang_args
+ iwyu_args
165 command
= '%s %s' % (' '.join(command
), args
.strip())
168 print('%s:', command
)
170 formatter(get_output(cwd
, command
))
173 def main(compilation_db_path
, source_files
, verbose
, formatter
, iwyu_args
):
175 # Canonicalize compilation database path
176 if os
.path
.isdir(compilation_db_path
):
177 compilation_db_path
= os
.path
.join(compilation_db_path
,
178 'compile_commands.json')
180 compilation_db_path
= os
.path
.realpath(compilation_db_path
)
181 if not os
.path
.isfile(compilation_db_path
):
182 print('ERROR: No such file or directory: \'%s\'', compilation_db_path
)
185 # Read compilation db from disk
186 with
open(compilation_db_path
, 'r') as fileobj
:
187 compilation_db
= json
.load(fileobj
)
190 for entry
in compilation_db
:
191 entry
['file'] = os
.path
.realpath(entry
['file'])
193 # Cross-reference source files with compilation database
194 source_files
= [os
.path
.realpath(s
) for s
in source_files
]
196 # No source files specified, analyze entire compilation database
197 entries
= compilation_db
199 # Source files specified, analyze the ones appearing in compilation db,
202 for source
in source_files
:
203 matches
= [e
for e
in compilation_db
if e
['file'] == source
]
205 entries
.extend(matches
)
207 print("{} not in compilation database".format(source
))
208 # TODO: As long as there is no complete compilation database available this check cannot be performed
210 #print('WARNING: \'%s\' not found in compilation database.', source)
214 for entry
in entries
:
215 cwd
, compile_command
= entry
['directory'], entry
['command']
216 run_iwyu(cwd
, compile_command
, iwyu_args
, verbose
, formatter
)
217 except OSError as why
:
218 print('ERROR: Failed to launch include-what-you-use: %s', why
)
225 """ Parse arguments and dispatch to main(). """
226 # This hackery is necessary to add the forwarded IWYU args to the
227 # usage and help strings.
228 def customize_usage(parser
):
229 """ Rewrite the parser's format_usage. """
230 original_format_usage
= parser
.format_usage
231 parser
.format_usage
= lambda: original_format_usage().rstrip() + \
232 ' -- [<IWYU args>]' + os
.linesep
234 def customize_help(parser
):
235 """ Rewrite the parser's format_help. """
236 original_format_help
= parser
.format_help
239 """ Customized help string, calls the adjusted format_usage. """
240 helpmsg
= original_format_help()
241 helplines
= helpmsg
.splitlines()
242 helplines
[0] = parser
.format_usage().rstrip()
243 return os
.linesep
.join(helplines
) + os
.linesep
245 parser
.format_help
= custom_help
248 parser
= argparse
.ArgumentParser(
249 description
='Include-what-you-use compilation database driver.',
250 epilog
='Assumes include-what-you-use is available on the PATH.')
251 customize_usage(parser
)
252 customize_help(parser
)
254 parser
.add_argument('-v', '--verbose', action
='store_true',
255 help='Print IWYU commands')
256 parser
.add_argument('-o', '--output-format', type=str,
257 choices
=FORMATTERS
.keys(), default
=DEFAULT_FORMAT
,
258 help='Output format (default: %s)' % DEFAULT_FORMAT
)
259 parser
.add_argument('-p', metavar
='<build-path>', required
=True,
260 help='Compilation database path', dest
='dbpath')
261 parser
.add_argument('source', nargs
='*',
262 help='Zero or more source files to run IWYU on. '
263 'Defaults to all in compilation database.')
265 def partition_args(argv
):
266 """ Split around '--' into driver args and IWYU args. """
268 double_dash
= argv
.index('--')
269 return argv
[:double_dash
], argv
[double_dash
+1:]
272 argv
, iwyu_args
= partition_args(sys
.argv
[1:])
273 args
= parser
.parse_args(argv
)
275 sys
.exit(main(args
.dbpath
, args
.source
, args
.verbose
,
276 FORMATTERS
[args
.output_format
], iwyu_args
))
279 if __name__
== '__main__':