// Generic gfortran options.
def A_DASH : Joined<["-"], "A-">, Group<gfortran_Group>;
-def cpp : Flag<["-"], "cpp">, Group<gfortran_Group>;
-def nocpp : Flag<["-"], "nocpp">, Group<gfortran_Group>;
def static_libgfortran : Flag<["-"], "static-libgfortran">, Group<gfortran_Group>;
// "f" options with values for gfortran.
//===----------------------------------------------------------------------===//
let Flags = [FC1Option, FlangOption, FlangOnlyOption] in {
+def cpp : Flag<["-"], "cpp">, Group<f_Group>,
+ HelpText<"Enable predefined and command line preprocessor macros">;
+def nocpp : Flag<["-"], "nocpp">, Group<f_Group>,
+ HelpText<"Disable predefined and command line preprocessor macros">;
def module_dir : Separate<["-"], "module-dir">, MetaVarName<"<dir>">,
HelpText<"Put MODULE files in <dir>">,
DocBrief<[{This option specifies where to put .mod files for compiled modules.
void Flang::AddPreprocessingOptions(const ArgList &Args,
ArgStringList &CmdArgs) const {
- Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I});
+ Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I,
+ options::OPT_cpp, options::OPT_nocpp});
}
void Flang::AddOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
/// Set the default predefinitions.
void setDefaultPredefinitions();
+ /// Collect the macro definitions from preprocessorOpts_ and prepare them for
+ /// the parser (i.e. copy into parserOpts_)
+ void collectMacroDefinitions();
+
/// Set the Fortran options to user-specified values.
/// These values are found in the preprocessor options.
void setFortranOpts();
/// \return True if the file extension should be processed as free form
bool isFreeFormSuffix(llvm::StringRef suffix);
+/// \param suffix The file extension
+/// \return True if the file should be preprocessed
+bool mustBePreprocessed(llvm::StringRef suffix);
+
enum class Language : uint8_t {
Unknown,
/// stdin this is never modified.
bool isFixedForm_ = false;
+ /// Must this file be preprocessed? Note that in Flang the preprocessor is
+ /// always run. This flag is used to control whether predefined and command
+ /// line preprocessor macros are enabled or not. In practice, this is
+ /// sufficient to implement gfortran`s logic controlled with `-cpp/-nocpp`.
+ unsigned mustBePreprocessed_ : 1;
+
public:
FrontendInputFile() = default;
FrontendInputFile(llvm::StringRef file, InputKind kind)
auto pathDotIndex{file.rfind(".")};
std::string pathSuffix{file.substr(pathDotIndex + 1)};
isFixedForm_ = isFixedFormSuffix(pathSuffix);
+ mustBePreprocessed_ = mustBePreprocessed(pathSuffix);
}
+
FrontendInputFile(const llvm::MemoryBuffer *buffer, InputKind kind)
: buffer_(buffer), kind_(kind) {}
bool IsFile() const { return !IsBuffer(); }
bool IsBuffer() const { return buffer_ != nullptr; }
bool IsFixedForm() const { return isFixedForm_; }
+ bool MustBePreprocessed() const { return mustBePreprocessed_; }
llvm::StringRef file() const {
assert(IsFile());
namespace Fortran::frontend {
+/// Communicates whether to include/exclude predefined and command
+/// line preprocessor macros
+enum class PPMacrosFlag : uint8_t {
+ /// Use the file extension to decide
+ Unknown,
+
+ Include,
+ Exclude
+};
+
/// This class is used for passing the various options used
/// in preprocessor initialization to the parser options.
class PreprocessorOptions {
// Search directories specified by the user with -fintrinsic-modules-path
std::vector<std::string> searchDirectoriesFromIntrModPath;
+ PPMacrosFlag macrosFlag_ = PPMacrosFlag::Unknown;
+
public:
PreprocessorOptions() {}
// Set some sane defaults for the frontend.
invoc.SetDefaultFortranOpts();
- invoc.setDefaultPredefinitions();
// Update the fortran options based on user-based input.
invoc.setFortranOpts();
// Set the encoding to read all input files in based on user input.
for (const auto *currentArg :
args.filtered(clang::driver::options::OPT_fintrinsic_modules_path))
opts.searchDirectoriesFromIntrModPath.emplace_back(currentArg->getValue());
+
+ // -cpp/-nocpp
+ if (const auto *currentArg = args.getLastArg(
+ clang::driver::options::OPT_cpp, clang::driver::options::OPT_nocpp))
+ opts.macrosFlag_ =
+ (currentArg->getOption().matches(clang::driver::options::OPT_cpp))
+ ? PPMacrosFlag::Include
+ : PPMacrosFlag::Exclude;
}
/// Parses all semantic related arguments and populates the variables
return success;
}
-/// Collect the macro definitions provided by the given preprocessor
-/// options into the parser options.
-///
-/// \param [in] ppOpts The preprocessor options
-/// \param [out] opts The fortran options
-static void collectMacroDefinitions(
- const PreprocessorOptions &ppOpts, Fortran::parser::Options &opts) {
+void CompilerInvocation::collectMacroDefinitions() {
+ auto &ppOpts = this->preprocessorOpts();
+
for (unsigned i = 0, n = ppOpts.macros.size(); i != n; ++i) {
llvm::StringRef macro = ppOpts.macros[i].first;
bool isUndef = ppOpts.macros[i].second;
// For an #undef'd macro, we only care about the name.
if (isUndef) {
- opts.predefinitions.emplace_back(
+ parserOpts_.predefinitions.emplace_back(
macroName.str(), std::optional<std::string>{});
continue;
}
llvm::StringRef::size_type End = macroBody.find_first_of("\n\r");
macroBody = macroBody.substr(0, End);
}
- opts.predefinitions.emplace_back(
+ parserOpts_.predefinitions.emplace_back(
macroName, std::optional<std::string>(macroBody.str()));
}
}
fortranOptions.features = frontendOptions.features_;
fortranOptions.encoding = frontendOptions.encoding_;
- collectMacroDefinitions(preprocessorOptions, fortranOptions);
-
// Adding search directories specified by -I
fortranOptions.searchDirectories.insert(
fortranOptions.searchDirectories.end(),
return false;
}
+ auto &invoc = ci.invocation();
+
+ // Include command-line and predefined preprocessor macros. Use either:
+ // * `-cpp/-nocpp`, or
+ // * the file extension (if the user didn't express any preference)
+ // to decide whether to include them or not.
+ if ((invoc.preprocessorOpts().macrosFlag_ == PPMacrosFlag::Include) ||
+ (invoc.preprocessorOpts().macrosFlag_ == PPMacrosFlag::Unknown &&
+ currentInput().MustBePreprocessed())) {
+ invoc.setDefaultPredefinitions();
+ invoc.collectMacroDefinitions();
+ }
+
+ // Decide between fixed and free form (if the user didn't express any
+ // preference, use the file extension to decide)
+ if (invoc.frontendOpts().fortranForm_ == FortranForm::Unknown) {
+ invoc.fortranOpts().isFixedForm = currentInput().IsFixedForm();
+ }
+
if (!BeginSourceFileAction(ci)) {
BeginSourceFileCleanUp(*this, ci);
return false;
#include "flang/Common/default-kinds.h"
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendOptions.h"
+#include "flang/Frontend/PreprocessorOptions.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Parser/dump-parse-tree.h"
#include "flang/Parser/parsing.h"
bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
CompilerInstance &ci = this->instance();
-
std::string currentInputPath{GetCurrentFileOrBufferName()};
-
Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
- if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
- // Switch between fixed and free form format based on the input file
- // extension.
- //
- // Ideally we should have all Fortran options set before entering this
- // method (i.e. before processing any specific input files). However, we
- // can't decide between fixed and free form based on the file extension
- // earlier than this.
- parserOptions.isFixedForm = currentInput().IsFixedForm();
- }
// Prescan. In case of failure, report and return.
ci.parsing().Prescan(currentInputPath, parserOptions);
bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) {
CompilerInstance &ci = this->instance();
-
std::string currentInputPath{GetCurrentFileOrBufferName()};
-
Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
- if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
- // Switch between fixed and free form format based on the input file
- // extension.
- //
- // Ideally we should have all Fortran options set before entering this
- // method (i.e. before processing any specific input files). However, we
- // can't decide between fixed and free form based on the file extension
- // earlier than this.
- parserOptions.isFixedForm = currentInput().IsFixedForm();
- }
-
// Prescan. In case of failure, report and return.
ci.parsing().Prescan(currentInputPath, parserOptions);
suffix == "F08" || suffix == "f18" || suffix == "F18";
}
+bool Fortran::frontend::mustBePreprocessed(llvm::StringRef suffix) {
+ return suffix == "F" || suffix == "FOR" || suffix == "fpp" ||
+ suffix == "FPP" || suffix == "F90" || suffix == "F95" ||
+ suffix == "F03" || suffix == "F08" || suffix == "F18";
+}
+
// TODO: This is a copy of `asFortran` from f18.cpp and is added here for
// compatiblity. It doesn't really belong here, but I couldn't find a better
// place. We should decide whether to add it to the Evaluate or Parse/Unparse
--- /dev/null
+!-----------
+! RUN lines
+!-----------
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
+! RUN: %flang_fc1 -E -cpp -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED
+! RUN: %flang_fc1 -E -nocpp -DX=A %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
+
+!-----------------
+! EXPECTED OUTPUT
+!-----------------
+! UNDEFINED:program b
+! UNDEFINED-NOT:program a
+
+! DEFINED:program a
+! DEFINED-NOT:program b
+
+#ifdef X
+program X
+#else
+program B
+#endif
+end
--- /dev/null
+!-----------
+! RUN lines
+!-----------
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=DEFINED
+! RUN: %flang_fc1 -E -cpp %s 2>&1 | FileCheck %s --check-prefix=DEFINED
+! RUN: %flang_fc1 -E -nocpp %s 2>&1 | FileCheck %s --check-prefix=NOT_DEFINED
+
+!-----------------
+! EXPECTED OUTPUT
+!-----------------
+! DEFINED: flang = 1
+! DEFINED-NEXT: flang_major = {{[1-9][0-9]*$}}
+
+! NOT_DEFINED: flang = __flang__
+! NOT_DEFINED-NEXT: flang_major = __flang_major__
+
+integer, parameter :: flang = __flang__
+integer, parameter :: flang_major = __flang_major__
! CHECK-EMPTY:
! CHECK-NEXT:OPTIONS:
! CHECK-NEXT: -### Print (but do not run) the commands to run for this compilation
+! CHECK-NEXT: -cpp Enable predefined and command line preprocessor macros
! CHECK-NEXT: -c Only run preprocess, compile, and assemble steps
! CHECK-NEXT: -D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
! CHECK-NEXT: -E Only run the preprocessor
! CHECK-NEXT: -help Display available options
! CHECK-NEXT: -I <dir> Add directory to the end of the list of include search paths
! CHECK-NEXT: -module-dir <dir> Put MODULE files in <dir>
+! CHECK-NEXT: -nocpp Disable predefined and command line preprocessor macros
! CHECK-NEXT: -o <file> Write output to <file>
! CHECK-NEXT: -pedantic Warn on language extensions
! CHECK-NEXT: -std=<value> Language standard to compile for
! HELP-EMPTY:
! HELP-NEXT:OPTIONS:
! HELP-NEXT: -### Print (but do not run) the commands to run for this compilation
+! HELP-NEXT: -cpp Enable predefined and command line preprocessor macros
! HELP-NEXT: -c Only run preprocess, compile, and assemble steps
! HELP-NEXT: -D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
! HELP-NEXT: -E Only run the preprocessor
! HELP-NEXT: -help Display available options
! HELP-NEXT: -I <dir> Add directory to the end of the list of include search paths
! HELP-NEXT: -module-dir <dir> Put MODULE files in <dir>
+! HELP-NEXT: -nocpp Disable predefined and command line preprocessor macros
! HELP-NEXT: -o <file> Write output to <file>
! HELP-NEXT: -pedantic Warn on language extensions
! HELP-NEXT: -std=<value> Language standard to compile for
! HELP-FC1:USAGE: flang-new
! HELP-FC1-EMPTY:
! HELP-FC1-NEXT:OPTIONS:
+! HELP-FC1-NEXT: -cpp Enable predefined and command line preprocessor macros
! HELP-FC1-NEXT: -D <macro>=<value> Define <macro> to <value> (or 1 if <value> omitted)
! HELP-FC1-NEXT: -emit-obj Emit native object files
! HELP-FC1-NEXT: -E Only run the preprocessor
! HELP-FC1-NEXT: -help Display available options
! HELP-FC1-NEXT: -I <dir> Add directory to the end of the list of include search paths
! HELP-FC1-NEXT: -module-dir <dir> Put MODULE files in <dir>
+! HELP-FC1-NEXT: -nocpp Disable predefined and command line preprocessor macros
! HELP-FC1-NEXT: -o <file> Write output to <file>
! HELP-FC1-NEXT: -pedantic Warn on language extensions
! HELP-FC1-NEXT: -std=<value> Language standard to compile for
! FLANG DRIVER (flang)
!--------------------------
! Input type is implicit
-! RUN: cat %s | %flang -E - | FileCheck %s --check-prefix=PP-NOT-DEFINED
-! RUN: cat %s | %flang -DNEW -E - | FileCheck %s --check-prefix=PP-DEFINED
+! RUN: cat %s | %flang -E -cpp - | FileCheck %s --check-prefix=PP-NOT-DEFINED
+! RUN: cat %s | %flang -DNEW -E -cpp - | FileCheck %s --check-prefix=PP-DEFINED
+! RUN: cat %s | %flang -DNEW -E - | FileCheck %s --check-prefix=PP-NOT-DEFINED
+! RUN: cat %s | %flang -DNEW -E -nocpp - | FileCheck %s --check-prefix=PP-NOT-DEFINED
! Input type is explicit
-! RUN: cat %s | %flang -E -x f95-cpp-input - | FileCheck %s --check-prefix=PP-NOT-DEFINED
-! RUN: cat %s | %flang -DNEW -E -x f95-cpp-input - | FileCheck %s --check-prefix=PP-DEFINED
+! RUN: cat %s | %flang -E -cpp -x f95-cpp-input - | FileCheck %s --check-prefix=PP-NOT-DEFINED
+! RUN: cat %s | %flang -DNEW -E -cpp -x f95-cpp-input - | FileCheck %s --check-prefix=PP-DEFINED
!---------------------------------------
! FLANG FRONTEND DRIVER (flang -fc1)
!---------------------------------------
! Test `-E`: for the corresponding frontend actions the driver relies on the prescanner API to handle file I/O
-! RUN: cat %s | %flang -fc1 -E | FileCheck %s --check-prefix=PP-NOT-DEFINED
-! RUN: cat %s | %flang -fc1 -DNEW -E | FileCheck %s --check-prefix=PP-DEFINED
+! RUN: cat %s | %flang -fc1 -E -cpp | FileCheck %s --check-prefix=PP-NOT-DEFINED
+! RUN: cat %s | %flang -fc1 -DNEW -E -cpp | FileCheck %s --check-prefix=PP-DEFINED
! Test `-test-io`: for the corresponding frontend action (`InputOutputTestAction`) the driver handles the file I/O on its own
! the corresponding action (`PrintPreprocessedAction`)
-! RUN: cat %s | %flang -fc1 -test-io | FileCheck %s --check-prefix=IO --match-full-lines
-! RUN: cat %s | %flang -fc1 -DNEW -test-io | FileCheck %s --check-prefix=IO --match-full-lines
+! RUN: cat %s | %flang -fc1 -test-io -cpp | FileCheck %s --check-prefix=IO --match-full-lines
+! RUN: cat %s | %flang -fc1 -DNEW -cpp -test-io | FileCheck %s --check-prefix=IO --match-full-lines
!-------------------------
! EXPECTED OUTPUT for `-E`
! Ensure arguments -D and -U work as expected.
-! REQUIRES: new-flang-driver
-
!--------------------------
! FLANG DRIVER (flang-new)
!--------------------------
-! RUN: %flang-new -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
-! RUN: %flang-new -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED
-! RUN: %flang-new -E -DX=A -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
+! RUN: %flang -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
+! RUN: %flang -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED
+! RUN: %flang -E -DX=A -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
!-----------------------------------------
! FRONTEND FLANG DRIVER (flang-new -fc1)
!-----------------------------------------
-! RUN: %flang-new -fc1 -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
-! RUN: %flang-new -fc1 -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED
-! RUN: %flang-new -fc1 -E -DX -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
+! RUN: %flang_fc1 -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED
+! RUN: %flang_fc1 -E -DX -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED
!--------------------------------------------
! EXPECTED OUTPUT FOR AN UNDEFINED MACRO
#else
program B
#endif
-end
\ No newline at end of file
+end
! Check that the driver correctly defines macros with the compiler version
-! REQUIRES: new-flang-driver
-
!--------------------------
! FLANG DRIVER (flang-new)
!--------------------------
-! RUN: %flang-new -E %s 2>&1 | FileCheck %s --ignore-case
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --ignore-case
!-----------------------------------------
! FRONTEND FLANG DRIVER (flang-new -fc1)
!-----------------------------------------
-! RUN: %flang-new -fc1 -E %s 2>&1 | FileCheck %s --ignore-case
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --ignore-case
!-----------------
! EXPECTED OUTPUT