diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index f35c9ec553d9c5..563c85588af350 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -252,7 +252,7 @@ class FrontendOpts class PreprocessorOutputOpts : KeyPathAndMacro<"PreprocessorOutputOpts.", base, "PREPROCESSOR_OUTPUT_"> {} class DependencyOutputOpts - : KeyPathAndMacro<"DependencyOutputOpts.", base> {} + : KeyPathAndMacro<"DependencyOutputOpts.", base, "DEPENDENCY_OUTPUT_"> {} class CodeGenOpts : KeyPathAndMacro<"CodeGenOpts.", base, "CODEGEN_"> {} class HeaderSearchOpts diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h index 7a4f3337936fc4..433e3ede11c56a 100644 --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -20,6 +20,14 @@ enum class ShowIncludesDestination { None, Stdout, Stderr }; /// DependencyOutputFormat - Format for the compiler dependency file. enum class DependencyOutputFormat { Make, NMake }; +/// ExtraDepKind - The kind of extra dependency file. +enum ExtraDepKind { + EDK_SanitizeBlacklist, + EDK_ProfileList, + EDK_ModuleFile, + EDK_DepFileEntry, +}; + /// DependencyOutputOptions - Options for controlling the compiler dependency /// file generation. class DependencyOutputOptions { @@ -51,8 +59,9 @@ class DependencyOutputOptions { /// must contain at least one entry. std::vector Targets; - /// A list of filenames to be used as extra dependencies for every target. - std::vector ExtraDeps; + /// A list of extra dependencies (filename and kind) to be used for every + /// target. + std::vector> ExtraDeps; /// In /showIncludes mode, pretend the main TU is a header with this name. std::string ShowIncludesPretendHeader; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index a2bb857cf0338e..3fa2331e259876 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1947,19 +1947,76 @@ bool CompilerInvocation::ParseCodeGenArgs( Res, Args, Diags, "CodeGenOptions"); } -static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, - ArgList &Args) { +static void +GenerateDependencyOutputArgs(const DependencyOutputOptions &Opts, + SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA) { + const DependencyOutputOptions &DependencyOutputOpts = Opts; +#define DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING + + if (Opts.ShowIncludesDest != ShowIncludesDestination::None) + GenerateArg(Args, OPT_show_includes, SA); + + for (const auto &Dep : Opts.ExtraDeps) { + switch (Dep.second) { + case EDK_SanitizeBlacklist: + // Sanitizer blacklist arguments are generated from LanguageOptions. + continue; + case EDK_ModuleFile: + // Module file arguments are generated from FrontendOptions and + // HeaderSearchOptions. + continue; + case EDK_ProfileList: + GenerateArg(Args, OPT_fprofile_list_EQ, Dep.first, SA); + break; + case EDK_DepFileEntry: + GenerateArg(Args, OPT_fdepfile_entry, Dep.first, SA); + break; + } + } +} + +static bool ParseDependencyOutputArgsImpl( + DependencyOutputOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + frontend::ActionKind Action, bool ShowLineMarkers) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + bool Success = true; + + DependencyOutputOptions &DependencyOutputOpts = Opts; +#define DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \ + SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \ + MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING + if (Args.hasArg(OPT_show_includes)) { // Writing both /showIncludes and preprocessor output to stdout // would produce interleaved output, so use stderr for /showIncludes. // This behaves the same as cl.exe, when /E, /EP or /P are passed. - if (Args.hasArg(options::OPT_E) || Args.hasArg(options::OPT_P)) + if (Action == frontend::PrintPreprocessedInput || !ShowLineMarkers) Opts.ShowIncludesDest = ShowIncludesDestination::Stderr; else Opts.ShowIncludesDest = ShowIncludesDestination::Stdout; } else { Opts.ShowIncludesDest = ShowIncludesDestination::None; } + // Add sanitizer blacklists as extra dependencies. // They won't be discovered by the regular preprocessor, so // we let make / ninja to know about this implicit dependency. @@ -1967,32 +2024,84 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, for (const auto *A : Args.filtered(OPT_fsanitize_blacklist)) { StringRef Val = A->getValue(); if (Val.find('=') == StringRef::npos) - Opts.ExtraDeps.push_back(std::string(Val)); + Opts.ExtraDeps.emplace_back(std::string(Val), EDK_SanitizeBlacklist); } if (Opts.IncludeSystemHeaders) { for (const auto *A : Args.filtered(OPT_fsanitize_system_blacklist)) { StringRef Val = A->getValue(); if (Val.find('=') == StringRef::npos) - Opts.ExtraDeps.push_back(std::string(Val)); + Opts.ExtraDeps.emplace_back(std::string(Val), EDK_SanitizeBlacklist); } } } // -fprofile-list= dependencies. for (const auto &Filename : Args.getAllArgValues(OPT_fprofile_list_EQ)) - Opts.ExtraDeps.push_back(Filename); + Opts.ExtraDeps.emplace_back(Filename, EDK_ProfileList); // Propagate the extra dependencies. - for (const auto *A : Args.filtered(OPT_fdepfile_entry)) { - Opts.ExtraDeps.push_back(A->getValue()); - } + for (const auto *A : Args.filtered(OPT_fdepfile_entry)) + Opts.ExtraDeps.emplace_back(A->getValue(), EDK_DepFileEntry); // Only the -fmodule-file= form. for (const auto *A : Args.filtered(OPT_fmodule_file)) { StringRef Val = A->getValue(); if (Val.find('=') == StringRef::npos) - Opts.ExtraDeps.push_back(std::string(Val)); + Opts.ExtraDeps.emplace_back(std::string(Val), EDK_ModuleFile); } + + return Success && Diags.getNumErrors() == NumErrorsBefore; +} + +static bool ParseDependencyOutputArgs(CompilerInvocation &Res, + DependencyOutputOptions &Opts, + ArgList &Args, DiagnosticsEngine &Diags, + frontend::ActionKind Action, + bool ShowLineMarkers) { + DependencyOutputOptions DummyOpts; + + return RoundTrip( + [Action, ShowLineMarkers](CompilerInvocation &Res, ArgList &Args, + DiagnosticsEngine &Diags) { + return ParseDependencyOutputArgsImpl(Res.getDependencyOutputOpts(), + Args, Diags, Action, + ShowLineMarkers); + }, + [&Args](CompilerInvocation &Res, + SmallVectorImpl &GeneratedArgs, + CompilerInvocation::StringAllocator SA) { + GenerateDependencyOutputArgs(Res.getDependencyOutputOpts(), + GeneratedArgs, SA); + // We're querying sanitizer blacklist and module file arguments, but + // they are generated from LanguageOptions and HeaderSearchOptions. + // Let's satisfy RoundTrip by generating them ourselves for now. + if (!Args.hasArg(OPT_fno_sanitize_blacklist)) { + for (const auto *A : Args.filtered(OPT_fsanitize_blacklist)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + GenerateArg(GeneratedArgs, OPT_fsanitize_blacklist, Val, SA); + } + if (Res.getDependencyOutputOpts().IncludeSystemHeaders) { + for (const auto *A : + Args.filtered(OPT_fsanitize_system_blacklist)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + GenerateArg(GeneratedArgs, OPT_fsanitize_system_blacklist, Val, + SA); + } + } + } + + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + GenerateArg(GeneratedArgs, OPT_fmodule_file, Val, SA); + } + }, + [&DummyOpts](CompilerInvocation &Res) { + std::swap(Res.getDependencyOutputOpts(), DummyOpts); + }, + Res, Args, Diags, "DependencyOutputOptions"); } static bool parseShowColorsArgs(const ArgList &Args, bool DefaultColor) { @@ -4284,12 +4393,6 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= Res.parseSimpleArgs(Args, Diags); Success &= ParseAnalyzerArgs(Res, *Res.getAnalyzerOpts(), Args, Diags); - ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), Args); - if (!Res.getDependencyOutputOpts().OutputFile.empty() && - Res.getDependencyOutputOpts().Targets.empty()) { - Diags.Report(diag::err_fe_dependency_file_requires_MT); - Success = false; - } Success &= ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags, /*DefaultDiagColor=*/false); Success &= ParseFrontendArgs(Res, Res.getFrontendOpts(), Args, Diags, @@ -4357,6 +4460,15 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ParsePreprocessorOutputArgs(Res, Res.getPreprocessorOutputOpts(), Args, Diags, Res.getFrontendOpts().ProgramAction); + ParseDependencyOutputArgs(Res, Res.getDependencyOutputOpts(), Args, Diags, + Res.getFrontendOpts().ProgramAction, + Res.getPreprocessorOutputOpts().ShowLineMarkers); + if (!Res.getDependencyOutputOpts().OutputFile.empty() && + Res.getDependencyOutputOpts().Targets.empty()) { + Diags.Report(diag::err_fe_dependency_file_requires_MT); + Success = false; + } + // Turn on -Wspir-compat for SPIR target. if (T.isSPIR()) Res.getDiagnosticOpts().Warnings.push_back("spir-compat"); @@ -4522,6 +4634,7 @@ void CompilerInvocation::generateCC1CommandLine( CodeGenOpts); GeneratePreprocessorOutputArgs(PreprocessorOutputOpts, Args, SA, FrontendOpts.ProgramAction); + GenerateDependencyOutputArgs(DependencyOutputOpts, Args, SA); } IntrusiveRefCntPtr diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index fe8ab719740044..8bdda0c134c803 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -182,7 +182,7 @@ DependencyFileGenerator::DependencyFileGenerator( IncludeModuleFiles(Opts.IncludeModuleFiles), OutputFormat(Opts.OutputFormat), InputFileIndex(0) { for (const auto &ExtraDep : Opts.ExtraDeps) { - if (addDependency(ExtraDep)) + if (addDependency(ExtraDep.first)) ++InputFileIndex; } } diff --git a/clang/lib/Frontend/HeaderIncludeGen.cpp b/clang/lib/Frontend/HeaderIncludeGen.cpp index 97fac8a26faea5..cc2989a31df8ea 100644 --- a/clang/lib/Frontend/HeaderIncludeGen.cpp +++ b/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -119,7 +119,7 @@ void clang::AttachHeaderIncludeGen(Preprocessor &PP, // as sanitizer blacklists. 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, ShowDepth, 2, MSStyle); + PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle); PP.addPPCallbacks(std::make_unique( &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth, MSStyle));