diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -949,6 +949,11 @@ 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 @@ // 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.h b/clang/lib/Driver/ToolChains/Flang.h --- a/clang/lib/Driver/ToolChains/Flang.h +++ b/clang/lib/Driver/ToolChains/Flang.h @@ -39,6 +39,13 @@ /// \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/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -30,6 +30,10 @@ 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 @@ 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/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h --- a/flang/include/flang/Frontend/CompilerInstance.h +++ b/flang/include/flang/Frontend/CompilerInstance.h @@ -30,6 +30,8 @@ std::shared_ptr parsing_; + std::unique_ptr semanticsContext_; + /// The stream for diagnostics from Semantics llvm::raw_ostream *semaOutputStream_ = &llvm::errs(); @@ -100,6 +102,9 @@ /// } /// @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 --- 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 @@ // 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 @@ 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 @@ /// 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 --- a/flang/lib/Frontend/CompilerInstance.cpp +++ b/flang/lib/Frontend/CompilerInstance.cpp @@ -24,8 +24,10 @@ : 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 @@ 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 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -221,6 +221,25 @@ 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 @@ 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 @@ 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 @@ 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 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -109,10 +109,6 @@ 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 @@ 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 --- 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 --- 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 --- 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 --- /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