Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -5863,6 +5863,9 @@ def _SLASH_D : CLJoinedOrSeparate<"D">, HelpText<"Define macro">, MetaVarName<"">, Alias; def _SLASH_E : CLFlag<"E">, HelpText<"Preprocess to stdout">, Alias; +def _SLASH_external_COLON_I : CLJoinedOrSeparate<"external:I">, Alias, + HelpText<"Add directory to include search path with warnings suppressed">, + MetaVarName<"">; def _SLASH_fp_except : CLFlag<"fp:except">, HelpText<"">, Alias; def _SLASH_fp_except_ : CLFlag<"fp:except-">, HelpText<"">, Alias; @@ -6071,6 +6074,9 @@ def _SLASH_EH : CLJoined<"EH">, HelpText<"Set exception handling model">; def _SLASH_EP : CLFlag<"EP">, HelpText<"Disable linemarker output and preprocess to stdout">; +def _SLASH_external_env : CLJoined<"external:env:">, + HelpText<"Add dirs in env var to include search path with warnings suppressed">, + MetaVarName<"">; def _SLASH_FA : CLFlag<"FA">, HelpText<"Output assembly code file during compilation">; def _SLASH_Fa : CLJoined<"Fa">, @@ -6234,7 +6240,6 @@ def _SLASH_experimental : CLJoined<"experimental:">; def _SLASH_exportHeader : CLFlag<"exportHeader">; def _SLASH_external : CLJoined<"external:">; -def _SLASH_external_COLON_I : CLJoinedOrSeparate<"external:I">; def _SLASH_FA_joined : CLJoined<"FA">; def _SLASH_favor : CLJoined<"favor">; def _SLASH_fsanitize_address_use_after_return : CLJoined<"fsanitize-address-use-after-return">; Index: clang/lib/Driver/ToolChains/MSVC.cpp =================================================================== --- clang/lib/Driver/ToolChains/MSVC.cpp +++ clang/lib/Driver/ToolChains/MSVC.cpp @@ -1243,22 +1243,45 @@ for (const auto &Path : DriverArgs.getAllArgValues(options::OPT__SLASH_imsvc)) addSystemInclude(DriverArgs, CC1Args, Path); + // Add %INCLUDE%-like dirs via the /external:env: flag. + for (const auto &Var : + DriverArgs.getAllArgValues(options::OPT__SLASH_external_env)) { + if (auto Val = llvm::sys::Process::GetEnv(Var)) { + SmallVector Dirs; + StringRef(*Val).split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + for (StringRef Dir : Dirs) + addSystemInclude(DriverArgs, CC1Args, Dir); + } + } + if (DriverArgs.hasArg(options::OPT_nostdlibinc)) return; - // Honor %INCLUDE%. It should know essential search paths with vcvarsall.bat. - // Skip if the user expressly set a vctoolsdir + // Honor %INCLUDE% and %EXTERNAL_INCLUDE%. It should have essential search + // paths set by vcvarsall.bat. Skip if the user expressly set a vctoolsdir. if (!DriverArgs.getLastArg(options::OPT__SLASH_vctoolsdir, options::OPT__SLASH_winsysroot)) { - if (llvm::Optional cl_include_dir = - llvm::sys::Process::GetEnv("INCLUDE")) { - SmallVector Dirs; - StringRef(*cl_include_dir) + SmallVector Dirs; + llvm::Optional include_var = + llvm::sys::Process::GetEnv("INCLUDE"); + // FIXME: MSVC seems to be moving from INCLUDE to EXTERNAL_INCLUDE for + // system includes. Perhaps INCLUDE paths should not be considered systems + // paths at some point in the future. + llvm::Optional ext_include_var = + llvm::sys::Process::GetEnv("EXTERNAL_INCLUDE"); + + if (include_var) { + StringRef(*include_var) .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (StringRef Dir : Dirs) - addSystemInclude(DriverArgs, CC1Args, Dir); - if (!Dirs.empty()) - return; + } + if (ext_include_var) { + StringRef(*ext_include_var) + .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + } + + if (!Dirs.empty()) { + addSystemIncludes(DriverArgs, CC1Args, Dirs); + return; } } Index: clang/test/Driver/cl-include.c =================================================================== --- clang/test/Driver/cl-include.c +++ clang/test/Driver/cl-include.c @@ -7,19 +7,37 @@ // RUN: %clang_cl -nobuiltininc -### -- %s 2>&1 | FileCheck %s --check-prefix=NOBUILTIN // NOBUILTIN-NOT: "-internal-isystem" "{{.*lib.*clang.*include}}" -// RUN: env INCLUDE=/my/system/inc %clang_cl -### -- %s 2>&1 | FileCheck %s --check-prefix=STDINC +// RUN: env INCLUDE=/my/system/inc env EXTERNAL_INCLUDE=/my/system/inc2 %clang_cl -### -- %s 2>&1 | FileCheck %s --check-prefix=STDINC // STDINC: "-internal-isystem" "/my/system/inc" +// STDINC: "-internal-isystem" "/my/system/inc2" // -nostdinc suppresses all of %INCLUDE%, clang resource dirs, and -imsvc dirs. -// RUN: env INCLUDE=/my/system/inc %clang_cl -nostdinc -imsvc /my/other/inc -### -- %s 2>&1 | FileCheck %s --check-prefix=NOSTDINC +// RUN: env INCLUDE=/my/system/inc env EXTERNAL_INCLUDE=/my/system/inc2 %clang_cl -nostdinc -imsvc /my/other/inc -### -- %s 2>&1 | FileCheck %s --check-prefix=NOSTDINC // NOSTDINC: argument unused{{.*}}-imsvc // NOSTDINC-NOT: "-internal-isystem" "/my/system/inc" +// NOSTDINC-NOT: "-internal-isystem" "/my/system/inc2" // NOSTDINC-NOT: "-internal-isystem" "{{.*lib.*clang.*include}}" // NOSTDINC-NOT: "-internal-isystem" "/my/other/inc" -// /X suppresses %INCLUDE% but not clang resource dirs or -imsvc dirs. -// RUN: env INCLUDE=/my/system/inc %clang_cl /X -imsvc /my/other/inc -### -- %s 2>&1 | FileCheck %s --check-prefix=SLASHX +// /X suppresses %INCLUDE% and %EXTERNAL_INCLUDE% but not clang resource dirs, -imsvc dirs, or /external: flags. +// RUN: env INCLUDE=/my/system/inc env EXTERNAL_INCLUDE=/my/system/inc2 env FOO=/my/other/inc2 %clang_cl /X -imsvc /my/other/inc /external:env:FOO -### -- %s 2>&1 | FileCheck %s --check-prefix=SLASHX // SLASHX-NOT: "argument unused{{.*}}-imsvc" // SLASHX-NOT: "-internal-isystem" "/my/system/inc" +// SLASHX-NOT: "-internal-isystem" "/my/system/inc2" // SLASHX: "-internal-isystem" "{{.*lib.*clang.*include}}" // SLASHX: "-internal-isystem" "/my/other/inc" +// SLASHX: "-internal-isystem" "/my/other/inc2" + +// /winsysroot suppresses %EXTERNAL_INCLUDE% but not -imsvc dirs or /external: flags. +// RUN: env env EXTERNAL_INCLUDE=/my/system/inc env FOO=/my/other/inc2 %clang_cl /winsysroot /foo -imsvc /my/other/inc /external:env:FOO -### -- %s 2>&1 | FileCheck %s --check-prefix=SYSROOT +// SYSROOT-NOT: "argument unused{{.*}}-imsvc" +// SYSROOT-NOT: "argument unused{{.*}}/external:" +// SYSROOT-NOT: "/my/system/inc" +// SYSROOT: "-internal-isystem" "/my/other/inc" +// SYSROOT: "-internal-isystem" "/my/other/inc2" +// SYSROOT: "-internal-isystem" "/foo{{.*}}" + +// RUN: env "FOO=/dir1;/dir2" env "BAR=/dir3" %clang_cl /external:env:FOO /external:env:BAR -### -- %s 2>&1 | FileCheck %s --check-prefix=EXTERNAL_ENV +// EXTERNAL_ENV: "-internal-isystem" "/dir1" +// EXTERNAL_ENV: "-internal-isystem" "/dir2" +// EXTERNAL_ENV: "-internal-isystem" "/dir3" Index: clang/test/Driver/cl-options.c =================================================================== --- clang/test/Driver/cl-options.c +++ clang/test/Driver/cl-options.c @@ -38,6 +38,10 @@ // EP: "-P" // EP: "-o" "-" +// RUN: %clang_cl /external:Ipath -### -- %s 2>&1 | FileCheck -check-prefix=EXTERNAL_I %s +// RUN: %clang_cl /external:I path -### -- %s 2>&1 | FileCheck -check-prefix=EXTERNAL_I %s +// EXTERNAL_I: "-isystem" "path" + // RUN: %clang_cl /fp:fast /fp:except -### -- %s 2>&1 | FileCheck -check-prefix=fpexcept %s // fpexcept-NOT: -menable-unsafe-fp-math @@ -434,8 +438,6 @@ // RUN: /experimental:preprocessor \ // RUN: /exportHeader /headerName:foo \ // RUN: /external:anglebrackets \ -// RUN: /external:Ipath \ -// RUN: /external:I path \ // RUN: /external:env:var \ // RUN: /external:W0 \ // RUN: /external:W1 \