From 985a42fdf8ae3117442ea129b684569fa6942a71 Mon Sep 17 00:00:00 2001 From: Arnamoy Bhattacharyya Date: Thu, 4 Feb 2021 16:13:04 +0000 Subject: [PATCH] [flang][driver] Add support for `-J/-module-dir` Add support for option -J/-module-dir in the new Flang driver. This will allow for including module files in other directories, as the default search path is currently the working folder. This also provides an option of storing the output module in the specified folder. Differential Revision: https://reviews.llvm.org/D95448 --- clang/include/clang/Driver/Options.td | 7 +++- clang/lib/Driver/ToolChains/Flang.cpp | 7 ++++ clang/lib/Driver/ToolChains/Flang.h | 7 ++++ .../include/flang/Frontend/CompilerInstance.h | 5 +++ .../flang/Frontend/CompilerInvocation.h | 12 +++++++ flang/lib/Frontend/CompilerInstance.cpp | 8 +++-- flang/lib/Frontend/CompilerInvocation.cpp | 36 +++++++++++++++++++ flang/lib/Frontend/FrontendActions.cpp | 8 +---- .../test/Flang-Driver/driver-help-hidden.f90 | 1 + flang/test/Flang-Driver/driver-help.f90 | 2 ++ flang/test/Flang-Driver/include-module.f90 | 19 ++++++++++ flang/test/Flang-Driver/write-module.f90 | 10 ++++++ 12 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 flang/test/Flang-Driver/write-module.f90 diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index a700cab350b71..7bd9a8ab40dc8 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -949,6 +949,11 @@ def dependency_dot : Separate<["-"], "dependency-dot">, Flags<[CC1Option]>, def module_dependency_dir : Separate<["-"], "module-dependency-dir">, Flags<[CC1Option]>, HelpText<"Directory to dump module dependencies to">, MarshallingInfoString>; +def module_dir : Separate<["-"], "module-dir">, Flags<[FlangOption,FC1Option]>, MetaVarName<"">, + HelpText<"Put MODULE files in ">, + DocBrief<[{This option specifies where to put .mod files for compiled modules. +It is also added to the list of directories to be searched by an USE statement. +The default is the current directory.}]>; def dsym_dir : JoinedOrSeparate<["-"], "dsym-dir">, Flags<[NoXarchOption, RenderAsInput]>, HelpText<"Directory to output dSYM's (if any) to">, MetaVarName<"">; @@ -4121,7 +4126,7 @@ defm devirtualize_speculatively : BooleanFFlag<"devirtualize-speculatively">, // Generic gfortran options. def A_DASH : Joined<["-"], "A-">, Group; -def J : JoinedOrSeparate<["-"], "J">, Flags<[RenderJoined]>, Group; +def J : JoinedOrSeparate<["-"], "J">, Flags<[RenderJoined,FlangOption,FC1Option]>, Group, Alias; def cpp : Flag<["-"], "cpp">, Group; def nocpp : Flag<["-"], "nocpp">, Group; def static_libgfortran : Flag<["-"], "static-libgfortran">, Group; diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 1b8d03406f308..dee2148c231c3 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -30,6 +30,10 @@ void Flang::AddPreprocessingOptions(const ArgList &Args, Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I}); } +void Flang::AddOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const { + Args.AddAllArgs(CmdArgs, options::OPT_module_dir); +} + void Flang::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { @@ -87,6 +91,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, AddFortranDialectOptions(Args, CmdArgs); + // Add other compile options + AddOtherOptions(Args, CmdArgs); + if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); diff --git a/clang/lib/Driver/ToolChains/Flang.h b/clang/lib/Driver/ToolChains/Flang.h index a6efa9ae9bda3..efbdbe854e24f 100644 --- a/clang/lib/Driver/ToolChains/Flang.h +++ b/clang/lib/Driver/ToolChains/Flang.h @@ -39,6 +39,13 @@ class LLVM_LIBRARY_VISIBILITY Flang : public Tool { /// \param [out] CmdArgs The list of output command arguments void AddPreprocessingOptions(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + /// Extract other compilation options from the driver arguments and add them + /// to the command arguments. + /// + /// \param [in] Args The list of input driver arguments + /// \param [out] CmdArgs The list of output command arguments + void AddOtherOptions(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; public: Flang(const ToolChain &TC); diff --git a/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h index 79a05c0ddbbeb..a6f5fa970ddb0 100644 --- a/flang/include/flang/Frontend/CompilerInstance.h +++ b/flang/include/flang/Frontend/CompilerInstance.h @@ -30,6 +30,8 @@ class CompilerInstance { std::shared_ptr parsing_; + std::unique_ptr semanticsContext_; + /// The stream for diagnostics from Semantics llvm::raw_ostream *semaOutputStream_ = &llvm::errs(); @@ -100,6 +102,9 @@ class CompilerInstance { /// } /// @name Semantic analysis /// { + Fortran::semantics::SemanticsContext &semanticsContext() const { + return *semanticsContext_; + } /// Replace the current stream for verbose output. void set_semaOutputStream(llvm::raw_ostream &Value); diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h index 5136de54e06b0..bc149a3044d8e 100644 --- a/flang/include/flang/Frontend/CompilerInvocation.h +++ b/flang/include/flang/Frontend/CompilerInvocation.h @@ -11,6 +11,7 @@ #include "flang/Frontend/FrontendOptions.h" #include "flang/Frontend/PreprocessorOptions.h" #include "flang/Parser/parsing.h" +#include "flang/Semantics/semantics.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "llvm/Option/ArgList.h" @@ -60,6 +61,11 @@ class CompilerInvocation : public CompilerInvocationBase { // of options. Fortran::parser::Options parserOpts_; + /// Semantic options + // TODO: Merge with or translate to frontendOpts_. We shouldn't need two sets + // of options. + std::string moduleDir_ = "."; + public: CompilerInvocation() = default; @@ -69,6 +75,9 @@ class CompilerInvocation : public CompilerInvocationBase { Fortran::parser::Options &fortranOpts() { return parserOpts_; } const Fortran::parser::Options &fortranOpts() const { return parserOpts_; } + std::string &moduleDir() { return moduleDir_; } + const std::string &moduleDir() const { return moduleDir_; } + /// Create a compiler invocation from a list of input options. /// \returns true on success. /// \returns false if an error was encountered while parsing the arguments @@ -87,6 +96,9 @@ class CompilerInvocation : public CompilerInvocationBase { /// Set the Fortran options to user-specified values. /// These values are found in the preprocessor options. void setFortranOpts(); + + /// Set the Semantic Options + void setSemanticsOpts(Fortran::semantics::SemanticsContext &); }; } // end namespace Fortran::frontend diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp index 9640cd78797dd..2a44425c220e8 100644 --- a/flang/lib/Frontend/CompilerInstance.cpp +++ b/flang/lib/Frontend/CompilerInstance.cpp @@ -24,8 +24,10 @@ CompilerInstance::CompilerInstance() : invocation_(new CompilerInvocation()), allSources_(new Fortran::parser::AllSources()), allCookedSources_(new Fortran::parser::AllCookedSources(*allSources_)), - parsing_(new Fortran::parser::Parsing(*allCookedSources_)) { - + parsing_(new Fortran::parser::Parsing(*allCookedSources_)), + semanticsContext_(new Fortran::semantics::SemanticsContext( + *(new Fortran::common::IntrinsicTypeDefaultKinds()), + *(new common::LanguageFeatureControl()), *allCookedSources_)) { // TODO: This is a good default during development, but ultimately we should // give the user the opportunity to specify this. allSources_->set_encoding(Fortran::parser::Encoding::UTF_8); @@ -144,6 +146,8 @@ bool CompilerInstance::ExecuteAction(FrontendAction &act) { invoc.SetDefaultFortranOpts(); // Update the fortran options based on user-based input. invoc.setFortranOpts(); + // Set semantic options + invoc.setSemanticsOpts(this->semanticsContext()); // Run the frontend action `act` for every input file. for (const FrontendInputFile &fif : frontendOpts().inputs_) { diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index 563953f76969b..445feaac1b309 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -221,6 +221,25 @@ static void parsePreprocessorArgs( opts.searchDirectoriesFromDashI.emplace_back(currentArg->getValue()); } +/// Parses all semantic related arguments and populates the variables +/// options accordingly. +static void parseSemaArgs(std::string &moduleDir, llvm::opt::ArgList &args, + clang::DiagnosticsEngine &diags) { + + auto moduleDirList = + args.getAllArgValues(clang::driver::options::OPT_module_dir); + // User can only specify -J/-module-dir once + // https://gcc.gnu.org/onlinedocs/gfortran/Directory-Options.html + if (moduleDirList.size() > 1) { + const unsigned diagID = + diags.getCustomDiagID(clang::DiagnosticsEngine::Error, + "Only one '-module-dir/-J' option allowed"); + diags.Report(diagID); + } + if (moduleDirList.size() == 1) + moduleDir = moduleDirList[0]; +} + bool CompilerInvocation::CreateFromArgs(CompilerInvocation &res, llvm::ArrayRef commandLineArgs, clang::DiagnosticsEngine &diags) { @@ -250,6 +269,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &res, ParseFrontendArgs(res.frontendOpts(), args, diags); // Parse the preprocessor args parsePreprocessorArgs(res.preprocessorOpts(), args); + // Parse semantic args + parseSemaArgs(res.moduleDir(), args, diags); return success; } @@ -314,6 +335,7 @@ void CompilerInvocation::setFortranOpts() { auto &fortranOptions = fortranOpts(); const auto &frontendOptions = frontendOpts(); const auto &preprocessorOptions = preprocessorOpts(); + auto &moduleDirJ = moduleDir(); if (frontendOptions.fortranForm_ != FortranForm::Unknown) { fortranOptions.isFixedForm = @@ -327,4 +349,18 @@ void CompilerInvocation::setFortranOpts() { fortranOptions.searchDirectories.end(), preprocessorOptions.searchDirectoriesFromDashI.begin(), preprocessorOptions.searchDirectoriesFromDashI.end()); + + // Add the directory supplied through -J/-module-dir to the list of search + // directories + if (moduleDirJ.compare(".") != 0) + fortranOptions.searchDirectories.emplace_back(moduleDirJ); +} + +void CompilerInvocation::setSemanticsOpts( + Fortran::semantics::SemanticsContext &semaCtxt) { + auto &fortranOptions = fortranOpts(); + auto &moduleDirJ = moduleDir(); + semaCtxt.set_moduleDirectory(moduleDirJ) + .set_searchDirectories(fortranOptions.searchDirectories); + return; } diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index bbf80045286d0..61e4a7dc3b2e1 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -109,10 +109,6 @@ void PrintPreprocessedAction::ExecuteAction() { void ParseSyntaxOnlyAction::ExecuteAction() { CompilerInstance &ci = this->instance(); - // TODO: These should be specifiable by users. For now just use the defaults. - common::LanguageFeatureControl features; - Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; - // Parse. In case of failure, report and return. ci.parsing().Parse(llvm::outs()); @@ -132,10 +128,8 @@ void ParseSyntaxOnlyAction::ExecuteAction() { auto &parseTree{*ci.parsing().parseTree()}; // Prepare semantics - Fortran::semantics::SemanticsContext semanticsContext{ - defaultKinds, features, ci.allCookedSources()}; Fortran::semantics::Semantics semantics{ - semanticsContext, parseTree, ci.parsing().cooked().AsCharBlock()}; + ci.semanticsContext(), parseTree, ci.parsing().cooked().AsCharBlock()}; // Run semantic checks semantics.Perform(); diff --git a/flang/test/Flang-Driver/driver-help-hidden.f90 b/flang/test/Flang-Driver/driver-help-hidden.f90 index 93fbac87b80f4..81d63b09af361 100644 --- a/flang/test/Flang-Driver/driver-help-hidden.f90 +++ b/flang/test/Flang-Driver/driver-help-hidden.f90 @@ -30,6 +30,7 @@ ! CHECK-NEXT: -fno-color-diagnostics Disable colors in diagnostics ! CHECK-NEXT: -help Display available options ! CHECK-NEXT: -I Add directory to the end of the list of include search paths +! CHECK-NEXT: -module-dir Put MODULE files in ! CHECK-NEXT: -o Write output to ! CHECK-NEXT: -test-io Run the InputOuputTest action. Use for development and testing only. ! CHECK-NEXT: -U Undefine macro diff --git a/flang/test/Flang-Driver/driver-help.f90 b/flang/test/Flang-Driver/driver-help.f90 index bd300df7e2f09..4675dfa8a38a3 100644 --- a/flang/test/Flang-Driver/driver-help.f90 +++ b/flang/test/Flang-Driver/driver-help.f90 @@ -30,6 +30,7 @@ ! HELP-NEXT: -fno-color-diagnostics Disable colors in diagnostics ! HELP-NEXT: -help Display available options ! HELP-NEXT: -I Add directory to the end of the list of include search paths +! HELP-NEXT: -module-dir Put MODULE files in ! HELP-NEXT: -o Write output to ! HELP-NEXT: -U Undefine macro ! HELP-NEXT: --version Print version information @@ -49,6 +50,7 @@ ! HELP-FC1-NEXT: -ffree-form Process source files in free form ! HELP-FC1-NEXT: -help Display available options ! HELP-FC1-NEXT: -I Add directory to the end of the list of include search paths +! HELP-FC1-NEXT: -module-dir Put MODULE files in ! HELP-FC1-NEXT: -o Write output to ! HELP-FC1-NEXT: -U Undefine macro ! HELP-FC1-NEXT: --version Print version information diff --git a/flang/test/Flang-Driver/include-module.f90 b/flang/test/Flang-Driver/include-module.f90 index e9530cc04c8cf..3abf6c8afa691 100644 --- a/flang/test/Flang-Driver/include-module.f90 +++ b/flang/test/Flang-Driver/include-module.f90 @@ -7,12 +7,26 @@ !-------------------------- ! RUN: not %flang-new -fsyntax-only -I %S/Inputs -I %S/Inputs/module-dir %s 2>&1 | FileCheck %s --check-prefix=INCLUDED ! RUN: not %flang-new -fsyntax-only -I %S/Inputs %s 2>&1 | FileCheck %s --check-prefix=SINGLEINCLUDE +! RUN: not %flang-new -fsyntax-only -I %S/Inputs -J %S/Inputs/module-dir %s 2>&1 | FileCheck %s --check-prefix=INCLUDED +! RUN: not %flang-new -fsyntax-only -J %S/Inputs %s 2>&1 | FileCheck %s --check-prefix=SINGLEINCLUDE +! RUN: not %flang-new -fsyntax-only -I %S/Inputs -module-dir %S/Inputs/module-dir %s 2>&1 | FileCheck %s --check-prefix=INCLUDED +! RUN: not %flang-new -fsyntax-only -module-dir %S/Inputs %s 2>&1 | FileCheck %s --check-prefix=SINGLEINCLUDE +! RUN: not %flang-new -fsyntax-only -J %S/Inputs/module-dir -J %S/Inputs/ %s 2>&1 | FileCheck %s --check-prefix=DOUBLEINCLUDE +! RUN: not %flang-new -fsyntax-only -J %S/Inputs/module-dir -module-dir %S/Inputs/ %s 2>&1 | FileCheck %s --check-prefix=DOUBLEINCLUDE +! RUN: not %flang-new -fsyntax-only -module-dir %S/Inputs/module-dir -J%S/Inputs/ %s 2>&1 | FileCheck %s --check-prefix=DOUBLEINCLUDE !----------------------------------------- ! FRONTEND FLANG DRIVER (flang-new -fc1) !----------------------------------------- ! RUN: not %flang-new -fc1 -fsyntax-only -I %S/Inputs -I %S/Inputs/module-dir %s 2>&1 | FileCheck %s --check-prefix=INCLUDED ! RUN: not %flang-new -fc1 -fsyntax-only -I %S/Inputs %s 2>&1 | FileCheck %s --check-prefix=SINGLEINCLUDE +! RUN: not %flang-new -fc1 -fsyntax-only -I %S/Inputs -J %S/Inputs/module-dir %s 2>&1 | FileCheck %s --check-prefix=INCLUDED +! RUN: not %flang-new -fc1 -fsyntax-only -J %S/Inputs %s 2>&1 | FileCheck %s --check-prefix=SINGLEINCLUDE +! RUN: not %flang-new -fc1 -fsyntax-only -I %S/Inputs -module-dir %S/Inputs/module-dir %s 2>&1 | FileCheck %s --check-prefix=INCLUDED +! RUN: not %flang-new -fc1 -fsyntax-only -module-dir %S/Inputs %s 2>&1 | FileCheck %s --check-prefix=SINGLEINCLUDE +! RUN: not %flang-new -fc1 -fsyntax-only -J %S/Inputs/module-dir -J %S/Inputs/ %s 2>&1 | FileCheck %s --check-prefix=DOUBLEINCLUDE +! RUN: not %flang-new -fc1 -fsyntax-only -J %S/Inputs/module-dir -module-dir %S/Inputs/ %s 2>&1 | FileCheck %s --check-prefix=DOUBLEINCLUDE +! RUN: not %flang-new -fc1 -fsyntax-only -module-dir %S/Inputs/module-dir -J%S/Inputs/ %s 2>&1 | FileCheck %s --check-prefix=DOUBLEINCLUDE !----------------------------------------- ! EXPECTED OUTPUT FOR MISSING MODULE FILE @@ -22,6 +36,11 @@ ! SINGLEINCLUDE-NOT:error: Derived type 't1' not found ! SINGLEINCLUDE:error: Derived type 't2' not found +!----------------------------------------- +! EXPECTED OUTPUT FOR MISSING MODULE FILE +!----------------------------------------- +! DOUBLEINCLUDE:error: Only one '-module-dir/-J' option allowed + !--------------------------------------- ! EXPECTED OUTPUT FOR ALL MODULES FOUND !--------------------------------------- diff --git a/flang/test/Flang-Driver/write-module.f90 b/flang/test/Flang-Driver/write-module.f90 new file mode 100644 index 0000000000000..a0e7064736407 --- /dev/null +++ b/flang/test/Flang-Driver/write-module.f90 @@ -0,0 +1,10 @@ +! RUN: mkdir -p %t/dir-f18 && %f18 -fparse-only -I tools/flang/include/flang -module %t/dir-f18 %s 2>&1 +! RUN: ls %t/dir-f18/testmodule.mod && not ls %t/testmodule.mod + +! RUN: mkdir -p %t/dir-flang-new && %flang-new -fsyntax-only -module-dir %t/dir-flang-new %s 2>&1 +! RUN: ls %t/dir-flang-new/testmodule.mod && not ls %t/testmodule.mod + +module testmodule + type::t2 + end type +end