1 // Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Temporary Fortran front end driver main program for development scaffolding.
16 
17 #include "../../lib/common/default-kinds.h"
18 #include "../../lib/evaluate/expression.h"
19 #include "../../lib/parser/characters.h"
20 #include "../../lib/parser/dump-parse-tree.h"
21 #include "../../lib/parser/features.h"
22 #include "../../lib/parser/message.h"
23 #include "../../lib/parser/parse-tree-visitor.h"
24 #include "../../lib/parser/parse-tree.h"
25 #include "../../lib/parser/parsing.h"
26 #include "../../lib/parser/provenance.h"
27 #include "../../lib/parser/unparse.h"
28 #include "../../lib/semantics/expression.h"
29 #include "../../lib/semantics/semantics.h"
30 #include "../../lib/semantics/unparse-with-symbols.h"
31 #include <cerrno>
32 #include <cstdio>
33 #include <cstring>
34 #include <fstream>
35 #include <iostream>
36 #include <list>
37 #include <memory>
38 #include <optional>
39 #include <stdlib.h>
40 #include <string>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 #include <vector>
44 
argList(int argc,char * const argv[])45 static std::list<std::string> argList(int argc, char *const argv[]) {
46   std::list<std::string> result;
47   for (int j = 0; j < argc; ++j) {
48     result.emplace_back(argv[j]);
49   }
50   return result;
51 }
52 
53 struct MeasurementVisitor {
PreMeasurementVisitor54   template<typename A> bool Pre(const A &) { return true; }
PostMeasurementVisitor55   template<typename A> void Post(const A &) {
56     ++objects;
57     bytes += sizeof(A);
58   }
59   size_t objects{0}, bytes{0};
60 };
61 
MeasureParseTree(const Fortran::parser::Program & program)62 void MeasureParseTree(const Fortran::parser::Program &program) {
63   MeasurementVisitor visitor;
64   Fortran::parser::Walk(program, visitor);
65   std::cout << "Parse tree comprises " << visitor.objects
66             << " objects and occupies " << visitor.bytes << " total bytes.\n";
67 }
68 
69 std::vector<std::string> filesToDelete;
70 
CleanUpAtExit()71 void CleanUpAtExit() {
72   for (const auto &path : filesToDelete) {
73     if (!path.empty()) {
74       unlink(path.data());
75     }
76   }
77 }
78 
79 struct GetDefinitionArgs {
80   int line, startColumn, endColumn;
81 };
82 
83 struct DriverOptions {
DriverOptionsDriverOptions84   DriverOptions() {}
85   bool verbose{false};  // -v
86   bool compileOnly{false};  // -c
87   std::string outputPath;  // -o path
88   std::vector<std::string> searchDirectories{"."s};  // -I dir
89   std::string moduleDirectory{"."s};  // -module dir
90   std::string moduleFileSuffix{".mod"};  // -moduleSuffix suff
91   bool forcedForm{false};  // -Mfixed or -Mfree appeared
92   bool warnOnNonstandardUsage{false};  // -Mstandard
93   bool warningsAreErrors{false};  // -Werror
94   Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8};
95   bool parseOnly{false};
96   bool dumpProvenance{false};
97   bool dumpCookedChars{false};
98   bool dumpUnparse{false};
99   bool dumpUnparseWithSymbols{false};
100   bool dumpParseTree{false};
101   bool dumpSymbols{false};
102   bool debugResolveNames{false};
103   bool debugSemantics{false};
104   bool measureTree{false};
105   bool unparseTypedExprsToPGF90{false};
106   std::vector<std::string> pgf90Args;
107   const char *prefix{nullptr};
108   bool getDefinition{false};
109   GetDefinitionArgs getDefinitionArgs{0, 0, 0};
110   bool getSymbolsSources{false};
111 };
112 
ParentProcess()113 bool ParentProcess() {
114   if (fork() == 0) {
115     return false;  // in child process
116   }
117   int childStat{0};
118   wait(&childStat);
119   if (!WIFEXITED(childStat) || WEXITSTATUS(childStat) != 0) {
120     exit(EXIT_FAILURE);
121   }
122   return true;
123 }
124 
Exec(std::vector<char * > & argv,bool verbose=false)125 void Exec(std::vector<char *> &argv, bool verbose = false) {
126   if (verbose) {
127     for (size_t j{0}; j < argv.size(); ++j) {
128       std::cerr << (j > 0 ? " " : "") << argv[j];
129     }
130     std::cerr << '\n';
131   }
132   argv.push_back(nullptr);
133   execvp(argv[0], &argv[0]);
134   std::cerr << "execvp(" << argv[0] << ") failed: " << std::strerror(errno)
135             << '\n';
136   exit(EXIT_FAILURE);
137 }
138 
RunOtherCompiler(DriverOptions & driver,char * source,char * relo)139 void RunOtherCompiler(DriverOptions &driver, char *source, char *relo) {
140   std::vector<char *> argv;
141   for (size_t j{0}; j < driver.pgf90Args.size(); ++j) {
142     argv.push_back(driver.pgf90Args[j].data());
143   }
144   char dashC[3] = "-c", dashO[3] = "-o";
145   argv.push_back(dashC);
146   argv.push_back(dashO);
147   argv.push_back(relo);
148   argv.push_back(source);
149   Exec(argv, driver.verbose);
150 }
151 
RelocatableName(const DriverOptions & driver,std::string path)152 std::string RelocatableName(const DriverOptions &driver, std::string path) {
153   if (driver.compileOnly && !driver.outputPath.empty()) {
154     return driver.outputPath;
155   }
156   std::string base{path};
157   auto slash{base.rfind("/")};
158   if (slash != std::string::npos) {
159     base = base.substr(slash + 1);
160   }
161   std::string relo{base};
162   auto dot{base.rfind(".")};
163   if (dot != std::string::npos) {
164     relo = base.substr(0, dot);
165   }
166   relo += ".o";
167   return relo;
168 }
169 
170 int exitStatus{EXIT_SUCCESS};
171 
CompileFortran(std::string path,Fortran::parser::Options options,DriverOptions & driver,const Fortran::common::IntrinsicTypeDefaultKinds & defaultKinds)172 std::string CompileFortran(std::string path, Fortran::parser::Options options,
173     DriverOptions &driver,
174     const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds) {
175   Fortran::parser::AllSources allSources;
176   allSources.set_encoding(driver.encoding);
177   Fortran::semantics::SemanticsContext semanticsContext{
178       defaultKinds, options.features, allSources};
179   semanticsContext.set_moduleDirectory(driver.moduleDirectory)
180       .set_moduleFileSuffix(driver.moduleFileSuffix)
181       .set_searchDirectories(driver.searchDirectories)
182       .set_warnOnNonstandardUsage(driver.warnOnNonstandardUsage)
183       .set_warningsAreErrors(driver.warningsAreErrors);
184   if (!driver.forcedForm) {
185     auto dot{path.rfind(".")};
186     if (dot != std::string::npos) {
187       std::string suffix{path.substr(dot + 1)};
188       options.isFixedForm = suffix == "f" || suffix == "F" || suffix == "ff";
189     }
190   }
191   options.searchDirectories = driver.searchDirectories;
192   Fortran::parser::Parsing parsing{semanticsContext.allSources()};
193   parsing.Prescan(path, options);
194   if (!parsing.messages().empty() &&
195       (driver.warningsAreErrors || parsing.messages().AnyFatalError())) {
196     std::cerr << driver.prefix << "could not scan " << path << '\n';
197     parsing.messages().Emit(std::cerr, parsing.cooked());
198     exitStatus = EXIT_FAILURE;
199     return {};
200   }
201   if (driver.dumpProvenance) {
202     parsing.DumpProvenance(std::cout);
203     return {};
204   }
205   if (driver.dumpCookedChars) {
206     parsing.messages().Emit(std::cerr, parsing.cooked());
207     parsing.DumpCookedChars(std::cout);
208     return {};
209   }
210   parsing.Parse(&std::cout);
211   if (options.instrumentedParse) {
212     parsing.DumpParsingLog(std::cout);
213     return {};
214   }
215   parsing.ClearLog();
216   parsing.messages().Emit(std::cerr, parsing.cooked());
217   if (!parsing.consumedWholeFile()) {
218     parsing.EmitMessage(
219         std::cerr, parsing.finalRestingPlace(), "parser FAIL (final position)");
220     exitStatus = EXIT_FAILURE;
221     return {};
222   }
223   if ((!parsing.messages().empty() &&
224           (driver.warningsAreErrors || parsing.messages().AnyFatalError())) ||
225       !parsing.parseTree().has_value()) {
226     std::cerr << driver.prefix << "could not parse " << path << '\n';
227     exitStatus = EXIT_FAILURE;
228     return {};
229   }
230   auto &parseTree{*parsing.parseTree()};
231   if (driver.measureTree) {
232     MeasureParseTree(parseTree);
233   }
234   // TODO: Change this predicate to just "if (!driver.debugNoSemantics)"
235   if (driver.debugSemantics || driver.debugResolveNames || driver.dumpSymbols ||
236       driver.dumpUnparseWithSymbols || driver.getDefinition ||
237       driver.getSymbolsSources) {
238     Fortran::semantics::Semantics semantics{
239         semanticsContext, parseTree, parsing.cooked()};
240     semantics.Perform();
241     semantics.EmitMessages(std::cerr);
242     if (driver.dumpSymbols) {
243       semantics.DumpSymbols(std::cout);
244     }
245     if (semantics.AnyFatalError()) {
246       std::cerr << driver.prefix << "semantic errors in " << path << '\n';
247       exitStatus = EXIT_FAILURE;
248       if (driver.dumpParseTree) {
249         Fortran::parser::DumpTree(std::cout, parseTree);
250       }
251       return {};
252     }
253     if (driver.dumpUnparseWithSymbols) {
254       Fortran::semantics::UnparseWithSymbols(
255           std::cout, parseTree, driver.encoding);
256       return {};
257     }
258     if (driver.getSymbolsSources) {
259       semantics.DumpSymbolsSources(std::cout);
260       return {};
261     }
262     if (driver.getDefinition) {
263       if (auto cb{parsing.cooked().GetCharBlockFromLineAndColumns(
264               driver.getDefinitionArgs.line,
265               driver.getDefinitionArgs.startColumn,
266               driver.getDefinitionArgs.endColumn)}) {
267         std::cerr << "String range: >" << cb->ToString() << "<\n";
268         if (auto symbol{semanticsContext.FindScope(*cb).FindSymbol(*cb)}) {
269           std::cerr << "Found symbol name: " << symbol->name().ToString()
270                     << "\n";
271           if (auto sourceInfo{
272                   parsing.cooked().GetSourcePositionRange(symbol->name())}) {
273             std::cout << symbol->name().ToString() << ": "
274                       << sourceInfo->first.file.path() << ", "
275                       << sourceInfo->first.line << ", "
276                       << sourceInfo->first.column << "-"
277                       << sourceInfo->second.column << "\n";
278             exitStatus = EXIT_SUCCESS;
279             return {};
280           }
281         }
282       }
283       std::cerr << "Symbol not found.\n";
284       exitStatus = EXIT_FAILURE;
285       return {};
286     }
287   }
288   if (driver.dumpParseTree) {
289     Fortran::parser::DumpTree(std::cout, parseTree);
290   }
291 
292   Fortran::parser::TypedExprAsFortran unparseExpression{
293       [](std::ostream &o, const Fortran::evaluate::GenericExprWrapper &x) {
294         if (x.v.has_value()) {
295           o << *x.v;
296         } else {
297           o << "(bad expression)";
298         }
299       }};
300 
301   if (driver.dumpUnparse) {
302     Unparse(std::cout, parseTree, driver.encoding, true /*capitalize*/,
303         options.features.IsEnabled(
304             Fortran::parser::LanguageFeature::BackslashEscapes),
305         nullptr /* action before each statement */, &unparseExpression);
306     return {};
307   }
308   if (driver.parseOnly) {
309     return {};
310   }
311 
312   std::string relo{RelocatableName(driver, path)};
313 
314   char tmpSourcePath[32];
315   std::snprintf(tmpSourcePath, sizeof tmpSourcePath, "/tmp/f18-%lx.f90",
316       static_cast<unsigned long>(getpid()));
317   {
318     std::ofstream tmpSource;
319     tmpSource.open(tmpSourcePath);
320     Fortran::evaluate::formatForPGF90 = true;
321     Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/,
322         options.features.IsEnabled(
323             Fortran::parser::LanguageFeature::BackslashEscapes),
324         nullptr /* action before each statement */,
325         driver.unparseTypedExprsToPGF90 ? &unparseExpression : nullptr);
326     Fortran::evaluate::formatForPGF90 = false;
327   }
328 
329   if (ParentProcess()) {
330     filesToDelete.push_back(tmpSourcePath);
331     if (!driver.compileOnly && driver.outputPath.empty()) {
332       filesToDelete.push_back(relo);
333     }
334     return relo;
335   }
336   RunOtherCompiler(driver, tmpSourcePath, relo.data());
337   return {};
338 }
339 
CompileOtherLanguage(std::string path,DriverOptions & driver)340 std::string CompileOtherLanguage(std::string path, DriverOptions &driver) {
341   std::string relo{RelocatableName(driver, path)};
342   if (ParentProcess()) {
343     if (!driver.compileOnly && driver.outputPath.empty()) {
344       filesToDelete.push_back(relo);
345     }
346     return relo;
347   }
348   RunOtherCompiler(driver, path.data(), relo.data());
349   return {};
350 }
351 
Link(std::vector<std::string> & relocatables,DriverOptions & driver)352 void Link(std::vector<std::string> &relocatables, DriverOptions &driver) {
353   if (!ParentProcess()) {
354     std::vector<char *> argv;
355     for (size_t j{0}; j < driver.pgf90Args.size(); ++j) {
356       argv.push_back(driver.pgf90Args[j].data());
357     }
358     for (auto &relo : relocatables) {
359       argv.push_back(relo.data());
360     }
361     if (!driver.outputPath.empty()) {
362       char dashO[3] = "-o";
363       argv.push_back(dashO);
364       argv.push_back(driver.outputPath.data());
365     }
366     Exec(argv, driver.verbose);
367   }
368 }
369 
main(int argc,char * const argv[])370 int main(int argc, char *const argv[]) {
371 
372   atexit(CleanUpAtExit);
373 
374   DriverOptions driver;
375   const char *pgf90{getenv("F18_FC")};
376   driver.pgf90Args.push_back(pgf90 ? pgf90 : "pgf90");
377   bool isPGF90{driver.pgf90Args.back().rfind("pgf90") != std::string::npos};
378 
379   std::list<std::string> args{argList(argc, argv)};
380   std::string prefix{args.front()};
381   args.pop_front();
382   prefix += ": ";
383   driver.prefix = prefix.data();
384 
385   Fortran::parser::Options options;
386   options.predefinitions.emplace_back("__F18", "1");
387   options.predefinitions.emplace_back("__F18_MAJOR__", "1");
388   options.predefinitions.emplace_back("__F18_MINOR__", "1");
389   options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1");
390 #if __x86_64__
391   options.predefinitions.emplace_back("__x86_64__", "1");
392 #endif
393 
394   Fortran::common::IntrinsicTypeDefaultKinds defaultKinds;
395 
396   std::vector<std::string> fortranSources, otherSources, relocatables;
397   bool anyFiles{false};
398 
399   while (!args.empty()) {
400     std::string arg{std::move(args.front())};
401     args.pop_front();
402     if (arg.empty()) {
403     } else if (arg.at(0) != '-') {
404       anyFiles = true;
405       auto dot{arg.rfind(".")};
406       if (dot == std::string::npos) {
407         driver.pgf90Args.push_back(arg);
408       } else {
409         std::string suffix{arg.substr(dot + 1)};
410         if (suffix == "f" || suffix == "F" || suffix == "ff" ||
411             suffix == "f90" || suffix == "F90" || suffix == "ff90" ||
412             suffix == "f95" || suffix == "F95" || suffix == "ff95" ||
413             suffix == "cuf" || suffix == "CUF" || suffix == "f18" ||
414             suffix == "F18" || suffix == "ff18") {
415           fortranSources.push_back(arg);
416         } else if (suffix == "o" || suffix == "a") {
417           relocatables.push_back(arg);
418         } else {
419           otherSources.push_back(arg);
420         }
421       }
422     } else if (arg == "-") {
423       fortranSources.push_back("-");
424     } else if (arg == "--") {
425       while (!args.empty()) {
426         fortranSources.emplace_back(std::move(args.front()));
427         args.pop_front();
428       }
429       break;
430     } else if (arg == "-Mfixed") {
431       driver.forcedForm = true;
432       options.isFixedForm = true;
433     } else if (arg == "-Mfree") {
434       driver.forcedForm = true;
435       options.isFixedForm = false;
436     } else if (arg == "-Mextend") {
437       options.fixedFormColumns = 132;
438     } else if (arg == "-Mbackslash") {
439       options.features.Enable(
440           Fortran::parser::LanguageFeature::BackslashEscapes, false);
441     } else if (arg == "-Mnobackslash") {
442       options.features.Enable(
443           Fortran::parser::LanguageFeature::BackslashEscapes, true);
444     } else if (arg == "-Mstandard") {
445       driver.warnOnNonstandardUsage = true;
446     } else if (arg == "-fopenmp") {
447       options.features.Enable(Fortran::parser::LanguageFeature::OpenMP);
448       options.predefinitions.emplace_back("_OPENMP", "201511");
449     } else if (arg == "-Werror") {
450       driver.warningsAreErrors = true;
451     } else if (arg == "-ed") {
452       options.features.Enable(Fortran::parser::LanguageFeature::OldDebugLines);
453     } else if (arg == "-E") {
454       driver.dumpCookedChars = true;
455     } else if (arg == "-fbackslash" || arg == "-fno-backslash") {
456       options.features.Enable(
457           Fortran::parser::LanguageFeature::BackslashEscapes,
458           arg == "-fbackslash");
459     } else if (arg == "-fxor-operator" || arg == "-fno-xor-operator") {
460       options.features.Enable(Fortran::parser::LanguageFeature::XOROperator,
461           arg == "-fxor-operator");
462     } else if (arg == "-fdebug-dump-provenance") {
463       driver.dumpProvenance = true;
464       options.needProvenanceRangeToCharBlockMappings = true;
465     } else if (arg == "-fdebug-dump-parse-tree") {
466       driver.dumpParseTree = true;
467     } else if (arg == "-fdebug-dump-symbols") {
468       driver.dumpSymbols = true;
469     } else if (arg == "-fdebug-resolve-names") {
470       driver.debugResolveNames = true;
471     } else if (arg == "-fdebug-measure-parse-tree") {
472       driver.measureTree = true;
473     } else if (arg == "-fdebug-instrumented-parse") {
474       options.instrumentedParse = true;
475     } else if (arg == "-fdebug-semantics") {
476       // TODO: Enable by default once basic tests pass
477       driver.debugSemantics = true;
478     } else if (arg == "-funparse") {
479       driver.dumpUnparse = true;
480     } else if (arg == "-funparse-with-symbols") {
481       driver.dumpUnparseWithSymbols = true;
482     } else if (arg == "-funparse-typed-exprs-to-pgf90") {
483       driver.unparseTypedExprsToPGF90 = true;
484     } else if (arg == "-fparse-only") {
485       driver.parseOnly = true;
486     } else if (arg == "-c") {
487       driver.compileOnly = true;
488     } else if (arg == "-o") {
489       driver.outputPath = args.front();
490       args.pop_front();
491     } else if (arg.substr(0, 2) == "-D") {
492       auto eq{arg.find('=')};
493       if (eq == std::string::npos) {
494         options.predefinitions.emplace_back(arg.substr(2), "1");
495       } else {
496         options.predefinitions.emplace_back(
497             arg.substr(2, eq - 2), arg.substr(eq + 1));
498       }
499     } else if (arg.substr(0, 2) == "-U") {
500       options.predefinitions.emplace_back(
501           arg.substr(2), std::optional<std::string>{});
502     } else if (arg == "-r8" || arg == "-fdefault-real-8") {
503       defaultKinds.set_defaultRealKind(8);
504     } else if (arg == "-i8" || arg == "-fdefault-integer-8") {
505       defaultKinds.set_defaultIntegerKind(8);
506     } else if (arg == "-module") {
507       driver.moduleDirectory = args.front();
508       args.pop_front();
509     } else if (arg == "-module-suffix") {
510       driver.moduleFileSuffix = args.front();
511       args.pop_front();
512     } else if (arg == "-intrinsic-module-directory") {
513       driver.searchDirectories.push_back(args.front());
514       args.pop_front();
515     } else if (arg == "-futf-8") {
516       driver.encoding = Fortran::parser::Encoding::UTF_8;
517     } else if (arg == "-flatin") {
518       driver.encoding = Fortran::parser::Encoding::LATIN_1;
519     } else if (arg == "-fget-definition") {
520       // Receives 3 arguments: line, startColumn, endColumn.
521       options.needProvenanceRangeToCharBlockMappings = true;
522       driver.getDefinition = true;
523       char *endptr;
524       int arguments[3];
525       for (int i = 0; i < 3; i++) {
526         if (args.empty()) {
527           std::cerr << "Must provide 3 arguments for -fget-definitions.\n";
528           return EXIT_FAILURE;
529         }
530         arguments[i] = std::strtol(args.front().c_str(), &endptr, 10);
531         if (*endptr != '\0') {
532           std::cerr << "Invalid argument to -fget-definitions: " << args.front()
533                     << '\n';
534           return EXIT_FAILURE;
535         }
536         args.pop_front();
537       }
538       driver.getDefinitionArgs = {arguments[0], arguments[1], arguments[2]};
539     } else if (arg == "-fget-symbols-sources") {
540       driver.getSymbolsSources = true;
541     } else if (arg == "-help" || arg == "--help" || arg == "-?") {
542       std::cerr
543           << "f18 options:\n"
544           << "  -Mfixed | -Mfree     force the source form\n"
545           << "  -Mextend             132-column fixed form\n"
546           << "  -f[no-]backslash     enable[disable] \\escapes in literals\n"
547           << "  -M[no]backslash      disable[enable] \\escapes in literals\n"
548           << "  -Mstandard           enable conformance warnings\n"
549           << "  -r8 | -fdefault-real-8 | -i8 | -fdefault-integer-8  "
550              "change default kinds of intrinsic types\n"
551           << "  -Werror              treat warnings as errors\n"
552           << "  -ed                  enable fixed form D lines\n"
553           << "  -E                   prescan & preprocess only\n"
554           << "  -module dir          module output directory (default .)\n"
555           << "  -flatin              interpret source as Latin-1 (ISO 8859-1) "
556              "rather than UTF-8\n"
557           << "  -fparse-only         parse only, no output except messages\n"
558           << "  -funparse            parse & reformat only, no code "
559              "generation\n"
560           << "  -funparse-with-symbols  parse, resolve symbols, and unparse\n"
561           << "  -fdebug-measure-parse-tree\n"
562           << "  -fdebug-dump-provenance\n"
563           << "  -fdebug-dump-parse-tree\n"
564           << "  -fdebug-dump-symbols\n"
565           << "  -fdebug-resolve-names\n"
566           << "  -fdebug-instrumented-parse\n"
567           << "  -fdebug-semantics    perform semantic checks\n"
568           << "  -fget-definition\n"
569           << "  -fget-symbols-sources\n"
570           << "  -v -c -o -I -D -U    have their usual meanings\n"
571           << "  -help                print this again\n"
572           << "Other options are passed through to the compiler.\n";
573       return exitStatus;
574     } else if (arg == "-V") {
575       std::cerr << "\nf18 compiler (under development)\n";
576       return exitStatus;
577     } else {
578       driver.pgf90Args.push_back(arg);
579       if (arg == "-v") {
580         driver.verbose = true;
581       } else if (arg == "-I") {
582         driver.pgf90Args.push_back(args.front());
583         driver.searchDirectories.push_back(args.front());
584         args.pop_front();
585       } else if (arg.substr(0, 2) == "-I") {
586         driver.searchDirectories.push_back(arg.substr(2));
587       }
588     }
589   }
590 
591   if (driver.warnOnNonstandardUsage) {
592     options.features.WarnOnAllNonstandard();
593   }
594   if (options.features.IsEnabled(Fortran::parser::LanguageFeature::OpenMP)) {
595     driver.pgf90Args.push_back("-mp");
596   }
597   if (isPGF90) {
598     if (!options.features.IsEnabled(
599             Fortran::parser::LanguageFeature::BackslashEscapes)) {
600       driver.pgf90Args.push_back(
601           "-Mbackslash");  // yes, this *disables* them in pgf90
602     }
603   } else {
604     // TODO: equivalents for other Fortran compilers
605   }
606 
607   if (!anyFiles) {
608     driver.measureTree = true;
609     driver.dumpUnparse = true;
610     CompileFortran("-", options, driver, defaultKinds);
611     return exitStatus;
612   }
613   for (const auto &path : fortranSources) {
614     std::string relo{CompileFortran(path, options, driver, defaultKinds)};
615     if (!driver.compileOnly && !relo.empty()) {
616       relocatables.push_back(relo);
617     }
618   }
619   for (const auto &path : otherSources) {
620     std::string relo{CompileOtherLanguage(path, driver)};
621     if (!driver.compileOnly && !relo.empty()) {
622       relocatables.push_back(relo);
623     }
624   }
625   if (!relocatables.empty()) {
626     Link(relocatables, driver);
627   }
628   return exitStatus;
629 }
630