Skip to content

Commit cd82a68

Browse files
committed
Make header search path behavior determined by driver used rather than -fms-compatibility.
Use of `-fms-compatibility` to opt-in to Microsoft header search behavior was found to cause incompatibility problems with some projects that use the GCC compatible driver on Windows. This change introduces a new `-fheader-search` option to control the behavior. Valid values for the option include `gcc` and `microsoft`. The `clang-cl` driver uses `-fheader-search=microsoft` by default; all other drivers continue to use `-fheader-search=gcc` by default. This change affects backward compatibility. The `-fheader-search=` option can be used to opt in to previous behavior with some caveats. Clang 19 and earlier followed the GCC header search behavior except when searching for header files included by quoted inclusion (`#include "file.h"`) when `-fms-compatibility` is specified; in that case, Microsoft's behavior of searching for the header file in the directories of each file in the include stack was used. An option to control this specific behavior is not provided, but could be added if found to be necessary or useful.
1 parent 8ecdbe8 commit cd82a68

File tree

13 files changed

+96
-53
lines changed

13 files changed

+96
-53
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -945,16 +945,17 @@ Windows Support
945945
allowed as VS2013 and prior allow it.
946946

947947
- Clang now matches MSVC behavior regarding the handling of duplicate header
948-
search paths when running in Microsoft compatibility mode. Historically,
949-
Clang has mimicked gcc behavior in which user search paths are ordered before
950-
system search paths, user search paths that duplicate a (later) system search
948+
search paths when run via the ``clang-cl`` driver (by default) or with the
949+
``-fheader-search=microsoft`` option otherwise. Historically, Clang has
950+
mimicked GCC behavior in which user search paths are ordered before system
951+
search paths, user search paths that duplicate a (later) system search
951952
path are ignored, and search paths that duplicate an earlier search path of
952953
the same user/system kind are ignored. This ordering is not compatible with
953954
the ordering that MSVC uses when paths are duplicated across ``/I`` options
954955
and the ``INCLUDE`` environment variable.
955956

956957
The order that MSVC uses and that Clang now replicates when the
957-
``-fms-compatibility`` option is enabled follows.
958+
``-fheader-search=microsoft`` option is enabled follows.
958959

959960
- Paths specified by the ``/I`` and ``/external:I`` options are processed in
960961
the order that they appear. Paths specified by ``/I`` that duplicate a path
@@ -982,6 +983,10 @@ Windows Support
982983
Paths that duplicate an earlier path in the ``EXTERNAL_INCLUDE``
983984
environment variable are ignored.
984985

986+
The ``-fheader-search=gcc`` option can be used to opt in to GCC duplicate
987+
header search path handling (which remains the default behavior for the GCC
988+
compatible drivers).
989+
985990
LoongArch Support
986991
^^^^^^^^^^^^^^^^^
987992

clang/docs/UsersManual.rst

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5166,12 +5166,26 @@ follows:
51665166

51675167
2. Consult the environment.
51685168

5169-
TODO: This is not yet implemented.
5169+
- `/external:env:[VARIABLE]`
51705170

5171-
This will consult the environment variables:
5171+
This command line option specifies a user identified environment variable
5172+
which is treated as a path delimited (`;`) list of system header paths.
51725173

5173-
- `WindowsSdkDir`
5174-
- `UCRTVersion`
5174+
- `INCLUDE`
5175+
5176+
This environment variable is treated as a path delimited (`;`) list of
5177+
system header paths.
5178+
5179+
- `EXTERNAL_INCLUDE`
5180+
5181+
This environment variable is treated as a path delimited (`;`) list of
5182+
system header paths.
5183+
5184+
The following environment variables will be consulted and used to form paths
5185+
to validate and load content from as appropriate:
5186+
5187+
- `WindowsSdkDir`
5188+
- `UCRTVersion`
51755189

51765190
3. Fallback to the registry.
51775191

