119 assert(!binaryFilename.empty() &&
"binary filename cannot be blank");
123 throw std::invalid_argument(
"Generated executable <" + binaryFilename +
"> could not be found");
130 ldPath += library +
':';
133 setenv(
"LD_LIBRARY_PATH", ldPath.c_str(), 1);
139 exePath =
"DYLD_LIBRARY_PATH=\"" + ldPath +
"\" ";
141 exePath += binaryFilename;
143 int exitCode = system(exePath.c_str());
146 remove(binaryFilename.c_str());
147 remove((binaryFilename +
".cpp").c_str());
151 if (exitCode != EXIT_SUCCESS) {
159 void compileToBinary(std::string compileCmd,
const std::string& sourceFilename) {
167 compileCmd +=
"-L" + path +
' ';
171 if (library.empty()) {
174 compileCmd +=
"-l" + library +
' ';
177 compileCmd += sourceFilename;
180 if (system(compileCmd.c_str()) != 0) {
181 throw std::invalid_argument(
"failed to compile C++ source <" + sourceFilename +
">");
185 int main(
int argc,
char** argv) {
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;
199 std::stringstream footer;
200 footer <<
"----------------------------------------------------------------------------" << 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;
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 "
219 {
"compile",
'c',
"",
"",
false,
220 "Generate C++ source code, compile to a binary executable, then run this "
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 "
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 '*' "
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."},
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."}};
264 for (
const std::string& option : configOptions) {
265 size_t splitPoint = option.find(
':');
267 std::string optionName = option.substr(0, splitPoint);
268 std::string optionValue = (splitPoint == std::string::npos)
270 : option.substr(splitPoint + 1, option.length());
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;
297 throw std::runtime_error(
"cannot open file " + std::string(
Global::config().
get(
"")));
304 throw std::runtime_error(
"-j/--jobs may only be set to 'auto' or an integer greater than 0.");
308 throw std::runtime_error(
"-j/--jobs may only be set to 'auto' or an integer greater than 0.");
315 std::cerr <<
"\nThis installation of Souffle does not support concurrent jobs.\n";
324 throw std::runtime_error(
330 std::string currentInclude =
"";
331 std::string allIncludes =
"";
335 throw std::runtime_error(
"include directory " + currentInclude +
" does not exists");
337 allIncludes +=
" -I";
338 allIncludes += currentInclude;
342 currentInclude += ch;
345 allIncludes +=
" -I" + currentInclude;
351 std::string currentMacro =
"";
352 std::string allMacros =
"";
356 allMacros += currentMacro;
362 allMacros +=
" -D" + currentMacro;
374 }
catch (std::exception&
e) {
375 std::cerr <<
e.what() << std::endl;
388 std::string souffleExecutable =
which(argv[0]);
390 if (souffleExecutable.empty()) {
391 throw std::runtime_error(
"failed to determine souffle executable path");
395 std::string cmd =
::which(
"mcpp");
398 throw std::runtime_error(
"failed to locate mcpp pre-processor");
408 FILE* in = popen(cmd.c_str(),
"r");
417 DebugReport debugReport;
418 Own<ast::TranslationUnit> astTranslationUnit =
422 int preprocessor_status = pclose(in);
423 if (preprocessor_status == -1) {
425 throw std::runtime_error(
"failed to close pre-processor pipe");
431 std::cout <<
"Parse Time: " << std::chrono::duration<double>(parser_end - parser_start).count()
436 std::cout << astTranslationUnit->getErrorReport();
437 return astTranslationUnit->getErrorReport().getNumErrors();
441 astTranslationUnit->getErrorReport().exitIfErrors();
446 (mk<ast::transform::PragmaChecker>())->apply(*astTranslationUnit);
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>());
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));
467 auto partitionPipeline =
468 mk<ast::transform::PipelineTransformer>(mk<ast::transform::NameUnnamedVariablesTransformer>(),
469 mk<ast::transform::PartitionBodyLiteralsTransformer>(),
470 mk<ast::transform::ReplaceSingletonVariablesTransformer>());
473 auto provenancePipeline = mk<ast::transform::ConditionalTransformer>(
474 Global::config().has(
"provenance"), mk<ast::transform::ProvenanceTransformer>());
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>());
514 std::vector<std::string> givenTransformers =
516 pipeline->disableTransformers(
517 std::set<std::string>(givenTransformers.begin(), givenTransformers.end()));
523 std::stringstream
ss;
527 ss <<
"Executed at ";
528 ss << std::put_time(std::localtime(&
time),
"%F %T") <<
"\n";
533 out <<
" \"" << arg.first <<
"\" -> \"" << arg.second <<
'"';
537 debugReport.addSection(
"Configuration",
"Configuration",
ss.str());
540 std::string runtimeStr =
541 "(" + std::to_string(std::chrono::duration<double>(parser_end - parser_start).
count()) +
"s)";
542 debugReport.addSection(
"Parsing",
"Parsing " + runtimeStr,
"");
544 pipeline->setDebugReport();
551 pipeline->apply(*astTranslationUnit);
556 std::cout << astTranslationUnit->getProgram() << std::endl;
562 astTranslationUnit->getAnalysis<ast::analysis::PrecedenceGraphAnalysis>()->print(std::cout);
563 std::cout << std::endl;
569 astTranslationUnit->getAnalysis<ast::analysis::SCCGraphAnalysis>()->print(std::cout);
570 std::cout << std::endl;
576 astTranslationUnit->getAnalysis<ast::analysis::TypeAnalysis>()->print(std::cout);
577 std::cout << std::endl;
584 debugReport.startSection();
585 auto ramTranslationUnit = ast2ram::AstToRamTranslator().translateUnit(*astTranslationUnit);
586 debugReport.endSection(
"ast-to-ram",
"Translate AST to RAM");
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>(),
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>(
604 mk<ParallelTransformer>()),
605 mk<ReportIndexTransformer>());
607 ramTransform->apply(*ramTranslationUnit);
610 if (ramTranslationUnit->getErrorReport().getNumIssues() != 0) {
611 std::cerr << ramTranslationUnit->getErrorReport();
616 std::cout << ramTranslationUnit->getProgram();
625 std::thread profiler;
628 profiler = std::thread([]() { profile::Tui().runProf(); });
632 Own<interpreter::Engine> interpreter(mk<interpreter::Engine>(*ramTranslationUnit));
633 interpreter->executeMain();
635 if (profiler.joinable()) {
640 interpreter::ProgInterface interface(*interpreter);
649 auto synthesiser = mk<synthesiser::Synthesiser>(*ramTranslationUnit);
652 std::string baseFilename;
659 if (baseFilename.size() >= 4 && baseFilename.substr(baseFilename.size() - 4) ==
".cpp") {
660 baseFilename = baseFilename.substr(0, baseFilename.size() - 4);
670 std::string sourceFilename = baseFilename +
".cpp";
672 bool withSharedLibrary;
675 synthesiser->generateCode(std::cout, baseIdentifier, withSharedLibrary);
677 std::ofstream os{sourceFilename};
678 synthesiser->generateCode(os, baseIdentifier, withSharedLibrary);
681 if (withSharedLibrary) {
690 auto findCompileCmd = [&] {
691 auto cmd =
::findTool(
"souffle-compile", souffleExecutable,
".");
694 throw std::runtime_error(
"failed to locate souffle-compile");
700 auto compileCmd = findCompileCmd() +
" -s " +
Global::config().get(
"swig") +
" ";
708 std::cout <<
"Compilation Time: " << std::chrono::duration<double>(end - start).count()
717 }
catch (std::exception&
e) {
718 std::cerr <<
e.what() << std::endl;
719 std::exit(EXIT_FAILURE);
725 std::cout <<
"Total Time: " << std::chrono::duration<double>(souffle_end - souffle_start).count()
734 int main(
int argc,
char** argv) {