diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -342,6 +342,13 @@ def err_drv_invalid_malign_branch_EQ : Error< "invalid argument '%0' to -malign-branch=; each element must be one of: %1">; +def err_drv_print_header_env_var : Error< + "environment variable CC_PRINT_HEADERS_%select{FORMAT|FILTERING}0 has invalid value %1">; +def err_drv_print_header_env_var_combination : Error< + "unsupported combination: CC_PRINT_HEADERS_FORMAT=%0 and CC_PRINT_HEADERS_FILTERING=%1">; +def err_drv_print_header_env_var_combination_cc1 : Error< + "unsupported combination: -header-include-format=%0 and -header-include-filtering=%1">; + def warn_O4_is_O3 : Warning<"-O4 is equivalent to -O3">, InGroup; def warn_drv_optimization_value : Warning<"optimization level '%0' is not supported; using '%1%2' instead">, InGroup; diff --git a/clang/include/clang/Basic/HeaderInclude.h b/clang/include/clang/Basic/HeaderInclude.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Basic/HeaderInclude.h @@ -0,0 +1,71 @@ +//===--- HeaderInclude.h - Header Include -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines enums used when emitting included header information. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_HEADERINCLUDEFORMATKIND_H +#define LLVM_CLANG_BASIC_HEADERINCLUDEFORMATKIND_H +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +#include + +namespace clang { +/// The format in which header information is emitted. +enum HeaderIncludeFormatKind { HIFMT_None, HIFMT_Textual, HIFMT_JSON }; + +/// Whether header information is filtered or not. If HIFIL_Only_Direct_System +/// is used, only information on system headers directly included from +/// non-system headers is emitted. +enum HeaderIncludeFilteringKind { HIFIL_None, HIFIL_Only_Direct_System }; + +inline HeaderIncludeFormatKind +stringToHeaderIncludeFormatKind(const char *Str) { + return llvm::StringSwitch(Str) + .Case("textual", HIFMT_Textual) + .Case("json", HIFMT_JSON) + .Default(HIFMT_None); +} + +inline bool stringToHeaderIncludeFiltering(const char *Str, + HeaderIncludeFilteringKind &Kind) { + std::pair P = + llvm::StringSwitch>(Str) + .Case("none", {true, HIFIL_None}) + .Case("only-direct-system", {true, HIFIL_Only_Direct_System}) + .Default({false, HIFIL_None}); + Kind = P.second; + return P.first; +} + +inline const char *headerIncludeFormatKindToString(HeaderIncludeFormatKind K) { + switch (K) { + case HIFMT_None: + llvm_unreachable("unexpected format kind"); + case HIFMT_Textual: + return "textual"; + case HIFMT_JSON: + return "json"; + } +} + +inline const char * +headerIncludeFilteringKindToString(HeaderIncludeFilteringKind K) { + switch (K) { + case HIFIL_None: + return "none"; + case HIFIL_Only_Direct_System: + return "only-direct-system"; + } +} + +} // end namespace clang + +#endif // LLVM_CLANG_BASIC_HEADERINCLUDEFORMATKIND_H diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_DRIVER_DRIVER_H #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/HeaderInclude.h" #include "clang/Basic/LLVM.h" #include "clang/Driver/Action.h" #include "clang/Driver/DriverDiagnostic.h" @@ -233,9 +234,16 @@ /// CCPrintOptionsFilename or to stderr. unsigned CCPrintOptions : 1; - /// Set CC_PRINT_HEADERS mode, which causes the frontend to log header include - /// information to CCPrintHeadersFilename or to stderr. - unsigned CCPrintHeaders : 1; + /// The format of the header information that is emitted. If CC_PRINT_HEADERS + /// is set, the format is textual. Otherwise, the format is determined by the + /// enviroment variable CC_PRINT_HEADERS_FORMAT. + HeaderIncludeFormatKind CCPrintHeadersFormat = HIFMT_None; + + /// This flag determines whether clang should filter the header information + /// that is emitted. If enviroment variable CC_PRINT_HEADERS_FILTERING is set + /// to "only-direct-system", only system headers that are directly included + /// from non-system headers are emitted. + HeaderIncludeFilteringKind CCPrintHeadersFiltering = HIFIL_None; /// Set CC_LOG_DIAGNOSTICS mode, which causes the frontend to log diagnostics /// to CCLogDiagnosticsFilename or to stderr, in a stable machine readable diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -5675,6 +5675,14 @@ def header_include_file : Separate<["-"], "header-include-file">, HelpText<"Filename (or -) to write header include output to">, MarshallingInfoString>; +def header_include_format_EQ : Joined<["-"], "header-include-format=">, + HelpText<"set format in which header info is emitted">, + Values<"textual,json">, NormalizedValues<["HIFMT_Textual", "HIFMT_JSON"]>, + MarshallingInfoEnum, "HIFMT_Textual">; +def header_include_filtering_EQ : Joined<["-"], "header-include-filtering=">, + HelpText<"set the flag that enables filtering header information">, + Values<"none,only-direct-system">, NormalizedValues<["HIFIL_None", "HIFIL_Only_Direct_System"]>, + MarshallingInfoEnum, "HIFIL_None">; def show_includes : Flag<["--"], "show-includes">, HelpText<"Print cl.exe style /showIncludes to stdout">; diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_FRONTEND_DEPENDENCYOUTPUTOPTIONS_H #define LLVM_CLANG_FRONTEND_DEPENDENCYOUTPUTOPTIONS_H +#include "clang/Basic/HeaderInclude.h" #include #include @@ -44,6 +45,12 @@ /// due to the "include guard /// optimization" or #pragma once. + /// The format of header information. + HeaderIncludeFormatKind HeaderIncludeFormat = HIFMT_Textual; + + /// Determine whether header information should be filtered. + HeaderIncludeFilteringKind HeaderIncludeFiltering = HIFIL_None; + /// Destination of cl.exe style /showIncludes info. ShowIncludesDestination ShowIncludesDest = ShowIncludesDestination::None; @@ -80,7 +87,8 @@ DependencyOutputOptions() : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), AddMissingHeaderDeps(0), IncludeModuleFiles(0), - ShowSkippedHeaderIncludes(0) {} + ShowSkippedHeaderIncludes(0), HeaderIncludeFormat(HIFMT_Textual), + HeaderIncludeFiltering(HIFIL_None) {} }; } // end namespace clang diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -198,7 +198,7 @@ ModulesModeCXX20(false), LTOMode(LTOK_None), ClangExecutable(ClangExecutable), SysRoot(DEFAULT_SYSROOT), DriverTitle(Title), CCCPrintBindings(false), CCPrintOptions(false), - CCPrintHeaders(false), CCLogDiagnostics(false), CCGenDiagnostics(false), + CCLogDiagnostics(false), CCGenDiagnostics(false), CCPrintProcessStats(false), TargetTriple(TargetTriple), Saver(Alloc), CheckInputsExist(true), ProbePrecompiled(true), SuppressMissingInputWarning(false) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -27,6 +27,7 @@ #include "clang/Basic/CLWarnings.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/HeaderInclude.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/MakeSupport.h" #include "clang/Basic/ObjCRuntime.h" @@ -5556,12 +5557,19 @@ } Args.AddAllArgs(CmdArgs, options::OPT_fshow_skipped_includes); - if (D.CCPrintHeaders && !D.CCGenDiagnostics) { + if (D.CCPrintHeadersFormat && !D.CCGenDiagnostics) { CmdArgs.push_back("-header-include-file"); CmdArgs.push_back(!D.CCPrintHeadersFilename.empty() ? D.CCPrintHeadersFilename.c_str() : "-"); CmdArgs.push_back("-sys-header-deps"); + CmdArgs.push_back(Args.MakeArgString( + "-header-include-format=" + + std::string(headerIncludeFormatKindToString(D.CCPrintHeadersFormat)))); + CmdArgs.push_back( + Args.MakeArgString("-header-include-filtering=" + + std::string(headerIncludeFilteringKindToString( + D.CCPrintHeadersFiltering)))); } Args.AddLastArg(CmdArgs, options::OPT_P); Args.AddLastArg(CmdArgs, options::OPT_print_ivar_layout); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2151,6 +2151,16 @@ Opts.ExtraDeps.emplace_back(std::string(Val), EDK_ModuleFile); } + // Check for invalid combinations of header-include-format + // and header-include-filtering. + if ((Opts.HeaderIncludeFormat == HIFMT_Textual && + Opts.HeaderIncludeFiltering != HIFIL_None) || + (Opts.HeaderIncludeFormat == HIFMT_JSON && + Opts.HeaderIncludeFiltering != HIFIL_Only_Direct_System)) + Diags.Report(diag::err_drv_print_header_env_var_combination_cc1) + << Args.getLastArg(OPT_header_include_format_EQ)->getValue() + << Args.getLastArg(OPT_header_include_filtering_EQ)->getValue(); + return Diags.getNumErrors() == NumErrorsBefore; } diff --git a/clang/lib/Frontend/HeaderIncludeGen.cpp b/clang/lib/Frontend/HeaderIncludeGen.cpp --- a/clang/lib/Frontend/HeaderIncludeGen.cpp +++ b/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -12,6 +12,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -49,6 +50,43 @@ void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, SrcMgr::CharacteristicKind FileType) override; }; + +/// A callback for emitting header usage information to a file in JSON. Each +/// line in the file is a JSON object that includes the source file name and +/// the list of headers directly or indirectly included from it. For example: +/// +/// {"source":"/tmp/foo.c", +/// "includes":["/usr/include/stdio.h", "/usr/include/stdlib.h"]} +/// +/// To reduce the amount of data written to the file, we only record system +/// headers that are directly included from a file that isn't in the system +/// directory. +class HeaderIncludesJSONCallback : public PPCallbacks { + SourceManager &SM; + raw_ostream *OutputFile; + bool OwnsOutputFile; + SmallVector IncludedHeaders; + +public: + HeaderIncludesJSONCallback(const Preprocessor *PP, raw_ostream *OutputFile_, + bool OwnsOutputFile_) + : SM(PP->getSourceManager()), OutputFile(OutputFile_), + OwnsOutputFile(OwnsOutputFile_) {} + + ~HeaderIncludesJSONCallback() override { + if (OwnsOutputFile) + delete OutputFile; + } + + void EndOfMainFile() override; + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + + void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override; +}; } static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename, @@ -116,16 +154,33 @@ } } - // Print header info for extra headers, pretending they were discovered by - // the regular preprocessor. The primary use case is to support proper - // generation of Make / Ninja file dependencies for implicit includes, such - // as sanitizer ignorelists. It's only important for cl.exe compatibility, - // the GNU way to generate rules is -M / -MM / -MD / -MMD. - for (const auto &Header : DepOpts.ExtraDeps) - PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle); - PP.addPPCallbacks(std::make_unique( - &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth, - MSStyle)); + switch (DepOpts.HeaderIncludeFormat) { + case HIFMT_None: + llvm_unreachable("unexpected header format kind"); + case HIFMT_Textual: { + assert(DepOpts.HeaderIncludeFiltering == HIFIL_None && + "header filtering is currently always disabled when output format is" + "textual"); + // Print header info for extra headers, pretending they were discovered by + // the regular preprocessor. The primary use case is to support proper + // generation of Make / Ninja file dependencies for implicit includes, such + // as sanitizer ignorelists. It's only important for cl.exe compatibility, + // the GNU way to generate rules is -M / -MM / -MD / -MMD. + for (const auto &Header : DepOpts.ExtraDeps) + PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle); + PP.addPPCallbacks(std::make_unique( + &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth, + MSStyle)); + break; + } + case HIFMT_JSON: { + assert(DepOpts.HeaderIncludeFiltering == HIFIL_Only_Direct_System && + "only-direct-system is the only option for filtering"); + PP.addPPCallbacks(std::make_unique( + &PP, OutputFile, OwnsOutputFile)); + break; + } + } } void HeaderIncludesCallback::FileChanged(SourceLocation Loc, @@ -197,3 +252,64 @@ PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth, CurrentIncludeDepth + 1, MSStyle); } + +void HeaderIncludesJSONCallback::EndOfMainFile() { + const FileEntry *FE = SM.getFileEntryForID(SM.getMainFileID()); + SmallString<256> MainFile(FE->getName()); + SM.getFileManager().makeAbsolutePath(MainFile); + + std::string Str; + llvm::raw_string_ostream OS(Str); + llvm::json::OStream JOS(OS); + JOS.object([&] { + JOS.attribute("source", MainFile.c_str()); + JOS.attributeArray("includes", [&] { + llvm::StringSet<> SeenHeaders; + for (const std::string &H : IncludedHeaders) + if (SeenHeaders.insert(H).second) + JOS.value(H); + }); + }); + OS << "\n"; + + if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) { + llvm::raw_fd_ostream *FDS = static_cast(OutputFile); + if (auto L = FDS->lock()) + *OutputFile << Str; + } else + *OutputFile << Str; +} + +/// Determine whether the header file should be recorded. The header file should +/// be recorded only if the header file is a system header and the current file +/// isn't a system header. +static bool shouldRecordNewFile(SrcMgr::CharacteristicKind NewFileType, + SourceLocation PrevLoc, SourceManager &SM) { + return SrcMgr::isSystem(NewFileType) && !SM.isInSystemHeader(PrevLoc); +} + +void HeaderIncludesJSONCallback::FileChanged( + SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, FileID PrevFID) { + if (!shouldRecordNewFile(NewFileType, SM.getLocForStartOfFile(PrevFID), SM)) + return; + + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + PresumedLoc UserLoc = SM.getPresumedLoc(Loc); + if (UserLoc.isInvalid()) + return; + + if (Reason == PPCallbacks::EnterFile && + UserLoc.getFilename() != StringRef("")) + IncludedHeaders.push_back(UserLoc.getFilename()); +} + +void HeaderIncludesJSONCallback::FileSkipped( + const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) { + if (!shouldRecordNewFile(FileType, FilenameTok.getLocation(), SM)) + return; + + IncludedHeaders.push_back(SkippedFile.getName().str()); +} diff --git a/clang/test/Preprocessor/Inputs/print-header-json/header0.h b/clang/test/Preprocessor/Inputs/print-header-json/header0.h new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/print-header-json/header0.h @@ -0,0 +1,3 @@ +#include "system3.h" +#include "header1.h" +#include "header2.h" diff --git a/clang/test/Preprocessor/Inputs/print-header-json/header1.h b/clang/test/Preprocessor/Inputs/print-header-json/header1.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/print-header-json/header2.h b/clang/test/Preprocessor/Inputs/print-header-json/header2.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/print-header-json/system/system0.h b/clang/test/Preprocessor/Inputs/print-header-json/system/system0.h new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/print-header-json/system/system0.h @@ -0,0 +1,2 @@ +#include "system1.h" +#include "system2.h" diff --git a/clang/test/Preprocessor/Inputs/print-header-json/system/system1.h b/clang/test/Preprocessor/Inputs/print-header-json/system/system1.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/print-header-json/system/system2.h b/clang/test/Preprocessor/Inputs/print-header-json/system/system2.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/print-header-json/system/system3.h b/clang/test/Preprocessor/Inputs/print-header-json/system/system3.h new file mode 100644 diff --git a/clang/test/Preprocessor/print-header-json.c b/clang/test/Preprocessor/print-header-json.c new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/print-header-json.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -E -header-include-format=json -header-include-filtering=only-direct-system -header-include-file %t.txt -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system %s +// RUN: cat %t.txt | FileCheck %s --check-prefix=SUPPORTED +// RUN: not %clang_cc1 -E -header-include-format=textual -header-include-filtering=only-direct-system -header-include-file %t.txt -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED0 +// RUN: not %clang_cc1 -E -header-include-format=json -header-include-filtering=none -header-include-file %t.txt -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED1 +// RUN: rm %t.txt +// RUN: env CC_PRINT_HEADERS_FORMAT=json CC_PRINT_HEADERS_FILTERING=only-direct-system CC_PRINT_HEADERS_FILE=%t.txt %clang -fsyntax-only -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system %s -o /dev/null +// RUN: env CC_PRINT_HEADERS_FORMAT=textual CC_PRINT_HEADERS_FILTERING=only-direct-system CC_PRINT_HEADERS_FILE=%t.txt not %clang -fsyntax-only -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED2 +// RUN: env CC_PRINT_HEADERS_FORMAT=json CC_PRINT_HEADERS_FILTERING=none CC_PRINT_HEADERS_FILE=%t.txt not %clang -fsyntax-only -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED3 +// RUN: cat %t.txt | FileCheck %s --check-prefix=SUPPORTED + +#include "system0.h" +#include "header0.h" +#include "system2.h" + +// SUPPORTED: {"source":"{{[^,]*}}/print-header-json.c","includes":["{{[^,]*}}system0.h","{{[^,]*}}system3.h","{{[^,]*}}system2.h"]} + +// UNSUPPORTED0: error: unsupported combination: -header-include-format=textual and -header-include-filtering=only-direct-system +// UNSUPPORTED1: error: unsupported combination: -header-include-format=json and -header-include-filtering=none +// UNSUPPORTED2: error: unsupported combination: CC_PRINT_HEADERS_FORMAT=textual and CC_PRINT_HEADERS_FILTERING=only-direct-system +// UNSUPPORTED3: error: unsupported combination: CC_PRINT_HEADERS_FORMAT=json and CC_PRINT_HEADERS_FILTERING=none diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -13,6 +13,7 @@ #include "clang/Driver/Driver.h" #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/HeaderInclude.h" #include "clang/Basic/Stack.h" #include "clang/Config/config.h" #include "clang/Driver/Compilation.h" @@ -244,29 +245,61 @@ *NumberSignPtr = '='; } -static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { - auto CheckEnvVar = [](const char *EnvOptSet, const char *EnvOptFile, - std::string &OptFile) { - bool OptSet = !!::getenv(EnvOptSet); - if (OptSet) { - if (const char *Var = ::getenv(EnvOptFile)) - OptFile = Var; - } - return OptSet; - }; +template +static T checkEnvVar(const char *EnvOptSet, const char *EnvOptFile, + std::string &OptFile) { + T OptVal = ::getenv(EnvOptSet); + if (OptVal) { + if (const char *Var = ::getenv(EnvOptFile)) + OptFile = Var; + } + return OptVal; +} +static bool SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { TheDriver.CCPrintOptions = - CheckEnvVar("CC_PRINT_OPTIONS", "CC_PRINT_OPTIONS_FILE", - TheDriver.CCPrintOptionsFilename); - TheDriver.CCPrintHeaders = - CheckEnvVar("CC_PRINT_HEADERS", "CC_PRINT_HEADERS_FILE", - TheDriver.CCPrintHeadersFilename); + checkEnvVar("CC_PRINT_OPTIONS", "CC_PRINT_OPTIONS_FILE", + TheDriver.CCPrintOptionsFilename); + if (checkEnvVar("CC_PRINT_HEADERS", "CC_PRINT_HEADERS_FILE", + TheDriver.CCPrintHeadersFilename)) { + TheDriver.CCPrintHeadersFormat = HIFMT_Textual; + TheDriver.CCPrintHeadersFiltering = HIFIL_None; + } else if (const char *EnvVar = checkEnvVar( + "CC_PRINT_HEADERS_FORMAT", "CC_PRINT_HEADERS_FILE", + TheDriver.CCPrintHeadersFilename)) { + TheDriver.CCPrintHeadersFormat = stringToHeaderIncludeFormatKind(EnvVar); + if (!TheDriver.CCPrintHeadersFormat) { + TheDriver.Diag(clang::diag::err_drv_print_header_env_var) << 0 << EnvVar; + return false; + } + + const char *FilteringStr = ::getenv("CC_PRINT_HEADERS_FILTERING"); + HeaderIncludeFilteringKind Filtering; + if (!stringToHeaderIncludeFiltering(FilteringStr, Filtering)) { + TheDriver.Diag(clang::diag::err_drv_print_header_env_var) + << 1 << FilteringStr; + return false; + } + + if ((TheDriver.CCPrintHeadersFormat == HIFMT_Textual && + Filtering != HIFIL_None) || + (TheDriver.CCPrintHeadersFormat == HIFMT_JSON && + Filtering != HIFIL_Only_Direct_System)) { + TheDriver.Diag(clang::diag::err_drv_print_header_env_var_combination) + << EnvVar << FilteringStr; + return false; + } + TheDriver.CCPrintHeadersFiltering = Filtering; + } + TheDriver.CCLogDiagnostics = - CheckEnvVar("CC_LOG_DIAGNOSTICS", "CC_LOG_DIAGNOSTICS_FILE", - TheDriver.CCLogDiagnosticsFilename); + checkEnvVar("CC_LOG_DIAGNOSTICS", "CC_LOG_DIAGNOSTICS_FILE", + TheDriver.CCLogDiagnosticsFilename); TheDriver.CCPrintProcessStats = - CheckEnvVar("CC_PRINT_PROC_STAT", "CC_PRINT_PROC_STAT_FILE", - TheDriver.CCPrintStatReportFilename); + checkEnvVar("CC_PRINT_PROC_STAT", "CC_PRINT_PROC_STAT_FILE", + TheDriver.CCPrintStatReportFilename); + + return true; } static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, @@ -481,7 +514,8 @@ insertTargetAndModeArgs(TargetAndMode, Args, SavedStrings); - SetBackdoorDriverOutputsFromEnvVars(TheDriver); + if (!SetBackdoorDriverOutputsFromEnvVars(TheDriver)) + return 1; if (!UseNewCC1Process) { TheDriver.CC1Main = &ExecuteCC1Tool;