@@ -5183,6 +5197,8 @@ The Visual C++ Toolset has a slightly more elaborate mechanism for detection.
51835197

51845198
1. Consult the command line.
51855199

5200+
Anything the user specifies is always given precedence.
5201+
51865202
- `/winsysroot:`
51875203

51885204
The `/winsysroot:` is used as an equivalent to `-sysroot` on Unix
@@ -5200,21 +5216,30 @@ The Visual C++ Toolset has a slightly more elaborate mechanism for detection.
52005216

52015217
2. Consult the environment.
52025218

5203-
- `/external:[VARIABLE]`
5219+
- `/external:env:[VARIABLE]`
5220+
5221+
This command line option specifies a user identified environment variable
5222+
which is treated as a path delimited (`;`) list of external header search
5223+
paths. Additionally, any header search path provided by other means that
5224+
has a prefix that matches one of these paths is treated as an external
5225+
header search path.
5226+
5227+
- `INCLUDE`
52045228

5205-
This specifies a user identified environment variable which is treated as
5206-
a path delimiter (`;`) separated list of paths to map into `-imsvc`
5207-
arguments which are treated as `-isystem`.
5229+
This environment variable is treated as a path delimited (`;`) list of
5230+
header search paths.
52085231

5209-
- `INCLUDE` and `EXTERNAL_INCLUDE`
5232+
- `EXTERNAL_INCLUDE`
52105233

5211-
The path delimiter (`;`) separated list of paths will be mapped to
5212-
`-imsvc` arguments which are treated as `-isystem`.
5234+
This environment variable is treated as a path delimited (`;`) list of
5235+
external header search paths. Additionally, any header search path
5236+
provided by other means that has a prefix that matches one of these paths
5237+
is treated as an external header search path.
52135238

52145239
- `LIB` (indirectly)
52155240

52165241
The linker `link.exe` or `lld-link.exe` will honour the environment
5217-
variable `LIB` which is a path delimiter (`;`) set of paths to consult for
5242+
variable `LIB` which is a path delimited (`;`) set of paths to consult for
52185243
the import libraries to use when linking the final target.
52195244

52205245
The following environment variables will be consulted and used to form paths

clang/include/clang/Driver/Options.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8240,6 +8240,17 @@ def fexperimental_max_bitint_width_EQ:
82408240
// Header Search Options
82418241
//===----------------------------------------------------------------------===//
82428242

