-
Notifications
You must be signed in to change notification settings - Fork 776
Expand file tree
/
Copy pathflags.py
More file actions
687 lines (557 loc) · 25.7 KB
/
flags.py
File metadata and controls
687 lines (557 loc) · 25.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
# Copyright (C) 2011-2020 ycmd contributors
#
# This file is part of ycmd.
#
# ycmd is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ycmd is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ycmd. If not, see <http://www.gnu.org/licenses/>.
import os
import inspect
from ycmd import extra_conf_store
from ycmd.utils import ( AbsolutePath,
ImportCore,
OnMac,
OnWindows,
PathsToAllParentFolders,
re,
ToUnicode,
CLANG_RESOURCE_DIR )
from ycmd.responses import NoExtraConfDetected
ycm_core = ImportCore()
# -include-pch and --sysroot= must be listed before -include and --sysroot
# respectively because the latter is a prefix of the former (and the algorithm
# checks prefixes).
INCLUDE_FLAGS = [ '-isystem', '-I', '-iquote', '-isysroot', '--sysroot',
'-gcc-toolchain', '-include-pch', '-include', '-iframework',
'-F', '-imacros', '-idirafter', '-B', '-imsvc' ]
INCLUDE_FLAGS_WIN_STYLE = [ '/I', '-imsvc' ]
PATH_FLAGS = [ '--sysroot=' ] + INCLUDE_FLAGS
# We need to remove --fcolor-diagnostics because it will cause shell escape
# sequences to show up in editors, which is bad. See Valloric/YouCompleteMe#1421
STATE_FLAGS_TO_SKIP = { '-c',
'-MP',
'-MD',
'-MMD',
'--fcolor-diagnostics' }
STATE_FLAGS_TO_SKIP_WIN_STYLE = { '/c' }
# The -M* flags spec:
# https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Preprocessor-Options.html
FILE_FLAGS_TO_SKIP = { '-MF',
'-MT',
'-MQ',
'-o',
'--serialize-diagnostics',
'--' }
# Use a regex to correctly detect c++/c language for both versioned and
# non-versioned compiler executable names suffixes
# (e.g., c++, g++, clang++, g++-4.9, clang++-3.7, c++-10.2 etc).
# See Valloric/ycmd#266
CPP_COMPILER_REGEX = re.compile( r'\+\+(-\d+(\.\d+){0,2})?$' )
# Use a regex to match all the possible forms of clang-cl or cl compiler
CL_COMPILER_REGEX = re.compile( r'(?:cl|clang-cl)(.exe)?$', re.IGNORECASE )
# List of file extensions to be considered "header" files and thus not present
# in the compilation database. The logic will try and find an associated
# "source" file (see SOURCE_EXTENSIONS below) and use the flags for that.
HEADER_EXTENSIONS = [ '.h', '.hxx', '.hpp', '.hh', '.cuh' ]
# List of file extensions which are considered "source" files for the purposes
# of heuristically locating the flags for a header file.
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.cu', '.m', '.mm' ]
EMPTY_FLAGS = {
'flags': [],
}
MAC_XCODE_TOOLCHAIN_DIR = (
'/Applications/Xcode.app/Contents/Developer/Toolchains'
'/XcodeDefault.xctoolchain' )
MAC_COMMAND_LINE_TOOLCHAIN_DIR = '/Library/Developer/CommandLineTools'
MAC_XCODE_SYSROOT = (
'/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform'
'/Developer/SDKs/MacOSX.sdk' )
MAC_COMMAND_LINE_SYSROOT = (
'/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' )
MAC_FOUNDATION_HEADERS_RELATIVE_DIR = (
'System/Library/Frameworks/Foundation.framework/Headers' )
class Flags:
"""Keeps track of the flags necessary to compile a file.
The flags are loaded from user-created python files (hereafter referred to as
'modules') that contain a method Settings( **kwargs )."""
def __init__( self ):
# We cache the flags by a tuple of filename and client data.
self.flags_for_file = {}
self.no_extra_conf_file_warning_posted = False
# We cache the compilation database for any given source directory
# Keys are directory names and values are ycm_core.CompilationDatabase
# instances or None. Value is None when it is known there is no compilation
# database to be found for the directory.
self.compilation_database_dir_map = {}
def FlagsForFile( self,
filename,
add_extra_clang_flags = True,
client_data = None ):
"""Returns a tuple describing the compiler invocation required to parse the
file |filename|. The tuple contains 2 entries:
1. A list of the compiler flags to use,
2. The name of the translation unit to parse.
Note that the second argument might not be the same as the |filename|
argument to this method in the event that the extra conf file overrides the
translation unit, e.g. in the case of a "unity" build."""
# The try-catch here is to avoid a synchronisation primitive. This method
# may be called from multiple threads, and python gives us
# 1-python-statement synchronisation for "free" (via the GIL)
try:
return self.flags_for_file[ filename, client_data ]
except KeyError:
pass
results = self._GetFlagsFromExtraConfOrDatabase( filename, client_data )
if not results.get( 'flags_ready', True ):
return [], filename
return self._ParseFlagsFromExtraConfOrDatabase( filename,
results,
add_extra_clang_flags,
client_data )
def _ParseFlagsFromExtraConfOrDatabase( self,
filename,
results,
add_extra_clang_flags,
client_data ):
if 'override_filename' in results:
filename = results[ 'override_filename' ] or filename
flags = _ExtractFlagsList( results )
if not flags:
return [], filename
sanitized_flags = PrepareFlagsForClang( flags,
filename,
add_extra_clang_flags,
ShouldAllowWinStyleFlags( flags ) )
if results.get( 'do_cache', True ):
self.flags_for_file[ filename, client_data ] = sanitized_flags, filename
return sanitized_flags, filename
def _GetFlagsFromExtraConfOrDatabase( self, filename, client_data ):
# Load the flags from the extra conf file if one is found and is not global.
module = extra_conf_store.ModuleForSourceFile( filename )
if module and not extra_conf_store.IsGlobalExtraConfModule( module ):
return _CallExtraConfFlagsForFile( module, filename, client_data )
# Load the flags from the compilation database if any.
database = self.LoadCompilationDatabase( filename )
if database:
return self._GetFlagsFromCompilationDatabase( database, filename )
# Load the flags from the global extra conf if set.
if module:
return _CallExtraConfFlagsForFile( module, filename, client_data )
# No compilation database and no extra conf found. Warn the user if not
# already warned.
if not self.no_extra_conf_file_warning_posted:
self.no_extra_conf_file_warning_posted = True
raise NoExtraConfDetected
return EMPTY_FLAGS
def Clear( self ):
self.flags_for_file.clear()
self.compilation_database_dir_map.clear()
def _GetFlagsFromCompilationDatabase( self, database, file_name ):
_, file_extension = os.path.splitext( file_name )
compilation_info = _GetCompilationInfoForFile( database,
file_name,
file_extension )
if not compilation_info:
# No flags for this file in the database.
return EMPTY_FLAGS
return {
'flags': _MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ ),
}
# Return a compilation database object for the supplied path or None if no
# compilation database is found.
def LoadCompilationDatabase( self, file_dir ):
# We search up the directory hierarchy, to first see if we have a
# compilation database already for that path, or if a compile_commands.json
# file exists in that directory.
for folder in PathsToAllParentFolders( file_dir ):
# Try/catch to synchronise access to cache
try:
return self.compilation_database_dir_map[ folder ]
except KeyError:
pass
compile_commands = os.path.join( folder, 'compile_commands.json' )
if os.path.exists( compile_commands ):
database = ycm_core.CompilationDatabase( folder )
if database.DatabaseSuccessfullyLoaded():
self.compilation_database_dir_map[ folder ] = database
return database
# Nothing was found. No compilation flags are available.
# Note: we cache the fact that none was found for this folder to speed up
# subsequent searches.
self.compilation_database_dir_map[ file_dir ] = None
return None
def _ExtractFlagsList( flags_for_file_output ):
return [ ToUnicode( x ) for x in flags_for_file_output[ 'flags' ] ]
def ShouldAllowWinStyleFlags( flags ):
if OnWindows():
# Iterate in reverse because we only care
# about the last occurrence of --driver-mode flag.
for flag in reversed( flags ):
if flag.startswith( '--driver-mode' ):
return flag == '--driver-mode=cl'
# If there was no --driver-mode flag,
# check if we are using a compiler like clang-cl.
return bool( CL_COMPILER_REGEX.search( flags[ 0 ] ) )
return False
def _CallExtraConfFlagsForFile( module, filename, client_data ):
filename = ToUnicode( filename )
if hasattr( module, 'Settings' ):
results = module.Settings( language = 'cfamily',
filename = filename,
client_data = client_data )
# For the sake of backwards compatibility, we need to first check whether the
# FlagsForFile function in the extra conf module even allows keyword args.
elif inspect.getfullargspec( module.FlagsForFile ).varkw:
results = module.FlagsForFile( filename, client_data = client_data )
else:
results = module.FlagsForFile( filename )
if not isinstance( results, dict ) or 'flags' not in results:
return EMPTY_FLAGS
results[ 'flags' ] = _MakeRelativePathsInFlagsAbsolute(
results[ 'flags' ],
results.get( 'include_paths_relative_to_dir' ) )
return results
def PrepareFlagsForClang( flags,
filename,
add_extra_clang_flags = True,
enable_windows_style_flags = False ):
flags = _AddLanguageFlagWhenAppropriate( flags, enable_windows_style_flags )
flags = _RemoveXclangFlags( flags )
flags = RemoveUnusedFlags( flags, filename, enable_windows_style_flags )
if add_extra_clang_flags:
# This flag tells libclang where to find the builtin includes.
flags.append( '-resource-dir=' + CLANG_RESOURCE_DIR )
# On Windows, parsing of templates is delayed until instantiation time.
# This makes GetType and GetParent commands fail to return the expected
# result when the cursor is in a template.
# Using the -fno-delayed-template-parsing flag disables this behavior. See
# http://clang.llvm.org/extra/PassByValueTransform.html#note-about-delayed-template-parsing # noqa
# for an explanation of the flag and
# https://code.google.com/p/include-what-you-use/source/detail?r=566
# for a similar issue.
if OnWindows():
flags.append( '-fno-delayed-template-parsing' )
if OnMac():
flags = AddMacIncludePaths( flags )
flags = _EnableTypoCorrection( flags )
vector = ycm_core.StringVector()
for flag in flags:
vector.append( flag )
return ycm_core.StringVector( flags )
def _RemoveXclangFlags( flags ):
"""Drops -Xclang flags. These are typically used to pass in options to
clang cc1 which are not used in the front-end, so they are not needed for
code completion."""
sanitized_flags = []
saw_xclang = False
for flag in flags:
if flag == '-Xclang':
saw_xclang = True
continue
elif saw_xclang:
saw_xclang = False
continue
sanitized_flags.append( flag )
return sanitized_flags
def _RemoveFlagsPrecedingCompiler( flags, enable_windows_style_flags ):
"""Assuming that the flag just before the first flag (looks like a flag,
not like a file path) is the compiler path, removes all flags preceding it."""
for index, flag in enumerate( flags ):
if ( flag.startswith( '-' ) or
( enable_windows_style_flags and
flag.startswith( '/' ) and
not os.path.exists( flag ) ) ):
return ( flags[ index - 1: ] if index > 1 else
flags )
return flags[ :-1 ]
def _AddLanguageFlagWhenAppropriate( flags, enable_windows_style_flags ):
"""When flags come from the compile_commands.json file, the flag preceding the
first flag starting with a dash is usually the path to the compiler that
should be invoked. Since LibClang does not deduce the language from the
compiler name, we explicitly set the language to C++ if the compiler is a C++
one (g++, clang++, etc.). We skip setting the language to CUDA if any of the
source files has a .cu or .cuh extension, because LLVM11 does that for us.
Otherwise, we let LibClang guess the language from the file extension. This
handles the case where the .h extension is used for C++ headers."""
flags = _RemoveFlagsPrecedingCompiler( flags, enable_windows_style_flags )
# First flag is now the compiler path, a flag starting with a dash or
# a flag starting with a forward slash if enable_windows_style_flags is True.
first_flag = flags[ 0 ]
# Because of _RemoveFlagsPrecedingCompiler called above, irrelevant of
# enable_windows_style_flags. the first flag is either the compiler
# (path or executable), a Windows style flag or starts with a dash.
if first_flag.startswith( '-' ):
return flags
# Libclang has already added `-xcuda` for us.
if any( fl.endswith( '.cu' ) or fl.endswith( '.cuh' )
for fl in reversed( flags ) ):
return flags
# NOTE: This is intentionally NOT checking for enable_windows_style_flags.
#
# The first flag is now either an absolute path, a Windows style flag or a
# C++ compiler executable from $PATH.
# If it starts with a forward slash the flag can either be an absolute
# flag or a Windows style flag.
# If it matches the regex, it is safe to assume the flag is a compiler
# path.
# If it does not match the regex, it could still be a Windows style
# path or an absolute path. - This is determined in _RemoveUnusedFlags()
# and cleaned properly.
# If the flag starts with anything else (i.e. not a '-' or a '/'), the flag
# is a stray file path and shall be gotten rid of in _RemoveUnusedFlags().
if CPP_COMPILER_REGEX.search( first_flag ):
return [ first_flag, '-x', 'c++' ] + flags[ 1: ]
return flags
def RemoveUnusedFlags( flags, filename, enable_windows_style_flags ):
"""Given an iterable object that produces strings (flags for Clang), removes
the '-c' and '-o' options that Clang does not like to see when it's producing
completions for a file. Same for '-MD' etc.
We also try to remove any stray filenames in the flags that aren't include
dirs."""
new_flags = []
# When flags come from the compile_commands.json file, the first flag is
# usually the path to the compiler that should be invoked. Directly move it to
# the new_flags list so it doesn't get stripped of in the loop below.
if not flags[ 0 ].startswith( '-' ):
new_flags = flags[ :1 ]
flags = flags[ 1: ]
skip_next = False
current_flag = flags[ 0 ]
filename = os.path.realpath( filename )
for flag in flags:
previous_flag = current_flag
current_flag = flag
if skip_next:
skip_next = False
continue
if ( flag in STATE_FLAGS_TO_SKIP or
( enable_windows_style_flags and
flag in STATE_FLAGS_TO_SKIP_WIN_STYLE ) ):
continue
if flag in FILE_FLAGS_TO_SKIP:
skip_next = True
continue
if os.path.realpath( flag ) == filename:
continue
# We want to make sure that we don't have any stray filenames in our flags;
# filenames that are part of include flags are ok, but others are not. This
# solves the case where we ask the compilation database for flags for
# "foo.cpp" when we are compiling "foo.h" because the comp db doesn't have
# flags for headers. The returned flags include "foo.cpp" and we need to
# remove that.
if _SkipStrayFilenameFlag( current_flag,
previous_flag,
enable_windows_style_flags ):
continue
new_flags.append( flag )
return new_flags
def _SkipStrayFilenameFlag( current_flag,
previous_flag,
enable_windows_style_flags ):
current_flag_starts_with_slash = current_flag.startswith( '/' )
previous_flag_starts_with_slash = previous_flag.startswith( '/' )
current_flag_starts_with_dash = current_flag.startswith( '-' )
previous_flag_starts_with_dash = previous_flag.startswith( '-' )
previous_flag_is_include = ( previous_flag in INCLUDE_FLAGS or
( enable_windows_style_flags and
previous_flag in INCLUDE_FLAGS_WIN_STYLE ) )
current_flag_may_be_path = ( '/' in current_flag or
( enable_windows_style_flags and
'\\' in current_flag ) )
return ( not ( current_flag_starts_with_dash or
( enable_windows_style_flags and
current_flag_starts_with_slash ) ) and
( not ( previous_flag_starts_with_dash or
( enable_windows_style_flags and
previous_flag_starts_with_slash ) ) or
( not previous_flag_is_include and current_flag_may_be_path ) ) )
def _GetMacSysRoot():
# Since macOS 10.14, the root framework directories do not contain the
# headers. Instead of relying on the macOS version, check if the Headers
# directory of a common framework (Foundation) exists. If it does, return the
# base directory as the default sysroot.
for sysroot in [ '/', MAC_XCODE_SYSROOT, MAC_COMMAND_LINE_SYSROOT ]:
if os.path.exists( os.path.join( sysroot,
MAC_FOUNDATION_HEADERS_RELATIVE_DIR ) ):
return sysroot
# No headers found. Use the root directory anyway.
return '/'
def _ExtractInfoForMacIncludePaths( flags ):
language = 'c++'
use_libcpp = True
sysroot = _GetMacSysRoot()
isysroot = None
previous_flag = None
for current_flag in flags:
if previous_flag == '-x':
language = current_flag
if current_flag.startswith( '-x' ):
language = current_flag[ 2: ]
if current_flag.startswith( '-stdlib=' ):
use_libcpp = current_flag[ 8: ] == 'libc++'
if previous_flag == '--sysroot':
sysroot = current_flag
if current_flag.startswith( '--sysroot=' ):
sysroot = current_flag[ 10: ]
if previous_flag == '-isysroot':
isysroot = current_flag
if current_flag.startswith( '-isysroot' ):
isysroot = current_flag[ 9: ]
previous_flag = current_flag
# -isysroot takes precedence over --sysroot.
if isysroot:
sysroot = isysroot
language_is_cpp = language in { 'c++', 'objective-c++' }
return language_is_cpp, use_libcpp, sysroot
def _FindMacToolchain():
for toolchain in [ MAC_XCODE_TOOLCHAIN_DIR, MAC_COMMAND_LINE_TOOLCHAIN_DIR ]:
if os.path.exists( toolchain ):
return toolchain
return None
# We can't rely on upstream libclang to find the system headers on macOS 10.14
# as it's unable to locate the framework headers without setting the sysroot if
# Command Line Tools is not installed. So, we try to reproduce the logic used by
# Apple Clang to find the system headers by looking at the output of the command
#
# clang++ -x c++ -E -v -
#
# which prints the list of system header directories and by reading the source
# code of upstream Clang:
# https://github.com/llvm-mirror/clang/blob/2709c8b804eb38dbdc8ae05b8fcf4f95c01b4102/lib/Frontend/InitHeaderSearch.cpp#L453-L510
# This has also the benefit of allowing completion of system header paths and
# navigation to these headers when the cursor is on an include statement.
def AddMacIncludePaths( flags ):
use_standard_cpp_includes = '-nostdinc++' not in flags
use_standard_system_includes = '-nostdinc' not in flags
use_builtin_includes = '-nobuiltininc' not in flags
language_is_cpp, use_libcpp, sysroot = _ExtractInfoForMacIncludePaths( flags )
toolchain = _FindMacToolchain()
if ( language_is_cpp and
use_standard_cpp_includes and
use_standard_system_includes and
use_libcpp ):
# Sometimes apple puts the stdlib in the platform, but it's also always in
# the toolchain. Pickt he platform one if it's there, else the toolchain
# one.
platform_stdlib = os.path.join( sysroot, 'usr/include/c++/v1' )
if os.path.exists( platform_stdlib ):
flags.extend( [ '-isystem', platform_stdlib ] )
elif toolchain:
flags.extend( [
'-isystem', os.path.join( toolchain, 'usr/include/c++/v1' ) ] )
if use_standard_system_includes:
flags.extend( [
'-isystem', os.path.join( sysroot, 'usr/local/include' ) ] )
# Apple Clang always adds /usr/local/include to the list of system header
# directories even if sysroot is not the root directory.
if sysroot != '/':
flags.extend( [ '-isystem', '/usr/local/include' ] )
if use_builtin_includes:
flags.extend( [
'-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ) ] )
if use_standard_system_includes:
if toolchain:
flags.extend( [
'-isystem', os.path.join( toolchain, 'usr/include' ) ] )
flags.extend( [
'-isystem', os.path.join( sysroot, 'usr/include' ),
'-iframework', os.path.join( sysroot, 'System/Library/Frameworks' ),
'-iframework', os.path.join( sysroot, 'Library/Frameworks' ) ] )
return flags
def _EnableTypoCorrection( flags ):
"""Adds the -fspell-checking flag if the -fno-spell-checking flag is not
present"""
# "Typo correction" (aka spell checking) in clang allows it to produce
# hints (in the form of fix-its) in the case of certain diagnostics. A common
# example is "no type named 'strng' in namespace 'std'; Did you mean
# 'string'? (FixIt)". This is enabled by default in the clang driver (i.e. the
# 'clang' binary), but is not when using libclang (as we do). It's a useful
# enough feature that we just always turn it on unless the user explicitly
# turned it off in their flags (with -fno-spell-checking).
if '-fno-spell-checking' in flags:
return flags
flags.append( '-fspell-checking' )
return flags
def _MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = ( PATH_FLAGS + INCLUDE_FLAGS_WIN_STYLE
if ShouldAllowWinStyleFlags( flags )
else PATH_FLAGS )
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
new_flag = AbsolutePath( flag, working_directory )
else:
for path_flag in path_flags:
# Single dash argument alone, e.g. -isysroot <path>
if flag == path_flag:
make_next_absolute = True
break
# Single dash argument with inbuilt path, e.g. -isysroot<path>
# or double-dash argument, e.g. --isysroot=<path>
if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
path = AbsolutePath( path, working_directory )
new_flag = f'{ path_flag }{ path }'
break
if new_flag:
new_flags.append( new_flag )
return new_flags
# Find the compilation info structure from the supplied database for the
# supplied file. If the source file is a header, try and find an appropriate
# source file and return the compilation_info for that.
def _GetCompilationInfoForFile( database, file_name, file_extension ):
# Ask the database for the flags.
compilation_info = database.GetCompilationInfoForFile( file_name )
if compilation_info.compiler_flags_:
return compilation_info
return None
def UserIncludePaths( user_flags, filename ):
"""
Returns a tuple ( quoted_include_paths, include_paths )
quoted_include_paths is a list of include paths that are only suitable for
quoted include statement.
include_paths is a list of include paths that can be used for angle bracketed
and quoted include statement.
"""
quoted_include_paths = [ ToUnicode( os.path.dirname( filename ) ) ]
include_paths = []
framework_paths = []
if user_flags:
include_flags = { '-iquote': quoted_include_paths,
'-I': include_paths,
'-isystem': include_paths,
'-F': framework_paths,
'-iframework': framework_paths }
if ShouldAllowWinStyleFlags( user_flags ):
include_flags[ '/I' ] = include_paths
try:
it = iter( user_flags )
for user_flag in it:
user_flag_len = len( user_flag )
for flag, container in include_flags.items():
if user_flag.startswith( flag ):
flag_len = len( flag )
include_path = ( next( it ) if user_flag_len == flag_len else
user_flag[ flag_len: ] )
if include_path:
container.append( ToUnicode( include_path ) )
break
except StopIteration:
pass
return quoted_include_paths, include_paths, framework_paths