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