8243+
let Visibility = [ClangOption, CC1Option, CLOption] in {
8244+
8245+
def fheader_search : Joined<["-"], "fheader-search=">, Group<clang_i_Group>,
8246+
HelpText<"Specify the method used to resolve included header files">,
8247+
Values<"gcc,microsoft">,
8248+
NormalizedValuesScope<"clang::HeaderSearchMode">,
8249+
NormalizedValues<["GCC", "Microsoft"]>,
8250+
MarshallingInfoEnum<HeaderSearchOpts<"Mode">, "GCC">;
8251+
8252+
} // let Visibility = [ClangOption, CC1Option, CLOption]
8253+
82438254
let Visibility = [CC1Option] in {
82448255

82458256
def nostdsysteminc : Flag<["-"], "nostdsysteminc">,

clang/include/clang/Lex/HeaderSearchOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ enum IncludeDirGroup {
6969

7070
} // namespace frontend
7171

72+
/// HeaderSearchMode - The method used to resolve included headers to files.
73+
/// This controls the order in which include paths are searched and how
74+
/// duplicate search paths are handled.
75+
enum class HeaderSearchMode { GCC, Microsoft };
76+
7277
/// HeaderSearchOptions - Helper class for storing options related to the
7378
/// initialization of the HeaderSearch object.
7479
class HeaderSearchOptions {
@@ -103,6 +108,9 @@ class HeaderSearchOptions {
103108
: Prefix(Prefix), IsSystemHeader(IsSystemHeader) {}
104109
};
105110

111+
/// The header search mode to use.
112+
HeaderSearchMode Mode = HeaderSearchMode::GCC;
113+
106114
/// If non-empty, the directory to use as a "virtual system root" for include
107115
/// paths.
108116
std::string Sysroot;

clang/lib/Lex/InitHeaderSearch.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class InitHeaderSearch {
9898
const HeaderSearchOptions &HSOpts);
9999

100100
/// Merges all search path lists into one list and send it to HeaderSearch.
101-
void Realize(const LangOptions &Lang);
101+
void Realize(const HeaderSearchOptions &HSOpts, const LangOptions &Lang);
102102
};
103103

104104
} // end anonymous namespace.
@@ -369,7 +369,7 @@ void InitHeaderSearch::AddDefaultIncludePaths(
369369
/// and system search paths. If partitioning is not needed, then call with
370370
/// Part1Begin equal to Part2Begin. The return value is the number of items
371371
/// removed from the first partition.
372-
static unsigned RemoveDuplicates(const LangOptions &Lang,
372+
static unsigned RemoveDuplicates(const HeaderSearchOptions &HSOpts,
373373
std::vector<DirectoryLookupInfo> &SearchList,
374374
unsigned Part1Begin, unsigned Part2Begin,
375375
bool Verbose) {
@@ -430,8 +430,8 @@ static unsigned RemoveDuplicates(const LangOptions &Lang,
430430
// A path that matches a later path specified by -iexternal is always
431431
// suppressed.
432432
DirToRemove = PrevIndex;
433-
} else if (!Lang.MSVCCompat && PrevSrcKind == SrcMgr::C_User &&
434-
CurSrcKind != SrcMgr::C_User) {
433+
} else if (HSOpts.Mode != HeaderSearchMode::Microsoft &&
434+
PrevSrcKind == SrcMgr::C_User && CurSrcKind != SrcMgr::C_User) {
435435
// When not in Microsoft compatibility mode, a user path that matches
436436
// a later system path is suppressed.
437437
DirToRemove = PrevIndex;
@@ -489,7 +489,8 @@ mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) {
489489
return LookupsToUserEntries;
490490
}
491491

492-
void InitHeaderSearch::Realize(const LangOptions &Lang) {
492+
void InitHeaderSearch::Realize(const HeaderSearchOptions &HSOpts,
493+
const LangOptions &Lang) {
493494
// Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.
494495
std::vector<DirectoryLookupInfo> SearchList;
495496
SearchList.reserve(IncludePath.size());
@@ -499,7 +500,7 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
499500
if (Include.Group == Quoted)
500501
SearchList.push_back(Include);
501502
// Remove duplicate search paths within the quoted inclusion list.
502-
RemoveDuplicates(Lang, SearchList, 0, 0, Verbose);
503+
RemoveDuplicates(HSOpts, SearchList, 0, 0, Verbose);
503504
unsigned EndQuoted = SearchList.size();
504505

505506
// Add search paths for angled inclusion next. Note that user paths and
@@ -516,7 +517,7 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
516517
// Remove duplicate search paths within the angled inclusion list.
517518
// This may leave paths duplicated across the quoted and angled inclusion
518519
// sections.
519-
RemoveDuplicates(Lang, SearchList, EndQuoted, EndQuoted, Verbose);
520+
RemoveDuplicates(HSOpts, SearchList, EndQuoted, EndQuoted, Verbose);
520521
unsigned EndAngled = SearchList.size();
521522

522523
// Add search paths for language dependent system paths next.
@@ -539,7 +540,7 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
539540
// same file. This may result in earlier user paths being removed, and thus
540541
// requires updating the EndAngled index.
541542
unsigned NonSystemRemoved =
542-
RemoveDuplicates(Lang, SearchList, EndQuoted, EndAngled, Verbose);
543+
RemoveDuplicates(HSOpts, SearchList, EndQuoted, EndAngled, Verbose);
543544
EndAngled -= NonSystemRemoved;
544545

545546
Headers.SetSearchPaths(extractLookups(SearchList), EndQuoted, EndAngled,
@@ -599,5 +600,5 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,
599600
HS.getModuleMap().setBuiltinIncludeDir(*Dir);
600601
}
601602

602-
Init.Realize(Lang);
603+
Init.Realize(HSOpts, Lang);
603604
}

clang/lib/Lex/PPDirectives.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "clang/Basic/TokenKinds.h"
2424
#include "clang/Lex/CodeCompletionHandler.h"
2525
#include "clang/Lex/HeaderSearch.h"
26+
#include "clang/Lex/HeaderSearchOptions.h"
2627
#include "clang/Lex/LexDiagnostic.h"
2728
#include "clang/Lex/LiteralSupport.h"
2829
#include "clang/Lex/MacroInfo.h"
@@ -999,7 +1000,9 @@ OptionalFileEntryRef Preprocessor::LookupFile(
9991000
// MSVC searches the current include stack from top to bottom for
10001001
// headers included by quoted include directives.
10011002
// See: http://msdn.microsoft.com/en-us/library/36k2cdd4.aspx
1002-
if (LangOpts.MSVCCompat && !isAngled) {
1003+
if (getHeaderSearchInfo().getHeaderSearchOpts().Mode ==
1004+
HeaderSearchMode::Microsoft &&
1005+
!isAngled) {
10031006
for (IncludeStackInfo &ISEntry : llvm::reverse(IncludeMacroStack)) {
10041007
if (IsFileLexer(ISEntry))
10051008
if ((FileEnt = ISEntry.ThePPLexer->getFileEntry()))

clang/lib/Serialization/ASTReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6166,8 +6166,8 @@ bool ASTReader::ParseHeaderSearchOptions(const RecordData &Record,
61666166
ASTReaderListener &Listener) {
61676167
HeaderSearchOptions HSOpts;
61686168
unsigned Idx = 0;
6169+
HSOpts.Mode = static_cast<HeaderSearchMode>(Record[Idx++]);
61696170
HSOpts.Sysroot = ReadString(Record, Idx);
6170-
61716171
HSOpts.ResourceDir = ReadString(Record, Idx);
61726172
HSOpts.ModuleCachePath = ReadString(Record, Idx);
61736173
HSOpts.ModuleUserBuildPath = ReadString(Record, Idx);

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,6 +1673,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) {
16731673
const HeaderSearchOptions &HSOpts =
16741674
PP.getHeaderSearchInfo().getHeaderSearchOpts();
16751675

1676+
Record.push_back(static_cast<unsigned>(HSOpts.Mode));
16761677
AddString(HSOpts.Sysroot, Record);
16771678
AddString(HSOpts.ResourceDir, Record);
16781679
AddString(HSOpts.ModuleCachePath, Record);

clang/test/Driver/header-search-duplicates.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
// RUN: rm -rf %t
66
// RUN: split-file %s %t
77

8-
// This test exercises the clang driver using a target that does not implicitly
9-
// enable the -fms-compatibility option. The -nostdinc option is used to
10-
// suppress default search paths to ease testing.
8+
// This test uses the -nostdinc option to suppress default search paths to
9+
// ease testing.
1110

1211
// Header search paths are categorized into the following general groups.
1312
// - Quoted: Search paths that are only used to resolve inclusion of header

clang/test/Driver/microsoft-header-search-duplicates.c

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,8 @@
22
// Microsoft compatibility mode. See header-search-duplicates.c for GCC
33
// compatible behavior.
44

5-
// This test is intended to be usable to validate MSVC behavior using a .bat
6-
// test driver similar to the following. A failure to compile successfully
7-
// would indicate a problem with the test or, perhaps, a behavioral difference
8-
// across MSVC versions.
9-
// @echo on
10-
// setlocal
11-
// rd /s/q test-msvc
12-
// split-file microsoft-header-search-duplicates.c test-msvc
13-
// pushd test-msvc
14-
// REM Validate test 1:
15-
// set INCLUDE=...
16-
// set EXTERNAL_INCLUDE=...
17-
// set EXTRA_INCLUDE=...
18-
// cl.exe /c /showIncludes ... test1.c
19-
// popd
20-
21-
// This test exercises both the clang and clang-cl drivers using a target that
22-
// implicitly enables the '-fms-compatibility' option. The '-nobuiltininc',
23-
// '-nostdinc', and '/X ('-nostdlibinc') options are used to suppress implicit
24-
// header search paths to ease testing.
5+
// This test uses the '-nobuiltininc', '-nostdinc', and '/X ('-nostdlibinc')
6+
// options to suppress implicit header search paths to ease testing.
257

268
// Header search paths are processed as follows:
279
// 1) Paths specified by the '/I' and '/external:I' options are processed in
@@ -38,7 +20,7 @@
3820
// 3) Paths specified by the 'INCLUDE' environment variable are processed in
3921
// order. Paths that duplicate a path from step 1, step 2, or an earlier
4022
// path in the 'INCLUDE' environment variable are ignored.
41-
// 4) Paths specified by the 'EXTERNALINCLUDE' environment variable are
23+
// 4) Paths specified by the 'EXTERNAL_INCLUDE' environment variable are
4224
// processed in order. Paths that duplicate a path from step 1, step 2,
4325
// step 3, or an earlier path in the 'EXTERNAL_INCLUDE' environment
4426
// variable are ignored.
@@ -51,6 +33,7 @@
5133
//
5234
// RUN: %clang \
5335
// RUN: -target x86_64-pc-windows -v -fsyntax-only \
36+
// RUN: -fheader-search=microsoft \
5437
// RUN: -nostdinc \
5538
// RUN: -I%t/test1/include/y \
5639
// RUN: -I%t/test1/include/z \
@@ -87,6 +70,7 @@
8770
//
8871
// RUN: %clang \
8972
// RUN: -target x86_64-pc-windows -v -fsyntax-only \
73+
// RUN: -fheader-search=microsoft \
9074
// RUN: -nostdinc \
9175
// RUN: -iexternal %t/test2/include/z \
9276
// RUN: -iexternal %t/test2/include/y \
@@ -123,6 +107,7 @@
123107
//
124108
// RUN: %clang \
125109
// RUN: -target x86_64-pc-windows -v -fsyntax-only \
110+
// RUN: -fheader-search=microsoft \
126111
// RUN: -nostdinc \
127112
// RUN: -iexternal %t/test3/include/w \
128113
// RUN: -I%t/test3/include/z \
@@ -193,6 +178,7 @@
193178
// RUN: env EXTRA_INCLUDE2="%t/test4/include/z;%t/test4/include/y;%t/test4/include/x;%t/test4/include/w" \
194179
// RUN: %clang \
195180
// RUN: -target x86_64-pc-windows -v -fsyntax-only \
181+
// RUN: -fheader-search=microsoft \
196182
// RUN: -nostdinc \
197183
// RUN: -I%t/test4/include/w \
198184
// RUN: -iexternal %t/test4/include/x \
@@ -262,6 +248,7 @@
262248
// RUN: env EXTERNAL_INCLUDE="%t/test5/include/z;%t/test5/include/y;%t/test5/include/w;%t/test5/include/v;%t/test5/include/u" \
263249
// RUN: %clang \
264250
// RUN: -target x86_64-pc-windows -v -fsyntax-only \
251+
// RUN: -fheader-search=microsoft \
265252
// RUN: -nostdinc \
266253
// RUN: -I%t/test5/include/u \
267254
// RUN: -iexternal %t/test5/include/v \

0 commit comments

Comments
 (0)