From f0e160dd674697ef9c569c1d1136746da70cf44e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 9 Jun 2017 21:24:02 +0000 Subject: [PATCH] Add -frewrite-imports flag. If specified, when preprocessing, the contents of imported .pcm files will be included in preprocessed output. The resulting preprocessed file can then be compiled standalone without the module sources or .pcm files. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@305116 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/Options.td | 4 + include/clang/Frontend/CompilerInstance.h | 3 + .../Frontend/PreprocessorOutputOptions.h | 2 + .../clang/Rewrite/Frontend/FrontendActions.h | 4 + lib/Driver/Driver.cpp | 2 + lib/Driver/ToolChains/Clang.cpp | 7 +- lib/Frontend/CompilerInstance.cpp | 10 +- lib/Frontend/CompilerInvocation.cpp | 1 + lib/Frontend/Rewrite/FrontendActions.cpp | 118 +++++++++++++++--- .../ExecuteCompilerInvocation.cpp | 3 +- test/Modules/preprocess-build-diamond.m | 26 ++++ 11 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 test/Modules/preprocess-build-diamond.m diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 7b9137e18c..6c51976e98 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -932,6 +932,10 @@ def frewrite_includes : Flag<["-"], "frewrite-includes">, Group, Flags<[CC1Option]>; def fno_rewrite_includes : Flag<["-"], "fno-rewrite-includes">, Group; +def frewrite_imports : Flag<["-"], "frewrite-imports">, Group, + Flags<[CC1Option]>; +def fno_rewrite_imports : Flag<["-"], "fno-rewrite-imports">, Group; + def frewrite_map_file : Separate<["-"], "frewrite-map-file">, Group, Flags<[ DriverOption, CC1Option ]>; diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index 3c7a8ef362..5b5c75298a 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -140,6 +140,9 @@ class CompilerInstance : public ModuleLoader { /// fly as part of this overall compilation action. std::map BuiltModules; + /// Should we delete the BuiltModules when we're done? + bool DeleteBuiltModules = true; + /// \brief The location of the module-import keyword for the last module /// import. SourceLocation LastModuleImportLoc; diff --git a/include/clang/Frontend/PreprocessorOutputOptions.h b/include/clang/Frontend/PreprocessorOutputOptions.h index 3261b66538..94afcd06a3 100644 --- a/include/clang/Frontend/PreprocessorOutputOptions.h +++ b/include/clang/Frontend/PreprocessorOutputOptions.h @@ -24,6 +24,7 @@ public: unsigned ShowMacros : 1; ///< Print macro definitions. unsigned ShowIncludeDirectives : 1; ///< Print includes, imports etc. within preprocessed output. unsigned RewriteIncludes : 1; ///< Preprocess include directives only. + unsigned RewriteImports : 1; ///< Include contents of transitively-imported modules. public: PreprocessorOutputOptions() { @@ -35,6 +36,7 @@ public: ShowMacros = 0; ShowIncludeDirectives = 0; RewriteIncludes = 0; + RewriteImports = 0; } }; diff --git a/include/clang/Rewrite/Frontend/FrontendActions.h b/include/clang/Rewrite/Frontend/FrontendActions.h index 9a8330fefa..5f83ac16fe 100644 --- a/include/clang/Rewrite/Frontend/FrontendActions.h +++ b/include/clang/Rewrite/Frontend/FrontendActions.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_REWRITE_FRONTEND_FRONTENDACTIONS_H #include "clang/Frontend/FrontendAction.h" +#include "llvm/Support/raw_ostream.h" namespace clang { class FixItRewriter; @@ -73,7 +74,10 @@ protected: }; class RewriteIncludesAction : public PreprocessorFrontendAction { + std::shared_ptr OutputStream; + class RewriteImportsListener; protected: + bool BeginSourceFileAction(CompilerInstance &CI) override; void ExecuteAction() override; }; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 63496af3f3..eb504fd4e5 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2672,6 +2672,8 @@ Action *Driver::ConstructPhaseAction(Compilation &C, const ArgList &Args, OutputTy = Input->getType(); if (!Args.hasFlag(options::OPT_frewrite_includes, options::OPT_fno_rewrite_includes, false) && + !Args.hasFlag(options::OPT_frewrite_imports, + options::OPT_fno_rewrite_imports, false) && !CCGenDiagnostics) OutputTy = types::getPreprocessedType(OutputTy); assert(OutputTy != types::TY_INVALID && diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 698c3aa326..6d3dbb5b52 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -4204,13 +4204,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } #endif + bool RewriteImports = Args.hasFlag(options::OPT_frewrite_imports, + options::OPT_fno_rewrite_imports, false); + if (RewriteImports) + CmdArgs.push_back("-frewrite-imports"); + // Enable rewrite includes if the user's asked for it or if we're generating // diagnostics. // TODO: Once -module-dependency-dir works with -frewrite-includes it'd be // nice to enable this when doing a crashdump for modules as well. if (Args.hasFlag(options::OPT_frewrite_includes, options::OPT_fno_rewrite_includes, false) || - (C.isForDiagnostics() && !HaveAnyModules)) + (C.isForDiagnostics() && (RewriteImports || !HaveAnyModules))) CmdArgs.push_back("-frewrite-includes"); // Only allow -traditional or -traditional-cpp outside in preprocessing modes. diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 406149b8c3..72a8c38180 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -667,8 +667,11 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) { llvm::sys::fs::remove(OF.Filename); } OutputFiles.clear(); - for (auto &Module : BuiltModules) - llvm::sys::fs::remove(Module.second); + if (DeleteBuiltModules) { + for (auto &Module : BuiltModules) + llvm::sys::fs::remove(Module.second); + BuiltModules.clear(); + } NonSeekStream.reset(); } @@ -1928,12 +1931,11 @@ void CompilerInstance::loadModuleFromSource(SourceLocation ImportLoc, llvm::MemoryBuffer::getMemBuffer(NullTerminatedSource.c_str())); Other.BuiltModules = std::move(BuiltModules); + Other.DeleteBuiltModules = false; }; auto PostBuildStep = [this](CompilerInstance &Other) { BuiltModules = std::move(Other.BuiltModules); - // Make sure the child build action doesn't delete the .pcms. - Other.BuiltModules.clear(); }; // Build the module, inheriting any modules that we've built locally. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 6e52e7e62e..bb635b7ad7 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2501,6 +2501,7 @@ static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); Opts.ShowIncludeDirectives = Args.hasArg(OPT_dI); Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes); + Opts.RewriteImports = Args.hasArg(OPT_frewrite_imports); Opts.UseLineDirectives = Args.hasArg(OPT_fuse_line_directives); } diff --git a/lib/Frontend/Rewrite/FrontendActions.cpp b/lib/Frontend/Rewrite/FrontendActions.cpp index 76eb07e981..45feffbcb5 100644 --- a/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/lib/Frontend/Rewrite/FrontendActions.cpp @@ -18,6 +18,11 @@ #include "clang/Rewrite/Frontend/ASTConsumers.h" #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/Module.h" +#include "clang/Serialization/ModuleManager.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -189,27 +194,112 @@ void RewriteTestAction::ExecuteAction() { DoRewriteTest(CI.getPreprocessor(), OS.get()); } -void RewriteIncludesAction::ExecuteAction() { - CompilerInstance &CI = getCompilerInstance(); - std::unique_ptr OS = - CI.createDefaultOutputFile(true, getCurrentFile()); - if (!OS) return; +class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { + CompilerInstance &CI; + std::weak_ptr Out; + + llvm::DenseSet Rewritten; + +public: + RewriteImportsListener(CompilerInstance &CI, std::shared_ptr Out) + : CI(CI), Out(Out) {} + + void visitModuleFile(StringRef Filename, + serialization::ModuleKind Kind) override { + auto *File = CI.getFileManager().getFile(Filename); + assert(File && "missing file for loaded module?"); + + // Only rewrite each module file once. + if (!Rewritten.insert(File).second) + return; + + serialization::ModuleFile *MF = + CI.getModuleManager()->getModuleManager().lookup(File); + assert(File && "missing module file for loaded module?"); + + // Not interested in PCH / preambles. + if (!MF->isModule()) + return; + + auto OS = Out.lock(); + assert(OS && "loaded module file after finishing rewrite action?"); + + (*OS) << "#pragma clang module build " << MF->ModuleName << "\n"; + + // Rewrite the contents of the module in a separate compiler instance. + CompilerInstance Instance(CI.getPCHContainerOperations(), + &CI.getPreprocessor().getPCMCache()); + Instance.setInvocation( + std::make_shared(CI.getInvocation())); + Instance.createDiagnostics( + new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + Instance.getFrontendOpts().Inputs.clear(); + Instance.getFrontendOpts().Inputs.emplace_back( + Filename, InputKind(InputKind::Unknown, InputKind::Precompiled)); + // Don't recursively rewrite imports. We handle them all at the top level. + Instance.getPreprocessorOutputOpts().RewriteImports = false; + + llvm::CrashRecoveryContext().RunSafelyOnThread([&]() { + RewriteIncludesAction Action; + Action.OutputStream = OS; + Instance.ExecuteAction(Action); + }); + + (*OS) << "#pragma clang module endbuild /*" << MF->ModuleName << "*/\n"; + } +}; + +bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) { + if (!OutputStream) { + OutputStream = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OutputStream) + return false; + } + + auto &OS = *OutputStream; // If we're preprocessing a module map, start by dumping the contents of the // module itself before switching to the input buffer. auto &Input = getCurrentInput(); if (Input.getKind().getFormat() == InputKind::ModuleMap) { if (Input.isFile()) { - (*OS) << "# 1 \""; - OS->write_escaped(Input.getFile()); - (*OS) << "\"\n"; + OS << "# 1 \""; + OS.write_escaped(Input.getFile()); + OS << "\"\n"; } - // FIXME: Include additional information here so that we don't need the - // original source files to exist on disk. - getCurrentModule()->print(*OS); - (*OS) << "#pragma clang module contents\n"; + getCurrentModule()->print(OS); + OS << "#pragma clang module contents\n"; + } + + // If we're rewriting imports, set up a listener to track when we import + // module files. + if (CI.getPreprocessorOutputOpts().RewriteImports) { + CI.createModuleManager(); + CI.getModuleManager()->addListener( + llvm::make_unique(CI, OutputStream)); + } + + return true; +} + +void RewriteIncludesAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + + // If we're rewriting imports, emit the module build output first rather + // than switching back and forth (potentially in the middle of a line). + if (CI.getPreprocessorOutputOpts().RewriteImports) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + + RewriteIncludesInInput(CI.getPreprocessor(), &OS, + CI.getPreprocessorOutputOpts()); + + (*OutputStream) << OS.str(); + } else { + RewriteIncludesInInput(CI.getPreprocessor(), OutputStream.get(), + CI.getPreprocessorOutputOpts()); } - RewriteIncludesInInput(CI.getPreprocessor(), OS.get(), - CI.getPreprocessorOutputOpts()); + OutputStream.reset(); } diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 1f7493c9e3..a7c140188b 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -85,7 +85,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case PrintDeclContext: return llvm::make_unique(); case PrintPreamble: return llvm::make_unique(); case PrintPreprocessedInput: { - if (CI.getPreprocessorOutputOpts().RewriteIncludes) + if (CI.getPreprocessorOutputOpts().RewriteIncludes || + CI.getPreprocessorOutputOpts().RewriteImports) return llvm::make_unique(); return llvm::make_unique(); } diff --git a/test/Modules/preprocess-build-diamond.m b/test/Modules/preprocess-build-diamond.m new file mode 100644 index 0000000000..b031a0a914 --- /dev/null +++ b/test/Modules/preprocess-build-diamond.m @@ -0,0 +1,26 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -E -o %t/diamond.mi -frewrite-imports +// RUN: FileCheck %s --input-file %t/diamond.mi +// RUN: %clang_cc1 -fmodules %t/diamond.mi -I. -verify + +// CHECK: {{^}}#pragma clang module build diamond_top +// CHECK: {{^}}module diamond_top { +// CHECK: {{^}}#pragma clang module contents + +// FIXME: @import does not work under -frewrite-includes / -frewrite-imports +// because we disable it when macro expansion is disabled. +#include "diamond_bottom.h" + +// expected-no-diagnostics +void test_diamond(int i, float f, double d, char c) { + top(&i); + left(&f); + right(&d); + bottom(&c); + top_left(&c); + left_and_right(&i); + struct left_and_right lr; + lr.left = 17; +} + -- 2.40.0