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,10 @@ const llvm::Triple &T, const std::string &OutputFile, const LangOptions &LangOptsRef); + + static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts, + SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA); }; 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" @@ -1812,9 +1814,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,6 +1828,118 @@ IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) #include "clang/Driver/Options.inc" #undef HEADER_SEARCH_OPTION_WITH_MARSHALLING + + const OptTable &OptTable = getDriverOptTable(); + + std::string Dash("-"); + std::string OptName; + + if (Opts.UseLibcxx) { + OptName = Dash + OptTable.getOptionName(OPT_stdlib_EQ); + Args.emplace_back(SA(OptName + "libc++")); + } + + if (!Opts.ModuleCachePath.empty()) { + OptName = Dash + OptTable.getOptionName(OPT_fmodules_cache_path); + Args.emplace_back(SA(OptName + Opts.ModuleCachePath)); + } + + OptName = Dash + OptTable.getOptionName(OPT_fmodule_file); + for (const auto &File : Opts.PrebuiltModuleFiles) + Args.emplace_back(SA(OptName + File.first + "=" + File.second)); + + OptName = Dash + OptTable.getOptionName(OPT_fprebuilt_module_path); + for (const auto &Path : Opts.PrebuiltModulePaths) + Args.emplace_back(SA(OptName + Path)); + + OptName = Dash + OptTable.getOptionName(OPT_fmodules_ignore_macro); + for (const auto &Macro : Opts.ModulesIgnoreMacros) + Args.emplace_back(SA(OptName + Macro.val())); + + 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) { + Optional Opt; + + // Add -I..., -F..., and -index-header-map options in order. + + if (E.Group == frontend::IndexHeaderMap) { + OptName = OptTable.getOptionName(OPT_index_header_map); + Args.emplace_back(SA(OptName)); + } + + 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) { + OptName = Dash + OptTable.getOptionName(*Opt); + Args.emplace_back(SA(OptName + E.Path)); + 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"); + + OptName = Dash + OptTable.getOptionName(*Opt); + Args.emplace_back(SA(OptName)); + Args.emplace_back(SA(E.Path)); + } + + // 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; + OptName = Dash + Dash + OptTable.getOptionName(Opt); + Args.emplace_back(SA(OptName + P.Prefix)); + } + + for (const std::string &F : Opts.VFSOverlayFiles) { + OptName = Dash + OptTable.getOptionName(OPT_ivfsoverlay); + Args.emplace_back(SA(OptName)); + Args.emplace_back(SA(F)); + } } static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, @@ -2992,6 +3106,48 @@ } } +template +void RoundTrip(ParseFn &&Parse, GenerateFn &&Generate, ResetFn &&Reset, + CompilerInvocation &Res, InputArgList &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); +} + bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, DiagnosticsEngine &Diags, @@ -3042,8 +3198,22 @@ LangOpts.IsHeaderFile); ParseTargetArgs(Res.getTargetOpts(), Args, Diags); llvm::Triple T(Res.getTargetOpts().Triple); - ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags, - Res.getFileSystemOpts().WorkingDir); + + auto ParseHS = [&Diags](CompilerInvocation &Res, InputArgList &Args) { + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags, + Res.getFileSystemOpts().WorkingDir); + }; + auto GenerateHS = [](CompilerInvocation &Res, + SmallVectorImpl &Args, + 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); if (DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the 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 {