this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
3/*
4 * Main authors:
5 * Guido Tack <guido.tack@monash.edu>
6 */
7
8/* This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11
12#ifdef _MSC_VER
13#define _CRT_SECURE_NO_WARNINGS
14#endif
15
16#include <minizinc/astexception.hh>
17#include <minizinc/file_utils.hh>
18#include <minizinc/htmlprinter.hh>
19#include <minizinc/model.hh>
20#include <minizinc/parser.hh>
21#include <minizinc/prettyprinter.hh>
22#include <minizinc/solver_config.hh>
23#include <minizinc/typecheck.hh>
24
25#include <fstream>
26#include <iomanip>
27#include <iostream>
28
29using namespace MiniZinc;
30using namespace std;
31
32bool beginswith(const string& s, const string& t) { return s.compare(0, t.length(), t) == 0; }
33
34int main(int argc, char** argv) {
35 string filename;
36 vector<string> includePaths;
37 bool flag_verbose = false;
38 bool flag_include_stdlib = false;
39 bool flag_index = true;
40 bool flag_rst = false;
41 int toplevel_groups = 0;
42 string output_base;
43 string header_file;
44 string footer_file;
45
46 string std_lib_dir;
47 if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) {
48 std_lib_dir = string(MZNSTDLIBDIR);
49 }
50 string globals_dir;
51
52 if (argc < 2) {
53 goto error;
54 }
55
56 try {
57 for (int i = 1; i < argc; i++) {
58 if (string(argv[i]) == string("-h") || string(argv[i]) == string("--help")) {
59 goto error;
60 }
61 if (string(argv[i]) == string("--version")) {
62 std::cout << "MiniZinc documentation generator, version " << MZN_VERSION_MAJOR << "."
63 << MZN_VERSION_MINOR << "." << MZN_VERSION_PATCH << std::endl;
64 std::cout << "Copyright (C) 2014-2017 Monash University, NICTA, Data61" << std::endl;
65 std::exit(EXIT_SUCCESS);
66 }
67 if (beginswith(string(argv[i]), "-I")) {
68 string include(argv[i]);
69 if (include.length() > 2) {
70 includePaths.push_back(include.substr(2) + string("/"));
71 } else {
72 i++;
73 if (i == argc) {
74 goto error;
75 }
76 includePaths.push_back(argv[i] + string("/"));
77 }
78 } else if (string(argv[i]) == string("-v") || string(argv[i]) == string("--verbose")) {
79 flag_verbose = true;
80 } else if (string(argv[i]) == "--stdlib-dir") {
81 i++;
82 if (i == argc) {
83 goto error;
84 }
85 std_lib_dir = argv[i];
86 } else if (beginswith(string(argv[i]), "-G")) {
87 string filename(argv[i]);
88 if (filename.length() > 2) {
89 globals_dir = filename.substr(2);
90 } else {
91 i++;
92 if (i == argc) {
93 goto error;
94 }
95 globals_dir = argv[i];
96 }
97 } else if (string(argv[i]) == "--toplevel-groups") {
98 i++;
99 if (i == argc) {
100 goto error;
101 }
102 toplevel_groups = atoi(argv[i]);
103 } else if (string(argv[i]) == "--html-header" || string(argv[i]) == "--rst-header") {
104 i++;
105 if (i == argc) {
106 goto error;
107 }
108 header_file = string(argv[i]);
109 } else if (string(argv[i]) == "--html-footer" || string(argv[i]) == "--rst-footer") {
110 i++;
111 if (i == argc) {
112 goto error;
113 }
114 footer_file = string(argv[i]);
115 } else if (string(argv[i]) == "--include-stdlib") {
116 flag_include_stdlib = true;
117 } else if (string(argv[i]) == "--no-index") {
118 flag_index = false;
119 } else if (string(argv[i]) == "--globals-dir" || string(argv[i]) == "--mzn-globals-dir") {
120 i++;
121 if (i == argc) {
122 goto error;
123 }
124 globals_dir = argv[i];
125 } else if (string(argv[i]) == "--output-base") {
126 i++;
127 if (i == argc) {
128 goto error;
129 }
130 output_base = argv[i];
131 } else if (string(argv[i]) == "--rst-output") {
132 flag_rst = true;
133 toplevel_groups = 0;
134 } else {
135 std::string input_file(argv[i]);
136 if (input_file.length() <= 4) {
137 std::cerr << "Error: cannot handle file " << input_file << "." << std::endl;
138 goto error;
139 }
140 size_t last_dot = input_file.find_last_of('.');
141 std::string extension;
142 if (last_dot != string::npos) {
143 extension = input_file.substr(last_dot, string::npos);
144 }
145 if (extension == ".mzn") {
146 if (filename.empty()) {
147 filename = input_file;
148 } else {
149 std::cerr << "Error: Multiple .mzn files given." << std::endl;
150 goto error;
151 }
152 } else if (extension == ".dzn" || extension == ".json") {
153 std::cerr << "Error: cannot generate documentation for data files." << std::endl;
154 } else {
155 std::cerr << "Error: cannot handle file extension " << extension << "." << std::endl;
156 goto error;
157 }
158 }
159 }
160
161 if (filename.empty()) {
162 std::cerr << "Error: no model file given." << std::endl;
163 goto error;
164 }
165
166 if (std_lib_dir.empty()) {
167 SolverConfigs solver_configs(std::cerr);
168 std_lib_dir = solver_configs.mznlibDir();
169 }
170
171 if (std_lib_dir.empty()) {
172 std::cerr << "Error: unknown minizinc standard library directory.\n"
173 << "Specify --stdlib-dir on the command line or set the\n"
174 << "MZN_STDLIB_DIR environment variable.\n";
175 std::exit(EXIT_FAILURE);
176 }
177
178 if (!globals_dir.empty()) {
179 includePaths.push_back(std_lib_dir + "/" + globals_dir + "/");
180 }
181 includePaths.push_back(std_lib_dir + "/std/");
182
183 for (auto& includePath : includePaths) {
184 if (!FileUtils::directory_exists(includePath)) {
185 std::cerr << "Cannot access include directory " << includePath << "\n";
186 std::exit(EXIT_FAILURE);
187 }
188 }
189
190 if (output_base.empty()) {
191 output_base = filename.substr(0, filename.length() - 4);
192 }
193
194 {
195 string header;
196 size_t header_title = std::string::npos;
197 size_t title_size = std::string("@TITLE").size();
198 if (!header_file.empty()) {
199 std::ifstream hs(FILE_PATH(header_file));
200 if (!hs.good()) {
201 std::cerr << "Cannot open header file " << header_file << "\n";
202 std::exit(EXIT_FAILURE);
203 }
204 std::string str((std::istreambuf_iterator<char>(hs)), std::istreambuf_iterator<char>());
205 header = str;
206 header_title = str.find("@TITLE");
207 }
208 string footer;
209 if (!footer_file.empty()) {
210 std::ifstream hs(FILE_PATH(footer_file));
211 if (!hs.good()) {
212 std::cerr << "Cannot open footer file " << footer_file << "\n";
213 std::exit(EXIT_FAILURE);
214 }
215 std::string str((std::istreambuf_iterator<char>(hs)), std::istreambuf_iterator<char>());
216 footer = str;
217 }
218
219 std::stringstream errstream;
220 if (flag_verbose) {
221 std::cerr << "Parsing '" << filename << "'" << std::endl;
222 }
223 std::vector<std::string> filenames;
224 filenames.push_back(filename);
225 Env env;
226 if (Model* m = parse(env, filenames, vector<string>(), "", "", includePaths, false, false,
227 true, flag_verbose, errstream)) {
228 try {
229 env.model(m);
230 if (flag_verbose) {
231 std::cerr << "Done parsing." << std::endl;
232 }
233 if (flag_verbose) {
234 std::cerr << "Typechecking ...";
235 }
236 vector<TypeError> typeErrors;
237 MiniZinc::typecheck(env, m, typeErrors, true, false);
238 if (!typeErrors.empty()) {
239 for (auto& typeError : typeErrors) {
240 if (flag_verbose) {
241 std::cerr << std::endl;
242 }
243 std::cerr << typeError.loc() << ":" << std::endl;
244 std::cerr << typeError.what() << ": " << typeError.msg() << std::endl;
245 }
246 exit(EXIT_FAILURE);
247 }
248 if (flag_verbose) {
249 std::cerr << " done" << std::endl;
250 }
251 std::string basename = output_base;
252 std::string basedir;
253 size_t lastSlash = output_base.find_last_of('/');
254 if (lastSlash != std::string::npos) {
255 basedir = basename.substr(0, lastSlash) + "/";
256 basename = basename.substr(lastSlash + 1, std::string::npos);
257 }
258 std::vector<HtmlDocument> docs;
259 if (flag_rst) {
260 docs = RSTPrinter::printRST(env.envi(), m, basename, toplevel_groups,
261 flag_include_stdlib, flag_index);
262 } else {
263 docs = HtmlPrinter::printHtml(env.envi(), m, basename, toplevel_groups,
264 flag_include_stdlib, flag_index);
265 }
266
267 for (auto& doc : docs) {
268 std::ofstream os(FILE_PATH(basedir + doc.filename() + (flag_rst ? ".rst" : ".html")));
269 std::string header_replace = header;
270 if (header_title != std::string::npos) {
271 header_replace = header_replace.replace(header_title, title_size, doc.title());
272 }
273 os << header_replace;
274 os << doc.document();
275 os << footer;
276 os.close();
277 }
278 } catch (LocationException& e) {
279 if (flag_verbose) {
280 std::cerr << std::endl;
281 }
282 std::cerr << e.loc() << ":" << std::endl;
283 std::cerr << e.what() << ": " << e.msg() << std::endl;
284 exit(EXIT_FAILURE);
285 } catch (Exception& e) {
286 if (flag_verbose) {
287 std::cerr << std::endl;
288 }
289 std::cerr << e.what() << ": " << e.msg() << std::endl;
290 exit(EXIT_FAILURE);
291 }
292 } else {
293 if (flag_verbose) {
294 std::cerr << std::endl;
295 }
296 std::copy(istreambuf_iterator<char>(errstream), istreambuf_iterator<char>(),
297 ostreambuf_iterator<char>(std::cerr));
298 exit(EXIT_FAILURE);
299 }
300 }
301
302 if (flag_verbose) {
303 std::cerr << "Done." << std::endl;
304 }
305 return 0;
306 } catch (...) {
307 std::cerr << " UNHANDLED EXCEPTION." << std::endl;
308 exit(EXIT_FAILURE);
309 }
310
311error:
312 std::string executable_name(argv[0]);
313 executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1);
314 std::cerr
315 << "Usage: " << executable_name
316 << " [<options>] [-I <include path>] <model>.mzn [<data>.dzn ...]" << std::endl
317 << std::endl
318 << "Options:" << std::endl
319 << " --help, -h\n Print this help message" << std::endl
320 << " --version\n Print version information" << std::endl
321 << " --include-stdlib\n Include the standard libraries in the output" << std::endl
322 << " -v, --verbose\n Print progress statements" << std::endl
323 << " --stdlib-dir <dir>\n Path to MiniZinc standard library directory" << std::endl
324 << " -G --globals-dir --mzn-globals-dir\n Search for included files in <stdlib>/<dir>."
325 << std::endl
326 << " --single-page\n Print entire documentation on a single HTML page." << std::endl
327 << " --no-index\n Do not generate an index of all symbols." << std::endl
328 << " --rst-output\n Generate ReStructuredText rather than HTML." << std::endl
329 << " --html-header, --html-footer, --rst-header, --rst-footer\n Header/footer files "
330 "to include in output."
331 << std::endl
332 << std::endl
333 << "Output options:" << std::endl
334 << std::endl
335 << " --output-base <name>\n Base name for output files" << std::endl;
336
337 exit(EXIT_FAILURE);
338}