diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -260,6 +260,16 @@ const llvm::Triple &T, const std::string &OutputFile, const LangOptions &LangOptsRef); + + static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts, + SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA); + + static void ParseHeaderSearchArgs(CompilerInvocation &Res, + HeaderSearchOptions &Opts, + llvm::opt::ArgList &Args, + DiagnosticsEngine &Diags, + const std::string &WorkingDir); }; IntrusiveRefCntPtr 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 @@ -48,10 +48,12 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FloatingPointMode.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -151,10 +153,10 @@ /// unnecessary template instantiations and just ignore it with a variadic /// argument. static void denormalizeSimpleFlag(SmallVectorImpl &Args, - const char *Spelling, - CompilerInvocation::StringAllocator, + Twine Spelling, + CompilerInvocation::StringAllocator SA, Option::OptionClass, unsigned, /*T*/...) { - Args.push_back(Spelling); + Args.push_back(SA(Spelling)); } template static constexpr bool is_uint64_t_convertible() { @@ -201,18 +203,18 @@ } static void denormalizeStringImpl(SmallVectorImpl &Args, - const char *Spelling, + Twine Spelling, CompilerInvocation::StringAllocator SA, Option::OptionClass OptClass, unsigned, Twine Value) { switch (OptClass) { case Option::SeparateClass: case Option::JoinedOrSeparateClass: - Args.push_back(Spelling); + Args.push_back(SA(Spelling)); Args.push_back(SA(Value)); break; case Option::JoinedClass: - Args.push_back(SA(Twine(Spelling) + Value)); + Args.push_back(SA(Spelling + Value)); break; default: llvm_unreachable("Cannot denormalize an option with option class " @@ -222,7 +224,7 @@ template static void -denormalizeString(SmallVectorImpl &Args, const char *Spelling, +denormalizeString(SmallVectorImpl &Args, Twine Spelling, CompilerInvocation::StringAllocator SA, Option::OptionClass OptClass, unsigned TableIndex, T Value) { denormalizeStringImpl(Args, Spelling, SA, OptClass, TableIndex, Twine(Value)); @@ -523,9 +525,63 @@ return 0; } -static std::string GetOptName(llvm::opt::OptSpecifier OptSpecifier) { - static const OptTable &OptTable = getDriverOptTable(); - return OptTable.getOption(OptSpecifier).getPrefixedName(); +static void GenerateArg(SmallVectorImpl &Args, + llvm::opt::OptSpecifier OptSpecifier, + CompilerInvocation::StringAllocator SA) { + Option Opt = getDriverOptTable().getOption(OptSpecifier); + denormalizeSimpleFlag(Args, Opt.getPrefix() + Opt.getName(), SA, + Option::OptionClass::FlagClass, 0); +} + +static void GenerateArg(SmallVectorImpl &Args, + llvm::opt::OptSpecifier OptSpecifier, + const Twine &Value, + CompilerInvocation::StringAllocator SA) { + Option Opt = getDriverOptTable().getOption(OptSpecifier); + denormalizeString(Args, Opt.getPrefix() + Opt.getName(), SA, Opt.getKind(), 0, + Value); +} + +template +void RoundTrip(ParseFn &&Parse, GenerateFn &&Generate, ResetFn &&Reset, + CompilerInvocation &Res, ArgList &OriginalArgs) { + // Set up the string allocator for generating command line arguments. + SmallVector GeneratedArgsStorage; + auto SA = [&GeneratedArgsStorage](const Twine &Arg) -> const char * { + return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); + }; + + // Parse the original arguments and records which options the parser queried. + llvm::DenseSet QueriedBefore = OriginalArgs.getQueriedOpts(); + Parse(Res, OriginalArgs); + llvm::DenseSet QueriedAfter = OriginalArgs.getQueriedOpts(); + llvm::DenseSet QueriedDuring = + llvm::set_difference(QueriedAfter, QueriedBefore); + + // Create an empty string list. + ArgStringList GeneratedArgStringList; + // Add all original arguments that were not queried by the parser. + OriginalArgs.AddAllArgsExcept(GeneratedArgStringList, QueriedDuring); + // Invoke the generator. If implemented correctly, it should complete the + // string list with arguments the parser queried, and have the same + // semantics as the original arguments. + Generate(Res, GeneratedArgStringList, SA); + + // Construct InputArgList instance from the generated string list. + const OptTable &Opts = getDriverOptTable(); + unsigned MissingArgIndex, MissingArgCount; + InputArgList GeneratedArgs = + Opts.ParseArgs(GeneratedArgStringList, MissingArgIndex, MissingArgCount, + options::CC1Option); + + // Reset the part of CompilerInvocation that the parser set up. + Reset(Res); + + // Run the parser again. If the generated arguments have the same semantics + // as the original arguments, this should set up the CompilerInvocation part + // in the same way the first parser call did. Let's verify that by using this + // for the rest of the compilation process. + Parse(Res, GeneratedArgs); } static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group, @@ -1812,9 +1868,9 @@ return Driver::GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR); } -static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts, - SmallVectorImpl &Args, - CompilerInvocation::StringAllocator SA) { +void CompilerInvocation::GenerateHeaderSearchArgs( + const HeaderSearchOptions &Opts, SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA) { const HeaderSearchOptions *HeaderSearchOpts = &Opts; #define HEADER_SEARCH_OPTION_WITH_MARSHALLING( \ PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ @@ -1826,11 +1882,102 @@ IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) #include "clang/Driver/Options.inc" #undef HEADER_SEARCH_OPTION_WITH_MARSHALLING + + if (Opts.UseLibcxx) + GenerateArg(Args, OPT_stdlib_EQ, "libc++", SA); + + if (!Opts.ModuleCachePath.empty()) + GenerateArg(Args, OPT_fmodules_cache_path, Opts.ModuleCachePath, SA); + + for (const auto &File : Opts.PrebuiltModuleFiles) + GenerateArg(Args, OPT_fmodule_file, File.first + "=" + File.second, SA); + + for (const auto &Path : Opts.PrebuiltModulePaths) + GenerateArg(Args, OPT_fprebuilt_module_path, Path, SA); + + for (const auto &Macro : Opts.ModulesIgnoreMacros) + GenerateArg(Args, OPT_fmodules_ignore_macro, Macro.val(), SA); + + auto matches = [](const HeaderSearchOptions::Entry &Entry, + frontend::IncludeDirGroup Group, bool IsFramework, + bool IgnoreSysRoot) { + return Entry.Group == Group && Entry.IsFramework == IsFramework && + Entry.IgnoreSysRoot == IgnoreSysRoot; + }; + + for (const HeaderSearchOptions::Entry &E : Opts.UserEntries) { + // Add -I..., -F..., and -index-header-map options in order. + + if (E.Group == frontend::IndexHeaderMap) + GenerateArg(Args, OPT_index_header_map, SA); + + Optional Opt; + + if (matches(E, frontend::IndexHeaderMap, true, true)) + Opt = OPT_F; + else if (matches(E, frontend::IndexHeaderMap, false, true)) + Opt = OPT_I; + else if (matches(E, frontend::Angled, true, true)) + Opt = OPT_F; + else if (matches(E, frontend::Angled, false, true)) + // Also handles [-iprefix=xx] -iwithprefixbefore=yy as -I[xx]yy. + Opt = OPT_I; + + if (Opt) { + GenerateArg(Args, *Opt, E.Path, SA); + continue; + } + + if (matches(E, frontend::After, false, true)) + // Also handles [-iprefix=xx] -iwithprefix=yy as -idirafter=[xx]yy. + Opt = OPT_idirafter; + else if (matches(E, frontend::Quoted, false, true)) + Opt = OPT_iquote; + else if (matches(E, frontend::System, false, true)) + Opt = OPT_isystem; + else if (matches(E, frontend::System, false, false)) + Opt = OPT_iwithsysroot; + else if (matches(E, frontend::System, true, true)) + Opt = OPT_iframework; + else if (matches(E, frontend::System, true, false)) + Opt = OPT_iframeworkwithsysroot; + + else if (matches(E, frontend::CSystem, false, true)) + // Add the paths for the various language specific isystem flags. + Opt = OPT_c_isystem; + else if (matches(E, frontend::CXXSystem, false, true)) + Opt = OPT_cxx_isystem; + else if (matches(E, frontend::ObjCSystem, false, true)) + Opt = OPT_objc_isystem; + else if (matches(E, frontend::ObjCXXSystem, false, true)) + Opt = OPT_objcxx_isystem; + + else if (matches(E, frontend::System, false, true)) + // Add the internal paths from a driver that detects standard include + // paths. + Opt = OPT_internal_isystem; + else if (matches(E, frontend::ExternCSystem, false, true)) + Opt = OPT_internal_externc_isystem; + else + llvm_unreachable("Tried to marshall invalid HeaderSearchOptions::Entry"); + + GenerateArg(Args, *Opt, E.Path, SA); + } + + // Add the path prefixes which are implicitly treated as being system headers. + for (const auto &P : Opts.SystemHeaderPrefixes) { + OptSpecifier Opt = P.IsSystemHeader ? OPT_system_header_prefix + : OPT_no_system_header_prefix; + GenerateArg(Args, Opt, P.Prefix, SA); + } + + for (const std::string &F : Opts.VFSOverlayFiles) + GenerateArg(Args, OPT_ivfsoverlay, F, SA); } -static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, - DiagnosticsEngine &Diags, - const std::string &WorkingDir) { +static void ParseHeaderSearchArgsImpl(HeaderSearchOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + const std::string &WorkingDir) { HeaderSearchOptions *HeaderSearchOpts = &Opts; bool Success = true; @@ -1961,6 +2108,28 @@ Opts.AddVFSOverlayFile(A->getValue()); } +void CompilerInvocation::ParseHeaderSearchArgs(CompilerInvocation &Res, + HeaderSearchOptions &Opts, + ArgList &Args, + DiagnosticsEngine &Diags, + const std::string &WorkingDir) { + auto ParseHS = [&Diags, &WorkingDir](CompilerInvocation &Res, ArgList &Args) { + ParseHeaderSearchArgsImpl(Res.getHeaderSearchOpts(), Args, Diags, + WorkingDir); + }; + auto GenerateHS = [](CompilerInvocation &Res, + SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA) { + GenerateHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, SA); + }; + auto ResetHS = [](CompilerInvocation &Res) { + auto EmptyHeaderSearchOpts = std::make_shared(); + Res.HeaderSearchOpts.swap(EmptyHeaderSearchOpts); + }; + + RoundTrip(ParseHS, GenerateHS, ResetHS, Res, Args); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const llvm::Triple &T, std::vector &Includes, @@ -2214,9 +2383,9 @@ SmallVectorImpl &Args, CompilerInvocation::StringAllocator SA) { if (Opts.IncludeDefaultHeader) - Args.push_back(SA(GetOptName(OPT_finclude_default_header))); + GenerateArg(Args, OPT_finclude_default_header, SA); if (Opts.DeclareOpenCLBuiltins) - Args.push_back(SA(GetOptName(OPT_fdeclare_opencl_builtins))); + GenerateArg(Args, OPT_fdeclare_opencl_builtins, SA); } void CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, @@ -3042,7 +3211,8 @@ LangOpts.IsHeaderFile); ParseTargetArgs(Res.getTargetOpts(), Args, Diags); llvm::Triple T(Res.getTargetOpts().Triple); - ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags, + + ParseHeaderSearchArgs(Res, Res.getHeaderSearchOpts(), Args, Diags, Res.getFileSystemOpts().WorkingDir); if (DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR) { diff --git a/llvm/include/llvm/Option/ArgList.h b/llvm/include/llvm/Option/ArgList.h --- a/llvm/include/llvm/Option/ArgList.h +++ b/llvm/include/llvm/Option/ArgList.h @@ -137,6 +137,16 @@ /// The first and last index of each different OptSpecifier ID. DenseMap OptRanges; + /// The OptSpecifiers that were queried from this argument list. + mutable DenseSet QueriedOpts; + + /// Record the queried OptSpecifiers. + template + void recordQueriedOpts(OptSpecifiers... Ids) const { + SmallVector OptsSpecifiers({toOptSpecifier(Ids).getID()...}); + QueriedOpts.insert(OptsSpecifiers.begin(), OptsSpecifiers.end()); + } + /// Get the range of indexes in which options with the specified IDs might /// reside, or (0, 0) if there are no such options. OptRange getRange(std::initializer_list Ids) const; @@ -203,6 +213,7 @@ template iterator_range> filtered(OptSpecifiers ...Ids) const { + recordQueriedOpts(Ids...); OptRange Range = getRange({toOptSpecifier(Ids)...}); auto B = Args.begin() + Range.first; auto E = Args.begin() + Range.second; @@ -214,6 +225,7 @@ template iterator_range> filtered_reverse(OptSpecifiers ...Ids) const { + recordQueriedOpts(Ids...); OptRange Range = getRange({toOptSpecifier(Ids)...}); auto B = Args.rend() - Range.second; auto E = Args.rend() - Range.first; @@ -308,6 +320,10 @@ A->render(*this, Output); } + /// AddAllArgsExcept - Render all arguments not matching any of the excluded + /// ids. + void AddAllArgsExcept(ArgStringList &Output, + const DenseSet &ExcludeIds) const; /// AddAllArgsExcept - Render all arguments matching any of the given ids /// and not matching any of the excluded ids. void AddAllArgsExcept(ArgStringList &Output, ArrayRef Ids, @@ -342,6 +358,9 @@ /// void ClaimAllArgs() const; + /// Return the OptSpecifiers queried from this argument list. + const DenseSet &getQueriedOpts() const { return QueriedOpts; } + /// @} /// @name Arg Synthesis /// @{ diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp --- a/llvm/lib/Option/ArgList.cpp +++ b/llvm/lib/Option/ArgList.cpp @@ -90,11 +90,22 @@ } std::vector ArgList::getAllArgValues(OptSpecifier Id) const { + recordQueriedOpts(Id); SmallVector Values; AddAllArgValues(Values, Id); return std::vector(Values.begin(), Values.end()); } +void ArgList::AddAllArgsExcept(ArgStringList &Output, + const DenseSet &ExcludeIds) const { + for (const Arg *Arg : *this) { + if (!ExcludeIds.contains(Arg->getOption().getID())) { + Arg->claim(); + Arg->render(*this, Output); + } + } +} + void ArgList::AddAllArgsExcept(ArgStringList &Output, ArrayRef Ids, ArrayRef ExcludeIds) const {