souffle  2.0.2-371-g6315b36
main.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file main.cpp
12  *
13  * Main driver for Souffle
14  *
15  ***********************************************************************/
16 
17 #include "Global.h"
18 #include "ast/Node.h"
19 #include "ast/Program.h"
20 #include "ast/TranslationUnit.h"
22 #include "ast/analysis/SCCGraph.h"
23 #include "ast/analysis/Type.h"
29 #include "ast/transform/Fixpoint.h"
36 #include "ast/transform/MagicSet.h"
43 #include "ast/transform/Pipeline.h"
60 #include "config.h"
61 #include "interpreter/Engine.h"
63 #include "parser/ParserDriver.h"
64 #include "ram/Node.h"
65 #include "ram/Program.h"
66 #include "ram/TranslationUnit.h"
76 #include "ram/transform/Loop.h"
78 #include "ram/transform/Parallel.h"
82 #include "ram/transform/Sequence.h"
84 #include "ram/transform/TupleId.h"
85 #include "reports/DebugReport.h"
86 #include "reports/ErrorReport.h"
87 #include "souffle/RamTypes.h"
88 #include "souffle/profile/Tui.h"
96 #include <cassert>
97 #include <chrono>
98 #include <cstdio>
99 #include <cstdlib>
100 #include <ctime>
101 #include <iomanip>
102 #include <iostream>
103 #include <map>
104 #include <memory>
105 #include <set>
106 #include <sstream>
107 #include <stdexcept>
108 #include <string>
109 #include <thread>
110 #include <utility>
111 #include <vector>
112 
113 namespace souffle {
114 
115 /**
116  * Executes a binary file.
117  */
118 void executeBinary(const std::string& binaryFilename) {
119  assert(!binaryFilename.empty() && "binary filename cannot be blank");
120 
121  // check whether the executable exists
122  if (!isExecutable(binaryFilename)) {
123  throw std::invalid_argument("Generated executable <" + binaryFilename + "> could not be found");
124  }
125 
126  std::string ldPath;
127  // run the executable
128  if (Global::config().has("library-dir")) {
129  for (const std::string& library : splitString(Global::config().get("library-dir"), ' ')) {
130  ldPath += library + ':';
131  }
132  ldPath.pop_back();
133  setenv("LD_LIBRARY_PATH", ldPath.c_str(), 1);
134  }
135 
136  std::string exePath;
137 #ifdef __APPLE__
138  // OSX does not pass on the environment from setenv so add it to the command line
139  exePath = "DYLD_LIBRARY_PATH=\"" + ldPath + "\" ";
140 #endif
141  exePath += binaryFilename;
142 
143  int exitCode = system(exePath.c_str());
144 
145  if (Global::config().get("dl-program").empty()) {
146  remove(binaryFilename.c_str());
147  remove((binaryFilename + ".cpp").c_str());
148  }
149 
150  // exit with same code as executable
151  if (exitCode != EXIT_SUCCESS) {
152  exit(exitCode);
153  }
154 }
155 
156 /**
157  * Compiles the given source file to a binary file.
158  */
159 void compileToBinary(std::string compileCmd, const std::string& sourceFilename) {
160  // add source code
161  compileCmd += ' ';
162  for (const std::string& path : splitString(Global::config().get("library-dir"), ' ')) {
163  // The first entry may be blank
164  if (path.empty()) {
165  continue;
166  }
167  compileCmd += "-L" + path + ' ';
168  }
169  for (const std::string& library : splitString(Global::config().get("libraries"), ' ')) {
170  // The first entry may be blank
171  if (library.empty()) {
172  continue;
173  }
174  compileCmd += "-l" + library + ' ';
175  }
176 
177  compileCmd += sourceFilename;
178 
179  // run executable
180  if (system(compileCmd.c_str()) != 0) {
181  throw std::invalid_argument("failed to compile C++ source <" + sourceFilename + ">");
182  }
183 }
184 
185 int main(int argc, char** argv) {
186  /* Time taking for overall runtime */
187  auto souffle_start = std::chrono::high_resolution_clock::now();
188 
189  /* have all to do with command line arguments in its own scope, as these are accessible through the global
190  * configuration only */
191  try {
192  std::stringstream header;
193  header << "============================================================================" << std::endl;
194  header << "souffle -- A datalog engine." << std::endl;
195  header << "Usage: souffle [OPTION] FILE." << std::endl;
196  header << "----------------------------------------------------------------------------" << std::endl;
197  header << "Options:" << std::endl;
198 
199  std::stringstream footer;
200  footer << "----------------------------------------------------------------------------" << std::endl;
201  footer << "Version: " << PACKAGE_VERSION << "" << std::endl;
202  footer << "----------------------------------------------------------------------------" << std::endl;
203  footer << "Copyright (c) 2016-20 The Souffle Developers." << std::endl;
204  footer << "Copyright (c) 2013-16 Oracle and/or its affiliates." << std::endl;
205  footer << "All rights reserved." << std::endl;
206  footer << "============================================================================" << std::endl;
207 
208  // command line options, the environment will be filled with the arguments passed to them, or
209  // the empty string if they take none
210  // main option, the datalog program itself, has an empty key
211  std::vector<MainOption> options{{"", 0, "", "", false, ""},
212  {"fact-dir", 'F', "DIR", ".", false, "Specify directory for fact files."},
213  {"include-dir", 'I', "DIR", ".", true, "Specify directory for include files."},
214  {"output-dir", 'D', "DIR", ".", false,
215  "Specify directory for output files. If <DIR> is `-` then stdout is used."},
216  {"jobs", 'j', "N", "1", false,
217  "Run interpreter/compiler in parallel using N threads, N=auto for system "
218  "default."},
219  {"compile", 'c', "", "", false,
220  "Generate C++ source code, compile to a binary executable, then run this "
221  "executable."},
222  {"generate", 'g', "FILE", "", false,
223  "Generate C++ source code for the given Datalog program and write it to "
224  "<FILE>. If <FILE> is `-` then stdout is used."},
225  {"swig", 's', "LANG", "", false,
226  "Generate SWIG interface for given language. The values <LANG> accepts is java and "
227  "python. "},
228  {"library-dir", 'L', "DIR", "", false, "Specify directory for library files."},
229  {"libraries", 'l', "FILE", "", false, "Specify libraries."},
230  {"no-warn", 'w', "", "", false, "Disable warnings."},
231  {"magic-transform", 'm', "RELATIONS", "", false,
232  "Enable magic set transformation changes on the given relations, use '*' "
233  "for all."},
234  {"macro", 'M', "MACROS", "", false, "Set macro definitions for the pre-processor"},
235  {"disable-transformers", 'z', "TRANSFORMERS", "", false,
236  "Disable the given AST transformers."},
237  {"dl-program", 'o', "FILE", "", false,
238  "Generate C++ source code, written to <FILE>, and compile this to a "
239  "binary executable (without executing it)."},
240  {"live-profile", '\2', "", "", false, "Enable live profiling."},
241  {"profile", 'p', "FILE", "", false, "Enable profiling, and write profile data to <FILE>."},
242  {"profile-use", 'u', "FILE", "", false,
243  "Use profile log-file <FILE> for profile-guided optimization."},
244  {"debug-report", 'r', "FILE", "", false, "Write HTML debug report to <FILE>."},
245  {"pragma", 'P', "OPTIONS", "", false, "Set pragma options."},
246  {"provenance", 't', "[ none | explain | explore ]", "", false,
247  "Enable provenance instrumentation and interaction."},
248  {"verbose", 'v', "", "", false, "Verbose output."},
249  {"version", '\3', "", "", false, "Version."},
250  {"show", '\4',
251  "[ parse-errors | precedence-graph | scc-graph | transformed-datalog | "
252  "transformed-ram | type-analysis ]",
253  "", false, "Print selected program information."},
254  {"parse-errors", '\5', "", "", false, "Show parsing errors, if any, then exit."},
255  {"help", 'h', "", "", false, "Display this help message."},
256  {"legacy", '\6', "", "", false, "Enable legacy support."}};
257  Global::config().processArgs(argc, argv, header.str(), footer.str(), options);
258 
259  // ------ command line arguments -------------
260 
261  // Take in pragma options from the command line
262  if (Global::config().has("pragma")) {
263  std::vector<std::string> configOptions = splitString(Global::config().get("pragma"), ';');
264  for (const std::string& option : configOptions) {
265  size_t splitPoint = option.find(':');
266 
267  std::string optionName = option.substr(0, splitPoint);
268  std::string optionValue = (splitPoint == std::string::npos)
269  ? ""
270  : option.substr(splitPoint + 1, option.length());
271 
272  if (!Global::config().has(optionName)) {
273  Global::config().set(optionName, optionValue);
274  }
275  }
276  }
277 
278  /* for the version option, if given print the version text then exit */
279  if (Global::config().has("version")) {
280  std::cout << "Souffle: " << PACKAGE_VERSION;
281  std::cout << "(" << RAM_DOMAIN_SIZE << "bit Domains)";
282  std::cout << std::endl;
283  std::cout << "Copyright (c) 2016-19 The Souffle Developers." << std::endl;
284  std::cout << "Copyright (c) 2013-16 Oracle and/or its affiliates." << std::endl;
285  return 0;
286  }
287  Global::config().set("version", PACKAGE_VERSION);
288 
289  /* for the help option, if given simply print the help text then exit */
290  if (!Global::config().has("") || Global::config().has("help")) {
291  std::cout << Global::config().help();
292  return 0;
293  }
294 
295  /* check that datalog program exists */
296  if (!existFile(Global::config().get(""))) {
297  throw std::runtime_error("cannot open file " + std::string(Global::config().get("")));
298  }
299 
300  /* for the jobs option, to determine the number of threads used */
301 #ifdef _OPENMP
302  if (isNumber(Global::config().get("jobs").c_str())) {
303  if (std::stoi(Global::config().get("jobs")) < 1) {
304  throw std::runtime_error("-j/--jobs may only be set to 'auto' or an integer greater than 0.");
305  }
306  } else {
307  if (!Global::config().has("jobs", "auto")) {
308  throw std::runtime_error("-j/--jobs may only be set to 'auto' or an integer greater than 0.");
309  }
310  Global::config().set("jobs", "0");
311  }
312 #else
313  // Check that -j option has not been changed from the default
314  if (Global::config().get("jobs") != "1" && !Global::config().has("no-warn")) {
315  std::cerr << "\nThis installation of Souffle does not support concurrent jobs.\n";
316  }
317 #endif
318 
319  /* if an output directory is given, check it exists */
320  if (Global::config().has("output-dir") && !Global::config().has("output-dir", "-") &&
321  !existDir(Global::config().get("output-dir")) &&
322  !(Global::config().has("generate") ||
323  (Global::config().has("dl-program") && !Global::config().has("compile")))) {
324  throw std::runtime_error(
325  "output directory " + Global::config().get("output-dir") + " does not exists");
326  }
327 
328  /* collect all input directories for the c pre-processor */
329  if (Global::config().has("include-dir")) {
330  std::string currentInclude = "";
331  std::string allIncludes = "";
332  for (const char& ch : Global::config().get("include-dir")) {
333  if (ch == ' ') {
334  if (!existDir(currentInclude)) {
335  throw std::runtime_error("include directory " + currentInclude + " does not exists");
336  } else {
337  allIncludes += " -I";
338  allIncludes += currentInclude;
339  currentInclude = "";
340  }
341  } else {
342  currentInclude += ch;
343  }
344  }
345  allIncludes += " -I" + currentInclude;
346  Global::config().set("include-dir", allIncludes);
347  }
348 
349  /* collect all macro definitions for the pre-processor */
350  if (Global::config().has("macro")) {
351  std::string currentMacro = "";
352  std::string allMacros = "";
353  for (const char& ch : Global::config().get("macro")) {
354  if (ch == ' ') {
355  allMacros += " -D";
356  allMacros += currentMacro;
357  currentMacro = "";
358  } else {
359  currentMacro += ch;
360  }
361  }
362  allMacros += " -D" + currentMacro;
363  Global::config().set("macro", allMacros);
364  }
365 
366  /* turn on compilation of executables */
367  if (Global::config().has("dl-program")) {
368  Global::config().set("compile");
369  }
370 
371  if (Global::config().has("live-profile") && !Global::config().has("profile")) {
372  Global::config().set("profile");
373  }
374  } catch (std::exception& e) {
375  std::cerr << e.what() << std::endl;
376  exit(EXIT_FAILURE);
377  }
378 
379  /**
380  * Ensure that code generation is enabled if using SWIG interface option.
381  */
382  if (Global::config().has("swig") && !Global::config().has("generate")) {
383  Global::config().set("generate", simpleName(Global::config().get("")));
384  }
385 
386  // ------ start souffle -------------
387 
388  std::string souffleExecutable = which(argv[0]);
389 
390  if (souffleExecutable.empty()) {
391  throw std::runtime_error("failed to determine souffle executable path");
392  }
393 
394  /* Create the pipe to establish a communication between cpp and souffle */
395  std::string cmd = ::which("mcpp");
396 
397  if (!isExecutable(cmd)) {
398  throw std::runtime_error("failed to locate mcpp pre-processor");
399  }
400 
401  cmd += " -e utf8 -W0 " + Global::config().get("include-dir");
402  if (Global::config().has("macro")) {
403  cmd += " " + Global::config().get("macro");
404  }
405  // Add RamDomain size as a macro
406  cmd += " -DRAM_DOMAIN_SIZE=" + std::to_string(RAM_DOMAIN_SIZE);
407  cmd += " " + Global::config().get("");
408  FILE* in = popen(cmd.c_str(), "r");
409 
410  /* Time taking for parsing */
411  auto parser_start = std::chrono::high_resolution_clock::now();
412 
413  // ------- parse program -------------
414 
415  // parse file
416  ErrorReport errReport(Global::config().has("no-warn"));
417  DebugReport debugReport;
418  Own<ast::TranslationUnit> astTranslationUnit =
419  ParserDriver::parseTranslationUnit("<stdin>", in, errReport, debugReport);
420 
421  // close input pipe
422  int preprocessor_status = pclose(in);
423  if (preprocessor_status == -1) {
424  perror(nullptr);
425  throw std::runtime_error("failed to close pre-processor pipe");
426  }
427 
428  /* Report run-time of the parser if verbose flag is set */
429  if (Global::config().has("verbose")) {
430  auto parser_end = std::chrono::high_resolution_clock::now();
431  std::cout << "Parse Time: " << std::chrono::duration<double>(parser_end - parser_start).count()
432  << "sec\n";
433  }
434 
435  if (Global::config().get("show") == "parse-errors") {
436  std::cout << astTranslationUnit->getErrorReport();
437  return astTranslationUnit->getErrorReport().getNumErrors();
438  }
439 
440  // ------- check for parse errors -------------
441  astTranslationUnit->getErrorReport().exitIfErrors();
442 
443  // ------- rewriting / optimizations -------------
444 
445  /* set up additional global options based on pragma declaratives */
446  (mk<ast::transform::PragmaChecker>())->apply(*astTranslationUnit);
447 
448  /* construct the transformation pipeline */
449 
450  // Equivalence pipeline
451  auto equivalencePipeline =
452  mk<ast::transform::PipelineTransformer>(mk<ast::transform::NameUnnamedVariablesTransformer>(),
453  mk<ast::transform::FixpointTransformer>(mk<ast::transform::MinimiseProgramTransformer>()),
454  mk<ast::transform::ReplaceSingletonVariablesTransformer>(),
455  mk<ast::transform::RemoveRelationCopiesTransformer>(),
456  mk<ast::transform::RemoveEmptyRelationsTransformer>(),
457  mk<ast::transform::RemoveRedundantRelationsTransformer>());
458 
459  // Magic-Set pipeline
460  auto magicPipeline = mk<ast::transform::PipelineTransformer>(mk<ast::transform::MagicSetTransformer>(),
461  mk<ast::transform::ResolveAliasesTransformer>(),
462  mk<ast::transform::RemoveRelationCopiesTransformer>(),
463  mk<ast::transform::RemoveEmptyRelationsTransformer>(),
464  mk<ast::transform::RemoveRedundantRelationsTransformer>(), souffle::clone(equivalencePipeline));
465 
466  // Partitioning pipeline
467  auto partitionPipeline =
468  mk<ast::transform::PipelineTransformer>(mk<ast::transform::NameUnnamedVariablesTransformer>(),
469  mk<ast::transform::PartitionBodyLiteralsTransformer>(),
470  mk<ast::transform::ReplaceSingletonVariablesTransformer>());
471 
472  // Provenance pipeline
473  auto provenancePipeline = mk<ast::transform::ConditionalTransformer>(
474  Global::config().has("provenance"), mk<ast::transform::ProvenanceTransformer>());
475 
476  // Main pipeline
477  auto pipeline = mk<ast::transform::PipelineTransformer>(mk<ast::transform::ComponentChecker>(),
478  mk<ast::transform::ComponentInstantiationTransformer>(),
479  mk<ast::transform::IODefaultsTransformer>(),
480  mk<ast::transform::SimplifyAggregateTargetExpressionTransformer>(),
481  mk<ast::transform::UniqueAggregationVariablesTransformer>(),
482  mk<ast::transform::FixpointTransformer>(mk<ast::transform::PipelineTransformer>(
483  mk<ast::transform::ResolveAnonymousRecordAliasesTransformer>(),
484  mk<ast::transform::FoldAnonymousRecords>())),
485  mk<ast::transform::SemanticChecker>(), mk<ast::transform::GroundWitnessesTransformer>(),
486  mk<ast::transform::UniqueAggregationVariablesTransformer>(),
487  mk<ast::transform::NormaliseMultiResultFunctorsTransformer>(),
488  mk<ast::transform::MaterializeSingletonAggregationTransformer>(),
489  mk<ast::transform::FixpointTransformer>(
490  mk<ast::transform::MaterializeAggregationQueriesTransformer>()),
491  mk<ast::transform::ResolveAliasesTransformer>(),
492  mk<ast::transform::RemoveBooleanConstraintsTransformer>(),
493  mk<ast::transform::ResolveAliasesTransformer>(), mk<ast::transform::MinimiseProgramTransformer>(),
494  mk<ast::transform::InlineRelationsTransformer>(), mk<ast::transform::GroundedTermsChecker>(),
495  mk<ast::transform::ResolveAliasesTransformer>(),
496  mk<ast::transform::RemoveRedundantRelationsTransformer>(),
497  mk<ast::transform::RemoveRelationCopiesTransformer>(),
498  mk<ast::transform::RemoveEmptyRelationsTransformer>(),
499  mk<ast::transform::ReplaceSingletonVariablesTransformer>(),
500  mk<ast::transform::FixpointTransformer>(mk<ast::transform::PipelineTransformer>(
501  mk<ast::transform::ReduceExistentialsTransformer>(),
502  mk<ast::transform::RemoveRedundantRelationsTransformer>())),
503  mk<ast::transform::RemoveRelationCopiesTransformer>(), std::move(partitionPipeline),
504  std::move(equivalencePipeline), mk<ast::transform::RemoveRelationCopiesTransformer>(),
505  std::move(magicPipeline), mk<ast::transform::ReorderLiteralsTransformer>(),
506  mk<ast::transform::RemoveRedundantSumsTransformer>(),
507  mk<ast::transform::RemoveEmptyRelationsTransformer>(),
508  mk<ast::transform::AddNullariesToAtomlessAggregatesTransformer>(),
509  mk<ast::transform::ReorderLiteralsTransformer>(), mk<ast::transform::ExecutionPlanChecker>(),
510  std::move(provenancePipeline), mk<ast::transform::IOAttributesTransformer>());
511 
512  // Disable unwanted transformations
513  if (Global::config().has("disable-transformers")) {
514  std::vector<std::string> givenTransformers =
515  splitString(Global::config().get("disable-transformers"), ',');
516  pipeline->disableTransformers(
517  std::set<std::string>(givenTransformers.begin(), givenTransformers.end()));
518  }
519 
520  // Set up the debug report if necessary
521  if (Global::config().has("debug-report")) {
522  auto parser_end = std::chrono::high_resolution_clock::now();
523  std::stringstream ss;
524 
525  // Add current time
526  std::time_t time = std::time(nullptr);
527  ss << "Executed at ";
528  ss << std::put_time(std::localtime(&time), "%F %T") << "\n";
529 
530  // Add config
531  ss << "(\n";
532  ss << join(Global::config().data(), ",\n", [](std::ostream& out, const auto& arg) {
533  out << " \"" << arg.first << "\" -> \"" << arg.second << '"';
534  });
535  ss << "\n)";
536 
537  debugReport.addSection("Configuration", "Configuration", ss.str());
538 
539  // Add parsing runtime
540  std::string runtimeStr =
541  "(" + std::to_string(std::chrono::duration<double>(parser_end - parser_start).count()) + "s)";
542  debugReport.addSection("Parsing", "Parsing " + runtimeStr, "");
543 
544  pipeline->setDebugReport();
545  }
546 
547  // Toggle pipeline verbosity
548  pipeline->setVerbosity(Global::config().has("verbose"));
549 
550  // Apply all the transformations
551  pipeline->apply(*astTranslationUnit);
552 
553  if (Global::config().has("show")) {
554  // Output the transformed datalog and return
555  if (Global::config().get("show") == "transformed-datalog") {
556  std::cout << astTranslationUnit->getProgram() << std::endl;
557  return 0;
558  }
559 
560  // Output the precedence graph in graphviz dot format and return
561  if (Global::config().get("show") == "precedence-graph") {
562  astTranslationUnit->getAnalysis<ast::analysis::PrecedenceGraphAnalysis>()->print(std::cout);
563  std::cout << std::endl;
564  return 0;
565  }
566 
567  // Output the scc graph in graphviz dot format and return
568  if (Global::config().get("show") == "scc-graph") {
569  astTranslationUnit->getAnalysis<ast::analysis::SCCGraphAnalysis>()->print(std::cout);
570  std::cout << std::endl;
571  return 0;
572  }
573 
574  // Output the type analysis
575  if (Global::config().get("show") == "type-analysis") {
576  astTranslationUnit->getAnalysis<ast::analysis::TypeAnalysis>()->print(std::cout);
577  std::cout << std::endl;
578  return 0;
579  }
580  }
581 
582  // ------- execution -------------
583  /* translate AST to RAM */
584  debugReport.startSection();
585  auto ramTranslationUnit = ast2ram::AstToRamTranslator().translateUnit(*astTranslationUnit);
586  debugReport.endSection("ast-to-ram", "Translate AST to RAM");
587 
588  // Apply RAM transforms
589  {
590  using namespace ram::transform;
591  Own<Transformer> ramTransform = mk<TransformerSequence>(
592  mk<LoopTransformer>(mk<TransformerSequence>(mk<ExpandFilterTransformer>(),
593  mk<HoistConditionsTransformer>(), mk<MakeIndexTransformer>())),
594  mk<LoopTransformer>(mk<IndexedInequalityTransformer>()), mk<IfConversionTransformer>(),
595  mk<ChoiceConversionTransformer>(), mk<CollapseFiltersTransformer>(), mk<TupleIdTransformer>(),
596  mk<LoopTransformer>(
597  mk<TransformerSequence>(mk<HoistAggregateTransformer>(), mk<TupleIdTransformer>())),
598  mk<ExpandFilterTransformer>(), mk<HoistConditionsTransformer>(),
599  mk<CollapseFiltersTransformer>(), mk<EliminateDuplicatesTransformer>(),
600  mk<ReorderConditionsTransformer>(), mk<LoopTransformer>(mk<ReorderFilterBreak>()),
601  mk<ConditionalTransformer>(
602  // job count of 0 means all cores are used.
603  []() -> bool { return std::stoi(Global::config().get("jobs")) != 1; },
604  mk<ParallelTransformer>()),
605  mk<ReportIndexTransformer>());
606 
607  ramTransform->apply(*ramTranslationUnit);
608  }
609 
610  if (ramTranslationUnit->getErrorReport().getNumIssues() != 0) {
611  std::cerr << ramTranslationUnit->getErrorReport();
612  }
613 
614  // Output the transformed RAM program and return
615  if (Global::config().get("show") == "transformed-ram") {
616  std::cout << ramTranslationUnit->getProgram();
617  return 0;
618  }
619 
620  try {
621  if (!Global::config().has("compile") && !Global::config().has("dl-program") &&
622  !Global::config().has("generate") && !Global::config().has("swig")) {
623  // ------- interpreter -------------
624 
625  std::thread profiler;
626  // Start up profiler if needed
627  if (Global::config().has("live-profile") && !Global::config().has("compile")) {
628  profiler = std::thread([]() { profile::Tui().runProf(); });
629  }
630 
631  // configure and execute interpreter
632  Own<interpreter::Engine> interpreter(mk<interpreter::Engine>(*ramTranslationUnit));
633  interpreter->executeMain();
634  // If the profiler was started, join back here once it exits.
635  if (profiler.joinable()) {
636  profiler.join();
637  }
638  if (Global::config().has("provenance")) {
639  // only run explain interface if interpreted
640  interpreter::ProgInterface interface(*interpreter);
641  if (Global::config().get("provenance") == "explain") {
642  explain(interface, false);
643  } else if (Global::config().get("provenance") == "explore") {
644  explain(interface, true);
645  }
646  }
647  } else {
648  // ------- compiler -------------
649  auto synthesiser = mk<synthesiser::Synthesiser>(*ramTranslationUnit);
650 
651  // Find the base filename for code generation and execution
652  std::string baseFilename;
653  if (Global::config().has("dl-program")) {
654  baseFilename = Global::config().get("dl-program");
655  } else if (Global::config().has("generate")) {
656  baseFilename = Global::config().get("generate");
657 
658  // trim .cpp extension if it exists
659  if (baseFilename.size() >= 4 && baseFilename.substr(baseFilename.size() - 4) == ".cpp") {
660  baseFilename = baseFilename.substr(0, baseFilename.size() - 4);
661  }
662  } else {
663  baseFilename = tempFile();
664  }
665  if (baseName(baseFilename) == "/" || baseName(baseFilename) == ".") {
666  baseFilename = tempFile();
667  }
668 
669  std::string baseIdentifier = identifier(simpleName(baseFilename));
670  std::string sourceFilename = baseFilename + ".cpp";
671 
672  bool withSharedLibrary;
673  const bool emitToStdOut = Global::config().has("generate", "-");
674  if (emitToStdOut)
675  synthesiser->generateCode(std::cout, baseIdentifier, withSharedLibrary);
676  else {
677  std::ofstream os{sourceFilename};
678  synthesiser->generateCode(os, baseIdentifier, withSharedLibrary);
679  }
680 
681  if (withSharedLibrary) {
682  if (!Global::config().has("libraries")) {
683  Global::config().set("libraries", "functors");
684  }
685  if (!Global::config().has("library-dir")) {
686  Global::config().set("library-dir", ".");
687  }
688  }
689 
690  auto findCompileCmd = [&] {
691  auto cmd = ::findTool("souffle-compile", souffleExecutable, ".");
692  /* Fail if a souffle-compile executable is not found */
693  if (!isExecutable(cmd)) {
694  throw std::runtime_error("failed to locate souffle-compile");
695  }
696  return cmd;
697  };
698 
699  if (Global::config().has("swig")) {
700  auto compileCmd = findCompileCmd() + " -s " + Global::config().get("swig") + " ";
701  compileToBinary(compileCmd, sourceFilename);
702  } else if (Global::config().has("compile")) {
704  compileToBinary(findCompileCmd(), sourceFilename);
705  /* Report overall run-time in verbose mode */
706  if (Global::config().has("verbose")) {
708  std::cout << "Compilation Time: " << std::chrono::duration<double>(end - start).count()
709  << "sec\n";
710  }
711  // run compiled C++ program if requested.
712  if (!Global::config().has("dl-program") && !Global::config().has("swig")) {
713  executeBinary(baseFilename);
714  }
715  }
716  }
717  } catch (std::exception& e) {
718  std::cerr << e.what() << std::endl;
719  std::exit(EXIT_FAILURE);
720  }
721 
722  /* Report overall run-time in verbose mode */
723  if (Global::config().has("verbose")) {
724  auto souffle_end = std::chrono::high_resolution_clock::now();
725  std::cout << "Total Time: " << std::chrono::duration<double>(souffle_end - souffle_start).count()
726  << "sec\n";
727  }
728 
729  return 0;
730 }
731 
732 } // end of namespace souffle
733 
734 int main(int argc, char** argv) {
735  return souffle::main(argc, argv);
736 }
souffle::baseName
std::string baseName(const std::string &filename)
Definition: FileUtil.h:199
souffle::tempFile
std::string tempFile()
Generate temporary file.
Definition: FileUtil.h:257
ChoiceConversion.h
souffle::simpleName
std::string simpleName(const std::string &path)
File name, with extension removed.
Definition: FileUtil.h:219
souffle::ParserDriver::parseTranslationUnit
static Own< ast::TranslationUnit > parseTranslationUnit(const std::string &filename, FILE *in, ErrorReport &errorReport, DebugReport &debugReport)
Definition: ParserDriver.cpp:84
souffle::compileToBinary
void compileToBinary(std::string compileCmd, const std::string &sourceFilename)
Compiles the given source file to a binary file.
Definition: main.cpp:165
Explain.h
souffle::existFile
bool existFile(const std::string &name)
Check whether a file exists in the file system.
Definition: FileUtil.h:71
souffle::explain
void explain(SouffleProgram &prog, bool ncurses)
Definition: Explain.h:627
TranslationUnit.h
SemanticChecker.h
DebugReport.h
InlineRelations.h
GroundWitnesses.h
souffle::isNumber
bool isNumber(const char *str)
Check whether a string is a sequence of digits.
Definition: StringUtil.h:217
souffle::MainConfig::help
const std::string & help() const
Definition: Global.h:123
AstToRamTranslator.h
IODefaults.h
RemoveRedundantRelations.h
HoistAggregate.h
AddNullariesToAtomlessAggregates.h
PACKAGE_VERSION
#define PACKAGE_VERSION
Definition: config.h:134
e
l j a showGridBackground &&c b raw series this eventEmitter e
Definition: htmlJsChartistMin.h:15
GroundedTermsChecker.h
MiscUtil.h
PartitionBodyLiterals.h
Pipeline.h
souffle::identifier
std::string identifier(std::string id)
Valid C++ identifier, note that this does not ensure the uniqueness of identifiers returned.
Definition: StringUtil.h:387
RemoveEmptyRelations.h
Engine.h
ProgInterface.h
MaterializeAggregationQueries.h
Synthesiser.h
Program.h
SCCGraph.h
Global.h
NameUnnamedVariables.h
Conditional.h
souffle::now
time_point now()
Definition: MiscUtil.h:89
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
ContainerUtil.h
Provenance.h
souffle::join
detail::joined_sequence< Iter, Printer > join(const Iter &a, const Iter &b, const std::string &sep, const Printer &p)
Creates an object to be forwarded to some output stream for printing sequences of elements interspers...
Definition: StreamUtil.h:175
ReorderFilterBreak.h
StringUtil.h
souffle::main
int main(int argc, char **argv)
Definition: main.cpp:191
ReportIndex.h
souffle::test::count
int count(const C &c)
Definition: table_test.cpp:40
ReduceExistentials.h
TCB_SPAN_NAMESPACE_NAME::get
constexpr auto get(span< E, S > s) -> decltype(s[N])
Definition: span.h:599
HoistConditions.h
souffle::existDir
bool existDir(const std::string &name)
Check whether a directory exists in the file system.
Definition: FileUtil.h:84
souffle::isExecutable
bool isExecutable(const std::string &name)
Check whether a given file exists and it is an executable.
Definition: FileUtil.h:97
PragmaChecker.h
Fixpoint.h
EliminateDuplicates.h
ComponentInstantiation.h
IndexedInequality.h
NormaliseMultiResultFunctors.h
Loop.h
ResolveAnonymousRecordAliases.h
TranslationUnit.h
CollapseFilters.h
Node.h
Transformer.h
souffle::findTool
std::string findTool(const std::string &tool, const std::string &base, const std::string &path)
Definition: FileUtil.h:182
souffle::which
std::string which(const std::string &name)
Simple implementation of a which tool.
Definition: FileUtil.h:104
souffle::Global::config
static MainConfig & config()
Definition: Global.h:141
ReorderConditions.h
ReorderLiterals.h
souffle::test::time
long time(const std::string &name, const Op &operation)
Definition: btree_multiset_test.cpp:411
Node.h
IOAttributes.h
TupleId.h
Program.h
MinimiseProgram.h
souffle::MainConfig::processArgs
void processArgs(int argc, char **argv, const std::string &header, const std::string &footer, const std::vector< MainOption > mainOptions)
Definition: Global.cpp:35
RamTypes.h
souffle::splitString
std::vector< std::string > splitString(const std::string &str, char delimiter)
Splits a string given a delimiter.
Definition: StringUtil.h:321
StreamUtil.h
MakeIndex.h
souffle::executeBinary
void executeBinary(const std::string &binaryFilename)
Executes a binary file.
Definition: main.cpp:124
RemoveBooleanConstraints.h
UniqueAggregationVariables.h
Type.h
config.h
Parallel.h
FileUtil.h
souffle
Definition: AggregateOp.h:25
Tui.h
ComponentChecker.h
MaterializeSingletonAggregation.h
ReplaceSingletonVariables.h
TCB_SPAN_NAMESPACE_NAME::detail::data
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.h:210
ParserDriver.h
RAM_DOMAIN_SIZE
#define RAM_DOMAIN_SIZE
Types of elements in a tuple.
Definition: RamTypes.h:46
IfConversion.h
RemoveRelationCopies.h
Sequence.h
ExpandFilter.h
PrecedenceGraph.h
ExecutionPlanChecker.h
RemoveRedundantSums.h
ErrorReport.h
MagicSet.h
Conditional.h
SimplifyAggregateTargetExpression.h
ResolveAliases.h
souffle::profile::ss
class souffle::profile::Tui ss
Definition: Tui.h:336
FoldAnonymousRecords.h