| 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 CompiledOptions.h
 | 
| 12 |  *
 | 
| 13 |  * A header file offering command-line option support for compiled
 | 
| 14 |  * RAM programs.
 | 
| 15 |  *
 | 
| 16 |  ***********************************************************************/
 | 
| 17 | 
 | 
| 18 | #pragma once
 | 
| 19 | 
 | 
| 20 | #include <cstdio>
 | 
| 21 | #include <cstdlib>
 | 
| 22 | #include <iostream>
 | 
| 23 | #include <string>
 | 
| 24 | #include <sys/stat.h>
 | 
| 25 | 
 | 
| 26 | #ifdef USE_CUSTOM_GETOPTLONG
 | 
| 27 | #include "souffle/utility/GetOptLongImpl.h"
 | 
| 28 | #else
 | 
| 29 | #include <getopt.h>
 | 
| 30 | #endif
 | 
| 31 | 
 | 
| 32 | namespace souffle {
 | 
| 33 | 
 | 
| 34 | /**
 | 
| 35 |  * A utility class for parsing command line arguments within generated
 | 
| 36 |  * query programs.
 | 
| 37 |  */
 | 
| 38 | class CmdOptions {
 | 
| 39 | protected:
 | 
| 40 |     /**
 | 
| 41 |      * source file
 | 
| 42 |      */
 | 
| 43 |     std::string src;
 | 
| 44 | 
 | 
| 45 |     /**
 | 
| 46 |      * fact directory
 | 
| 47 |      */
 | 
| 48 |     std::string input_dir;
 | 
| 49 | 
 | 
| 50 |     /**
 | 
| 51 |      * output directory
 | 
| 52 |      */
 | 
| 53 |     std::string output_dir;
 | 
| 54 | 
 | 
| 55 |     /**
 | 
| 56 |      * profiling flag
 | 
| 57 |      */
 | 
| 58 |     bool profiling;
 | 
| 59 | 
 | 
| 60 |     /**
 | 
| 61 |      * profile filename
 | 
| 62 |      */
 | 
| 63 |     std::string profile_name;
 | 
| 64 | 
 | 
| 65 |     /**
 | 
| 66 |      * number of threads
 | 
| 67 |      */
 | 
| 68 |     std::size_t num_jobs;
 | 
| 69 | 
 | 
| 70 | public:
 | 
| 71 |     // all argument constructor
 | 
| 72 |     CmdOptions(const char* s, const char* id, const char* od, bool pe, const char* pfn, std::size_t nj)
 | 
| 73 |             : src(s), input_dir(id), output_dir(od), profiling(pe), profile_name(pfn), num_jobs(nj) {}
 | 
| 74 | 
 | 
| 75 |     /**
 | 
| 76 |      * get source code name
 | 
| 77 |      */
 | 
| 78 |     const std::string& getSourceFileName() const {
 | 
| 79 |         return src;
 | 
| 80 |     }
 | 
| 81 | 
 | 
| 82 |     /**
 | 
| 83 |      * get input directory
 | 
| 84 |      */
 | 
| 85 |     const std::string& getInputFileDir() const {
 | 
| 86 |         return input_dir;
 | 
| 87 |     }
 | 
| 88 | 
 | 
| 89 |     /**
 | 
| 90 |      * get output directory
 | 
| 91 |      */
 | 
| 92 |     const std::string& getOutputFileDir() const {
 | 
| 93 |         return output_dir;
 | 
| 94 |     }
 | 
| 95 | 
 | 
| 96 |     /**
 | 
| 97 |      * is profiling switched on
 | 
| 98 |      */
 | 
| 99 |     bool isProfiling() const {
 | 
| 100 |         return profiling;
 | 
| 101 |     }
 | 
| 102 | 
 | 
| 103 |     /**
 | 
| 104 |      * get filename of profile
 | 
| 105 |      */
 | 
| 106 |     const std::string& getProfileName() const {
 | 
| 107 |         return profile_name;
 | 
| 108 |     }
 | 
| 109 | 
 | 
| 110 |     /**
 | 
| 111 |      * get number of jobs
 | 
| 112 |      */
 | 
| 113 |     std::size_t getNumJobs() const {
 | 
| 114 |         return num_jobs;
 | 
| 115 |     }
 | 
| 116 | 
 | 
| 117 |     /**
 | 
| 118 |      * Parses the given command line parameters, handles -h help requests or errors
 | 
| 119 |      * and returns whether the parsing was successful or not.
 | 
| 120 |      */
 | 
| 121 |     bool parse(int argc, char** argv) {
 | 
| 122 |         // get executable name
 | 
| 123 |         std::string exec_name = "analysis";
 | 
| 124 |         if (argc > 0) {
 | 
| 125 |             exec_name = argv[0];
 | 
| 126 |         }
 | 
| 127 | 
 | 
| 128 |         // local options
 | 
| 129 |         std::string fact_dir = input_dir;
 | 
| 130 |         std::string out_dir = output_dir;
 | 
| 131 | 
 | 
| 132 |         // long options
 | 
| 133 |         option longOptions[] = {{"facts", true, nullptr, 'F'}, {"output", true, nullptr, 'D'},
 | 
| 134 |                 {"profile", true, nullptr, 'p'}, {"jobs", true, nullptr, 'j'}, {"index", true, nullptr, 'i'},
 | 
| 135 |                 // the terminal option -- needs to be null
 | 
| 136 |                 {nullptr, false, nullptr, 0}};
 | 
| 137 | 
 | 
| 138 |         // check whether all options are fine
 | 
| 139 |         bool ok = true;
 | 
| 140 | 
 | 
| 141 |         int c; /* command-line arguments processing */
 | 
| 142 |         while ((c = getopt_long(argc, argv, "D:F:hp:j:i:", longOptions, nullptr)) != EOF) {
 | 
| 143 |             switch (c) {
 | 
| 144 |                 /* Fact directories */
 | 
| 145 |                 case 'F':
 | 
| 146 |                     if (!existDir(optarg)) {
 | 
| 147 |                         printf("Fact directory %s does not exists!\n", optarg);
 | 
| 148 |                         ok = false;
 | 
| 149 |                     }
 | 
| 150 |                     fact_dir = optarg;
 | 
| 151 |                     break;
 | 
| 152 |                 /* Output directory for resulting .csv files */
 | 
| 153 |                 case 'D':
 | 
| 154 |                     if (*optarg && !existDir(optarg) && !dirIsStdout(optarg)) {
 | 
| 155 |                         printf("Output directory %s does not exists!\n", optarg);
 | 
| 156 |                         ok = false;
 | 
| 157 |                     }
 | 
| 158 |                     out_dir = optarg;
 | 
| 159 |                     break;
 | 
| 160 |                 case 'p':
 | 
| 161 |                     if (!profiling) {
 | 
| 162 |                         std::cerr << "\nError: profiling was not enabled in compilation\n\n";
 | 
| 163 |                         printHelpPage(exec_name);
 | 
| 164 |                         exit(EXIT_FAILURE);
 | 
| 165 |                     }
 | 
| 166 |                     profile_name = optarg;
 | 
| 167 |                     break;
 | 
| 168 |                 case 'j':
 | 
| 169 | #ifdef _OPENMP
 | 
| 170 |                     if (std::string(optarg) == "auto") {
 | 
| 171 |                         num_jobs = 0;
 | 
| 172 |                     } else {
 | 
| 173 |                         int num = atoi(optarg);
 | 
| 174 |                         if (num > 0) {
 | 
| 175 |                             num_jobs = num;
 | 
| 176 |                         } else {
 | 
| 177 |                             std::cerr << "Invalid number of jobs [-j]: " << optarg << "\n";
 | 
| 178 |                             ok = false;
 | 
| 179 |                         }
 | 
| 180 |                     }
 | 
| 181 | #else
 | 
| 182 |                     std::cerr << "\nWarning: OpenMP was not enabled in compilation\n\n";
 | 
| 183 | #endif
 | 
| 184 |                     break;
 | 
| 185 |                 default: printHelpPage(exec_name); return false;
 | 
| 186 |             }
 | 
| 187 |         }
 | 
| 188 | 
 | 
| 189 |         // update member fields
 | 
| 190 |         input_dir = fact_dir;
 | 
| 191 |         output_dir = out_dir;
 | 
| 192 | 
 | 
| 193 |         // return success state
 | 
| 194 |         return ok;
 | 
| 195 |     }
 | 
| 196 | 
 | 
| 197 | private:
 | 
| 198 |     /**
 | 
| 199 |      * Prints the help page if it has been requested or there was a typo in the command line arguments.
 | 
| 200 |      */
 | 
| 201 |     void printHelpPage(const std::string& exec_name) const {
 | 
| 202 |         std::cerr << "====================================================================\n";
 | 
| 203 |         std::cerr << " Datalog Program: " << src << "\n";
 | 
| 204 |         std::cerr << " Usage: " << exec_name << " [OPTION]\n\n";
 | 
| 205 |         std::cerr << " Options:\n";
 | 
| 206 |         std::cerr << "    -D <DIR>, --output=<DIR>     -- Specify directory for output relations\n";
 | 
| 207 |         std::cerr << "                                    (default: " << output_dir << ")\n";
 | 
| 208 |         std::cerr << "                                    (suppress output with \"\")\n";
 | 
| 209 |         std::cerr << "    -F <DIR>, --facts=<DIR>      -- Specify directory for fact files\n";
 | 
| 210 |         std::cerr << "                                    (default: " << input_dir << ")\n";
 | 
| 211 |         if (profiling) {
 | 
| 212 |             std::cerr << "    -p <file>, --profile=<file>  -- Specify filename for profiling\n";
 | 
| 213 |             std::cerr << "                                    (default: " << profile_name << ")\n";
 | 
| 214 |         }
 | 
| 215 | #ifdef _OPENMP
 | 
| 216 |         std::cerr << "    -j <NUM>, --jobs=<NUM>       -- Specify number of threads\n";
 | 
| 217 |         if (num_jobs > 0) {
 | 
| 218 |             std::cerr << "                                    (default: " << num_jobs << ")\n";
 | 
| 219 |         } else {
 | 
| 220 |             std::cerr << "                                    (default: auto)\n";
 | 
| 221 |         }
 | 
| 222 | #endif
 | 
| 223 |         std::cerr << "    -h                           -- prints this help page.\n";
 | 
| 224 |         std::cerr << "--------------------------------------------------------------------\n";
 | 
| 225 | #ifdef SOUFFLE_GENERATOR_VERSION
 | 
| 226 |         std::cerr << " Version: " << SOUFFLE_GENERATOR_VERSION << std::endl;
 | 
| 227 | #endif
 | 
| 228 |         std::cerr << " Word size: " << RAM_DOMAIN_SIZE << " bits" << std::endl;
 | 
| 229 |         std::cerr << "--------------------------------------------------------------------\n";
 | 
| 230 |         std::cerr << " Copyright (c) 2016-22 The Souffle Developers." << std::endl;
 | 
| 231 |         std::cerr << " Copyright (c) 2013-16 Oracle and/or its affiliates." << std::endl;
 | 
| 232 |         std::cerr << " All rights reserved.\n";
 | 
| 233 |         std::cerr << "====================================================================\n";
 | 
| 234 |     }
 | 
| 235 | 
 | 
| 236 |     /**
 | 
| 237 |      *  Check whether a file exists in the file system
 | 
| 238 |      */
 | 
| 239 |     inline bool existFile(const std::string& name) const {
 | 
| 240 |         struct stat buffer;
 | 
| 241 |         if (stat(name.c_str(), &buffer) == 0) {
 | 
| 242 |             if ((buffer.st_mode & S_IFREG) != 0) {
 | 
| 243 |                 return true;
 | 
| 244 |             }
 | 
| 245 |         }
 | 
| 246 |         return false;
 | 
| 247 |     }
 | 
| 248 | 
 | 
| 249 |     /**
 | 
| 250 |      *  Check whether a directory exists in the file system
 | 
| 251 |      */
 | 
| 252 |     bool existDir(const std::string& name) const {
 | 
| 253 |         struct stat buffer;
 | 
| 254 |         if (stat(name.c_str(), &buffer) == 0) {
 | 
| 255 |             if ((buffer.st_mode & S_IFDIR) != 0) {
 | 
| 256 |                 return true;
 | 
| 257 |             }
 | 
| 258 |         }
 | 
| 259 |         return false;
 | 
| 260 |     }
 | 
| 261 | 
 | 
| 262 |     /**
 | 
| 263 |      *  Check whether the output is "-", for which the output should be stdout
 | 
| 264 |      */
 | 
| 265 |     bool dirIsStdout(const std::string& name) const {
 | 
| 266 |         return name == "-";
 | 
| 267 |     }
 | 
| 268 | };
 | 
| 269 | 
 | 
| 270 | }  // end of namespace souffle
 |