diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Driver/Driver.cpp | 103 | ||||
-rw-r--r-- | clang/lib/Driver/Job.cpp | 23 | ||||
-rw-r--r-- | clang/lib/Driver/Tools.cpp | 70 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInstance.cpp | 36 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInvocation.cpp | 1 |
5 files changed, 227 insertions, 6 deletions
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index eaf29de2aaf..8965d3d6c3c 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1452,6 +1452,66 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, } } + // Diagnose unsupported forms of /Yc /Yu. Ignore /Yc/Yu for now if: + // * no filename after it + // * both /Yc and /Yu passed but with different filenames + // * corresponding file not also passed as /FI + Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc); + Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu); + if (YcArg && YcArg->getValue()[0] == '\0') { + Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YcArg->getSpelling(); + Args.eraseArg(options::OPT__SLASH_Yc); + YcArg = nullptr; + } + if (YuArg && YuArg->getValue()[0] == '\0') { + Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YuArg->getSpelling(); + Args.eraseArg(options::OPT__SLASH_Yu); + YuArg = nullptr; + } + if (YcArg && YuArg && strcmp(YcArg->getValue(), YuArg->getValue()) != 0) { + Diag(clang::diag::warn_drv_ycyu_different_arg_clang_cl); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + if (YcArg || YuArg) { + StringRef Val = YcArg ? YcArg->getValue() : YuArg->getValue(); + bool FoundMatchingInclude = false; + for (const Arg *Inc : Args.filtered(options::OPT_include)) { + // FIXME: Do case-insensitive matching and consider / and \ as equal. + if (Inc->getValue() == Val) + FoundMatchingInclude = true; + } + if (!FoundMatchingInclude) { + Diag(clang::diag::warn_drv_ycyu_no_fi_arg_clang_cl) + << (YcArg ? YcArg : YuArg)->getSpelling(); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + } + if (YcArg && Inputs.size() > 1) { + Diag(clang::diag::warn_drv_yc_multiple_inputs_clang_cl); + Args.eraseArg(options::OPT__SLASH_Yc); + YcArg = nullptr; + } + if (Args.hasArg(options::OPT__SLASH_Y_)) { + // /Y- disables all pch handling. Rather than check for it everywhere, + // just remove clang-cl pch-related flags here. + Args.eraseArg(options::OPT__SLASH_Fp); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + // FIXME: For now, only enable pch support if an internal flag is passed too. + // Remove this once pch support has stabilitzed. + if (!Args.hasArg(options::OPT__SLASH_internal_enable_pch)) { + Args.eraseArg(options::OPT__SLASH_Fp); + Args.eraseArg(options::OPT__SLASH_Yc); + Args.eraseArg(options::OPT__SLASH_Yu); + YcArg = YuArg = nullptr; + } + // Construct the actions to perform. ActionList LinkerInputs; @@ -1463,6 +1523,25 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, PL.clear(); types::getCompilationPhases(InputType, PL); + if (YcArg) { + // Add a separate precompile phase for the compile phase. + if (FinalPhase >= phases::Compile) { + llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PCHPL; + types::getCompilationPhases(types::TY_CXXHeader, PCHPL); + Arg *PchInputArg = MakeInputArg(Args, Opts, YcArg->getValue()); + + // Build the pipeline for the pch file. + Action *ClangClPch = C.MakeAction<InputAction>(*PchInputArg, InputType); + for (phases::ID Phase : PCHPL) + ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch); + assert(ClangClPch); + Actions.push_back(ClangClPch); + // The driver currently exits after the first failed command. This + // relies on that behavior, to make sure if the pch generation fails, + // the main compilation won't run. + } + } + // If the first step comes after the final phase we are doing as part of // this compilation, warn the user about it. phases::ID InitialPhase = PL[0]; @@ -2109,8 +2188,11 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, Output += "-"; Output.append(BoundArch); NamedOutput = C.getArgs().MakeArgString(Output.c_str()); - } else + } else { NamedOutput = getDefaultImageName(); + } + } else if (JA.getType() == types::TY_PCH && IsCLMode()) { + NamedOutput = C.getArgs().MakeArgString(GetClPchPath(C, BaseName).c_str()); } else { const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode()); assert(Suffix && "All types used for output should have a suffix."); @@ -2276,6 +2358,25 @@ std::string Driver::GetTemporaryPath(StringRef Prefix, return Path.str(); } +std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const { + SmallString<128> Output; + if (Arg *FpArg = C.getArgs().getLastArg(options::OPT__SLASH_Fp)) { + // FIXME: If anybody needs it, implement this obscure rule: + // "If you specify a directory without a file name, the default file name + // is VCx0.pch., where x is the major version of Visual C++ in use." + Output = FpArg->getValue(); + + // "If you do not specify an extension as part of the path name, an + // extension of .pch is assumed. " + if (!llvm::sys::path::has_extension(Output)) + Output += ".pch"; + } else { + Output = BaseName; + llvm::sys::path::replace_extension(Output, ".pch"); + } + return Output.str(); +} + const ToolChain &Driver::getToolChain(const ArgList &Args, const llvm::Triple &Target) const { diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 22904e5398a..3eb23c9ee4c 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -297,6 +297,29 @@ int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg, return SecondaryStatus; } +ForceSuccessCommand::ForceSuccessCommand(const Action &Source_, + const Tool &Creator_, + const char *Executable_, + const ArgStringList &Arguments_, + ArrayRef<InputInfo> Inputs) + : Command(Source_, Creator_, Executable_, Arguments_, Inputs) {} + +void ForceSuccessCommand::Print(raw_ostream &OS, const char *Terminator, + bool Quote, CrashReportInfo *CrashInfo) const { + Command::Print(OS, "", Quote, CrashInfo); + OS << " || (exit 0)" << Terminator; +} + +int ForceSuccessCommand::Execute(const StringRef **Redirects, + std::string *ErrMsg, + bool *ExecutionFailed) const { + int Status = Command::Execute(Redirects, ErrMsg, ExecutionFailed); + (void)Status; + if (ExecutionFailed) + *ExecutionFailed = false; + return 0; +} + void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { for (const auto &Job : *this) diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index a0fceb9672a..0f5c700ce61 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -386,9 +386,71 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, // wonky, but we include looking for .gch so we can support seamless // replacement into a build system already set up to be generating // .gch files. + int YcIndex = -1, YuIndex = -1; + { + int AI = -1; + const Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc); + const Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu); + for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) { + // Walk the whole i_Group and skip non "-include" flags so that the index + // here matches the index in the next loop below. + ++AI; + if (!A->getOption().matches(options::OPT_include)) + continue; + if (YcArg && strcmp(A->getValue(), YcArg->getValue()) == 0) + YcIndex = AI; + if (YuArg && strcmp(A->getValue(), YuArg->getValue()) == 0) + YuIndex = AI; + } + } + if (isa<PrecompileJobAction>(JA) && YcIndex != -1) { + Driver::InputList Inputs; + D.BuildInputs(getToolChain(), C.getArgs(), Inputs); + assert(Inputs.size() == 1 && "Need one input when building pch"); + CmdArgs.push_back(Args.MakeArgString(Twine("-find-pch-source=") + + Inputs[0].second->getValue())); + } + bool RenderedImplicitInclude = false; + int AI = -1; for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) { - if (A->getOption().matches(options::OPT_include)) { + ++AI; + + if (getToolChain().getDriver().IsCLMode()) { + // In clang-cl mode, /Ycfoo.h means that all code up to a foo.h + // include is compiled into foo.h, and everything after goes into + // the .obj file. /Yufoo.h means that all includes prior to and including + // foo.h are completely skipped and replaced with a use of the pch file + // for foo.h. (Each flag can have at most one value, multiple /Yc flags + // just mean that the last one wins.) If /Yc and /Yu are both present + // and refer to the same file, /Yc wins. + // Note that OPT__SLASH_FI gets mapped to OPT_include. + // FIXME: The code here assumes that /Yc and /Yu refer to the same file. + // cl.exe seems to support both flags with different values, but that + // seems strange (which flag does /Fp now refer to?), so don't implement + // that until someone needs that. + int PchIndex = YcIndex != -1 ? YcIndex : YuIndex; + if (PchIndex != -1) { + if (isa<PrecompileJobAction>(JA)) { + // When building the pch, skip all includes after the pch. + assert(YcIndex != -1 && PchIndex == YcIndex); + if (AI >= YcIndex) + continue; + } else { + // When using the pch, skip all includes prior to the pch. + if (AI < PchIndex) + continue; + if (AI == PchIndex) { + A->claim(); + CmdArgs.push_back("-include-pch"); + CmdArgs.push_back( + Args.MakeArgString(D.GetClPchPath(C, A->getValue()))); + continue; + } + } + } + } else if (A->getOption().matches(options::OPT_include)) { + // Handling of gcc-style gch precompiled headers. bool IsFirstImplicitInclude = !RenderedImplicitInclude; RenderedImplicitInclude = true; @@ -5654,6 +5716,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, getCLFallback()->GetCommand(C, JA, Output, Inputs, Args, LinkingOutput); C.addCommand(llvm::make_unique<FallbackCommand>( JA, *this, Exec, CmdArgs, Inputs, std::move(CLCommand))); + } else if (Args.hasArg(options::OPT__SLASH_fallback) && + isa<PrecompileJobAction>(JA)) { + // In /fallback builds, run the main compilation even if the pch generation + // fails, so that the main compilation's fallback to cl.exe runs. + C.addCommand(llvm::make_unique<ForceSuccessCommand>(JA, *this, Exec, + CmdArgs, Inputs)); } else { C.addCommand(llvm::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs)); } diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 04a4c7dbe68..c02eb761041 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -712,15 +712,18 @@ std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::createOutputFile( // Initialization Utilities bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){ - return InitializeSourceManager(Input, getDiagnostics(), - getFileManager(), getSourceManager(), - getFrontendOpts()); + return InitializeSourceManager( + Input, getDiagnostics(), getFileManager(), getSourceManager(), + hasPreprocessor() ? &getPreprocessor().getHeaderSearchInfo() : nullptr, + getFrontendOpts()); } +// static bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, DiagnosticsEngine &Diags, FileManager &FileMgr, SourceManager &SourceMgr, + HeaderSearch *HS, const FrontendOptions &Opts) { SrcMgr::CharacteristicKind Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User; @@ -737,7 +740,32 @@ bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, // Figure out where to get and map in the main file. if (InputFile != "-") { - const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/true); + const FileEntry *File; + if (Opts.FindPchSource.empty()) { + File = FileMgr.getFile(InputFile, /*OpenFile=*/true); + } else { + // When building a pch file in clang-cl mode, the .h file is built as if + // it was included by a cc file. Since the driver doesn't know about + // all include search directories, the frontend must search the input + // file through HeaderSearch here, as if it had been included by the + // cc file at Opts.FindPchSource. + const FileEntry *FindFile = FileMgr.getFile(Opts.FindPchSource); + if (!FindFile) { + Diags.Report(diag::err_fe_error_reading) << Opts.FindPchSource; + return false; + } + const DirectoryLookup *UnusedCurDir; + SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 16> + Includers; + Includers.push_back(std::make_pair(FindFile, FindFile->getDir())); + File = HS->LookupFile(InputFile, SourceLocation(), /*isAngled=*/false, + /*FromDir=*/nullptr, + /*CurDir=*/UnusedCurDir, Includers, + /*SearchPath=*/nullptr, + /*RelativePath=*/nullptr, + /*RequestingModule=*/nullptr, + /*SuggestedModule=*/nullptr, /*SkipCache=*/true); + } if (!File) { Diags.Report(diag::err_fe_error_reading) << InputFile; return false; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index b23eb354375..9bb33e7a766 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1107,6 +1107,7 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, = Args.getLastArgValue(OPT_foverride_record_layout_EQ); Opts.AuxTriple = llvm::Triple::normalize(Args.getLastArgValue(OPT_aux_triple)); + Opts.FindPchSource = Args.getLastArgValue(OPT_find_pch_source_EQ); if (const Arg *A = Args.getLastArg(OPT_arcmt_check, OPT_arcmt_modify, |