• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--- jsonnet.cpp	2020-09-09 12:15:33.687539042 +0000
2+++ write_helper.cpp	2020-09-25 15:38:37.317147682 +0000
3@@ -14,559 +14,125 @@
4 limitations under the License.
5 */
6
7-#include <cassert>
8-#include <cstdlib>
9-#include <cstring>
10+// We need two functions defined in jsonnet.cpp file, used for writing output
11+// (multiple files and yaml streams) -- with minor changes (e.x. return type).
12
13-#include <exception>
14 #include <fstream>
15 #include <iostream>
16-#include <list>
17 #include <map>
18-#include <sstream>
19-#include <string>
20 #include <vector>
21
22-#include "utils.h"
23+#include "contrib/jsonnet/jsonnet_helper.h"
24
25-extern "C" {
26-#include <libjsonnet.h>
27-}
28-
29-#ifdef _WIN32
30-const char PATH_SEP = ';';
31-#else
32-const char PATH_SEP = ':';
33-#endif
34-
35-void version(std::ostream &o)
36-{
37-    o << "Jsonnet commandline interpreter " << jsonnet_version() << std::endl;
38-}
39-
40-void usage(std::ostream &o)
41-{
42-    version(o);
43-    o << "\n";
44-    o << "jsonnet {<option>} <filename>\n";
45-    o << "\n";
46-    o << "Available options:\n";
47-    o << "  -h / --help             This message\n";
48-    o << "  -e / --exec             Treat filename as code\n";
49-    o << "  -J / --jpath <dir>      Specify an additional library search dir (right-most wins)\n";
50-    o << "  -o / --output-file <file> Write to the output file rather than stdout\n";
51-    o << "  -m / --multi <dir>      Write multiple files to the directory, list files on stdout\n";
52-    o << "  -y / --yaml-stream      Write output as a YAML stream of JSON documents\n";
53-    o << "  -S / --string           Expect a string, manifest as plain text\n";
54-    o << "  -s / --max-stack <n>    Number of allowed stack frames\n";
55-    o << "  -t / --max-trace <n>    Max length of stack trace before cropping\n";
56-    o << "  --gc-min-objects <n>    Do not run garbage collector until this many\n";
57-    o << "  --gc-growth-trigger <n> Run garbage collector after this amount of object growth\n";
58-    o << "  --version               Print version\n";
59-    o << "Available options for specifying values of 'external' variables:\n";
60-    o << "Provide the value as a string:\n";
61-    o << "  -V / --ext-str <var>[=<val>]     If <val> is omitted, get from environment var <var>\n";
62-    o << "       --ext-str-file <var>=<file> Read the string from the file\n";
63-    o << "Provide a value as Jsonnet code:\n";
64-    o << "  --ext-code <var>[=<code>]    If <code> is omitted, get from environment var <var>\n";
65-    o << "  --ext-code-file <var>=<file> Read the code from the file\n";
66-    o << "Available options for specifying values of 'top-level arguments':\n";
67-    o << "Provide the value as a string:\n";
68-    o << "  -A / --tla-str <var>[=<val>]     If <val> is omitted, get from environment var <var>\n";
69-    o << "       --tla-str-file <var>=<file> Read the string from the file\n";
70-    o << "Provide a value as Jsonnet code:\n";
71-    o << "  --tla-code <var>[=<code>]    If <code> is omitted, get from environment var <var>\n";
72-    o << "  --tla-code-file <var>=<file> Read the code from the file\n";
73-    o << "Environment variables:\n";
74-    o << "JSONNET_PATH is a colon (semicolon on Windows) separated list of directories added\n";
75-    o << "in reverse order before the paths specified by --jpath (i.e. left-most wins)\n";
76-    o << "E.g. JSONNET_PATH=a:b jsonnet -J c -J d is equivalent to:\n";
77-    o << "JSONNET_PATH=d:c:a:b jsonnet\n";
78-    o << "jsonnet -J b -J a -J c -J d\n";
79-    o << "\n";
80-    o << "In all cases:\n";
81-    o << "<filename> can be - (stdin)\n";
82-    o << "Multichar options are expanded e.g. -abc becomes -a -b -c.\n";
83-    o << "The -- option suppresses option processing for subsequent arguments.\n";
84-    o << "Note that since filenames and jsonnet programs can begin with -, it is advised to\n";
85-    o << "use -- if the argument is unknown, e.g. jsonnet -- \"$FILENAME\".";
86-    o << std::endl;
87-}
88-
89-/** Class for representing configuration read from command line flags.  */
90-struct JsonnetConfig {
91-    std::vector<std::string> inputFiles;
92-    std::string outputFile;
93-    bool filenameIsCode;
94-
95-    // EVAL flags
96-    bool evalMulti;
97-    bool evalStream;
98-    std::string evalMultiOutputDir;
99-
100-    JsonnetConfig()
101-        : filenameIsCode(false),
102-          evalMulti(false),
103-          evalStream(false)
104-    {
105-    }
106-};
107-
108-bool get_var_val(const std::string &var_val, std::string &var, std::string &val)
109-{
110-    size_t eq_pos = var_val.find_first_of('=', 0);
111-    if (eq_pos == std::string::npos) {
112-        var = var_val;
113-        const char *val_cstr = ::getenv(var.c_str());
114-        if (val_cstr == nullptr) {
115-            std::cerr << "ERROR: environment variable " << var << " was undefined." << std::endl;
116-            return false;
117-        }
118-        val = val_cstr;
119-    } else {
120-        var = var_val.substr(0, eq_pos);
121-        val = var_val.substr(eq_pos + 1, std::string::npos);
122-    }
123-    return true;
124-}
125-
126-bool get_var_file(const std::string &var_file, const std::string &imp, std::string &var, std::string &val)
127-{
128-    size_t eq_pos = var_file.find_first_of('=', 0);
129-    if (eq_pos == std::string::npos) {
130-        std::cerr << "ERROR: argument not in form <var>=<file> \"" << var_file << "\"."
131-                  << std::endl;
132-        return false;
133-    }
134-    var = var_file.substr(0, eq_pos);
135-    const std::string path = var_file.substr(eq_pos + 1, std::string::npos);
136-
137-    size_t b, e;
138-    val.erase().append(imp).append(" @'");
139-    // duplicate all the single quotes in @path to make a quoted string
140-    for (b = 0; (e = path.find("'", b)) != std::string::npos; b = e + 1) {
141-        val.append(path.substr(b, e - b + 1)).push_back('\'');
142+/** Writes output files for multiple file output */
143+bool write_multi_output_files(char *output, const std::string &output_dir, bool show_output_file_names) {
144+  // If multiple file output is used, then iterate over each string from
145+  // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
146+  // construct pairs of filename and content, and write each output file.
147+  std::map<std::string, std::string> r;
148+  for (const char *c = output; *c != '\0';) {
149+    const char *filename = c;
150+    const char *c2 = c;
151+    while (*c2 != '\0') ++c2;
152+    ++c2;
153+    const char *json = c2;
154+    while (*c2 != '\0') ++c2;
155+    ++c2;
156+    c = c2;
157+    r[filename] = json;
158+  }
159+
160+  std::ostream *o;
161+  std::ofstream f;
162+
163+  o = &std::cout;
164+
165+  for (const auto &pair : r) {
166+    const std::string &new_content = pair.second;
167+    const std::string &filename = output_dir + pair.first;
168+    if (show_output_file_names) {
169+      (*o) << filename << std::endl;
170     }
171-    val.append(path.substr(b)).push_back('\'');
172-
173-    return true;
174-}
175-
176-enum ArgStatus {
177-    ARG_CONTINUE,
178-    ARG_SUCCESS,
179-    ARG_FAILURE,
180-};
181-
182-/** Parse the command line arguments, configuring the Jsonnet VM context and
183- * populating the JsonnetConfig.
184- */
185-static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config, JsonnetVm *vm)
186-{
187-    auto args = simplify_args(argc, argv);
188-    std::vector<std::string> remaining_args;
189-
190-    unsigned i = 0;
191-
192-    for (; i < args.size(); ++i) {
193-        const std::string &arg = args[i];
194-        if (arg == "-h" || arg == "--help") {
195-            usage(std::cout);
196-            return ARG_SUCCESS;
197-        } else if (arg == "-v" || arg == "--version") {
198-            version(std::cout);
199-            return ARG_SUCCESS;
200-        } else if (arg == "-e" || arg == "--exec") {
201-            config->filenameIsCode = true;
202-        } else if (arg == "-o" || arg == "--output-file") {
203-            std::string output_file = next_arg(i, args);
204-            if (output_file.length() == 0) {
205-                std::cerr << "ERROR: -o argument was empty string" << std::endl;
206-                return ARG_FAILURE;
207-            }
208-            config->outputFile = output_file;
209-        } else if (arg == "--") {
210-            // All subsequent args are not options.
211-            while ((++i) < args.size())
212-                remaining_args.push_back(args[i]);
213-            break;
214-        } else if (arg == "-s" || arg == "--max-stack") {
215-            long l = strtol_check(next_arg(i, args));
216-            if (l < 1) {
217-                std::cerr << "ERROR: invalid --max-stack value: " << l << std::endl;
218-                return ARG_FAILURE;
219-            }
220-            jsonnet_max_stack(vm, l);
221-        } else if (arg == "-J" || arg == "--jpath") {
222-            std::string dir = next_arg(i, args);
223-            if (dir.length() == 0) {
224-                std::cerr << "ERROR: -J argument was empty string" << std::endl;
225-                return ARG_FAILURE;
226-            }
227-            if (dir[dir.length() - 1] != '/') {
228-                dir += '/';
229-            }
230-            jsonnet_jpath_add(vm, dir.c_str());
231-        } else if (arg == "-V" || arg == "--ext-str") {
232-            std::string var, val;
233-            if (!get_var_val(next_arg(i, args), var, val))
234-                return ARG_FAILURE;
235-            jsonnet_ext_var(vm, var.c_str(), val.c_str());
236-        } else if (arg == "-E" || arg == "--var" || arg == "--env") {
237-            // TODO(dcunnin): Delete this in a future release.
238-            std::cerr << "WARNING: jsonnet eval -E, --var and --env are deprecated,"
239-                        << " please use -V or --ext-str." << std::endl;
240-            std::string var, val;
241-            if (!get_var_val(next_arg(i, args), var, val))
242-                return ARG_FAILURE;
243-            jsonnet_ext_var(vm, var.c_str(), val.c_str());
244-        } else if (arg == "--ext-str-file") {
245-            std::string var, val;
246-            if (!get_var_file(next_arg(i, args), "importstr", var, val))
247-                return ARG_FAILURE;
248-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
249-        } else if (arg == "-F" || arg == "--file") {
250-            // TODO(dcunnin): Delete this in a future release.
251-            std::cerr << "WARNING: jsonnet eval -F and --file are deprecated,"
252-                        << " please use --ext-str-file." << std::endl;
253-            std::string var, val;
254-            if (!get_var_file(next_arg(i, args), "importstr", var, val))
255-                return ARG_FAILURE;
256-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
257-        } else if (arg == "--ext-code") {
258-            std::string var, val;
259-            if (!get_var_val(next_arg(i, args), var, val))
260-                return ARG_FAILURE;
261-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
262-        } else if (arg == "--code-var" || arg == "--code-env") {
263-            // TODO(dcunnin): Delete this in a future release.
264-            std::cerr << "WARNING: jsonnet eval --code-var and --code-env are deprecated,"
265-                        << " please use --ext-code." << std::endl;
266-            std::string var, val;
267-            if (!get_var_val(next_arg(i, args), var, val))
268-                return ARG_FAILURE;
269-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
270-        } else if (arg == "--ext-code-file") {
271-            std::string var, val;
272-            if (!get_var_file(next_arg(i, args), "import", var, val))
273-                return ARG_FAILURE;
274-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
275-        } else if (arg == "--code-file") {
276-            // TODO(dcunnin): Delete this in a future release.
277-            std::cerr << "WARNING: jsonnet eval --code-file is deprecated,"
278-                        << " please use --ext-code-file." << std::endl;
279-            std::string var, val;
280-            if (!get_var_file(next_arg(i, args), "import", var, val))
281-                return ARG_FAILURE;
282-            jsonnet_ext_code(vm, var.c_str(), val.c_str());
283-        } else if (arg == "-A" || arg == "--tla-str") {
284-            std::string var, val;
285-            if (!get_var_val(next_arg(i, args), var, val))
286-                return ARG_FAILURE;
287-            jsonnet_tla_var(vm, var.c_str(), val.c_str());
288-        } else if (arg == "--tla-str-file") {
289-            std::string var, val;
290-            if (!get_var_file(next_arg(i, args), "importstr", var, val))
291-                return ARG_FAILURE;
292-            jsonnet_tla_code(vm, var.c_str(), val.c_str());
293-        } else if (arg == "--tla-code") {
294-            std::string var, val;
295-            if (!get_var_val(next_arg(i, args), var, val))
296-                return ARG_FAILURE;
297-            jsonnet_tla_code(vm, var.c_str(), val.c_str());
298-        } else if (arg == "--tla-code-file") {
299-            std::string var, val;
300-            if (!get_var_file(next_arg(i, args), "import", var, val))
301-                return ARG_FAILURE;
302-            jsonnet_tla_code(vm, var.c_str(), val.c_str());
303-
304-        } else if (arg == "--gc-min-objects") {
305-            long l = strtol_check(next_arg(i, args));
306-            if (l < 0) {
307-                std::cerr << "ERROR: invalid --gc-min-objects value: " << l << std::endl;
308-                return ARG_FAILURE;
309-            }
310-            jsonnet_gc_min_objects(vm, l);
311-        } else if (arg == "-t" || arg == "--max-trace") {
312-            long l = strtol_check(next_arg(i, args));
313-            if (l < 0) {
314-                std::cerr << "ERROR: invalid --max-trace value: " << l << std::endl;
315-                return ARG_FAILURE;
316-            }
317-            jsonnet_max_trace(vm, l);
318-        } else if (arg == "--gc-growth-trigger") {
319-            std::string num = next_arg(i, args);
320-            char *ep;
321-            double v = std::strtod(num.c_str(), &ep);
322-            if (*ep != '\0' || num.length() == 0) {
323-                std::cerr << "ERROR: invalid number \"" << num << "\"" << std::endl;
324-                return ARG_FAILURE;
325-            }
326-            if (v < 0) {
327-                std::cerr << "ERROR: invalid --gc-growth-trigger \"" << num << "\""
328-                            << std::endl;
329-                return ARG_FAILURE;
330-            }
331-            jsonnet_gc_growth_trigger(vm, v);
332-        } else if (arg == "-m" || arg == "--multi") {
333-            config->evalMulti = true;
334-            std::string output_dir = next_arg(i, args);
335-            if (output_dir.length() == 0) {
336-                std::cerr << "ERROR: -m argument was empty string" << std::endl;
337-                return ARG_FAILURE;
338-            }
339-            if (output_dir[output_dir.length() - 1] != '/') {
340-                output_dir += '/';
341-            }
342-            config->evalMultiOutputDir = output_dir;
343-        } else if (arg == "-y" || arg == "--yaml-stream") {
344-            config->evalStream = true;
345-        } else if (arg == "-S" || arg == "--string") {
346-            jsonnet_string_output(vm, 1);
347-        } else if (arg.length() > 1 && arg[0] == '-') {
348-            std::cerr << "ERROR: unrecognized argument: " << arg << std::endl;
349-            return ARG_FAILURE;
350-        } else {
351-            remaining_args.push_back(args[i]);
352+    {
353+      std::ifstream exists(filename.c_str());
354+      if (exists.good()) {
355+        std::string existing_content;
356+        existing_content.assign(std::istreambuf_iterator<char>(exists),
357+                                std::istreambuf_iterator<char>());
358+        if (existing_content == new_content) {
359+          // Do not bump the timestamp on the file if its content is
360+          // the same. This may trigger other tools (e.g. make) to do
361+          // unnecessary work.
362+          continue;
363         }
364+      }
365     }
366-
367-    const char *want = config->filenameIsCode ? "code" : "filename";
368-    if (remaining_args.size() == 0) {
369-        std::cerr << "ERROR: must give " << want << "\n" << std::endl;
370-        usage(std::cerr);
371-        return ARG_FAILURE;
372-    }
373-
374-    if (remaining_args.size() > 1) {
375-        std::string filename = remaining_args[0];
376-        std::cerr << "ERROR: only one " << want << " is allowed\n" << std::endl;
377-        return ARG_FAILURE;
378-    }
379-    config->inputFiles = remaining_args;
380-    return ARG_CONTINUE;
381-}
382-
383-/** Writes output files for multiple file output */
384-static bool write_multi_output_files(JsonnetVm *vm, char *output, const std::string &output_dir,
385-                                     const std::string &output_file)
386-{
387-    // If multiple file output is used, then iterate over each string from
388-    // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
389-    // construct pairs of filename and content, and write each output file.
390-    std::map<std::string, std::string> r;
391-    for (const char *c = output; *c != '\0';) {
392-        const char *filename = c;
393-        const char *c2 = c;
394-        while (*c2 != '\0')
395-            ++c2;
396-        ++c2;
397-        const char *json = c2;
398-        while (*c2 != '\0')
399-            ++c2;
400-        ++c2;
401-        c = c2;
402-        r[filename] = json;
403-    }
404-    jsonnet_realloc(vm, output, 0);
405-
406-    std::ostream *o;
407     std::ofstream f;
408-
409-    if (output_file.empty()) {
410-        o = &std::cout;
411-    } else {
412-        f.open(output_file.c_str());
413-        if (!f.good()) {
414-            std::string msg = "Writing to output file: " + output_file;
415-            perror(msg.c_str());
416-            return false;
417-        }
418-        o = &f;
419+    f.open(filename.c_str());
420+    if (!f.good()) {
421+      std::string msg = "Opening output file: " + filename;
422+      perror(msg.c_str());
423+      return false;
424+    }
425+    f << new_content;
426+    f.close();
427+    if (!f.good()) {
428+      std::string msg = "Writing to output file: " + filename;
429+      perror(msg.c_str());
430+      return false;
431     }
432+  }
433
434-    for (const auto &pair : r) {
435-        const std::string &new_content = pair.second;
436-        const std::string &filename = output_dir + pair.first;
437-        (*o) << filename << std::endl;
438-        {
439-            std::ifstream exists(filename.c_str());
440-            if (exists.good()) {
441-                std::string existing_content;
442-                existing_content.assign(std::istreambuf_iterator<char>(exists),
443-                                        std::istreambuf_iterator<char>());
444-                if (existing_content == new_content) {
445-                    // Do not bump the timestamp on the file if its content is
446-                    // the same. This may trigger other tools (e.g. make) to do
447-                    // unnecessary work.
448-                    continue;
449-                }
450-            }
451-        }
452-        std::ofstream f;
453-        f.open(filename.c_str());
454-        if (!f.good()) {
455-            std::string msg = "Opening output file: " + filename;
456-            perror(msg.c_str());
457-            return false;
458-        }
459-        f << new_content;
460-        f.close();
461-        if (!f.good()) {
462-            std::string msg = "Writing to output file: " + filename;
463-            perror(msg.c_str());
464-            return false;
465-        }
466-    }
467+  std::cout.flush();
468
469-    if (output_file.empty()) {
470-        std::cout.flush();
471-    } else {
472-        f.close();
473-        if (!f.good()) {
474-            std::string msg = "Writing to output file: " + output_file;
475-            perror(msg.c_str());
476-            return false;
477-        }
478-    }
479-    return true;
480+  return true;
481 }
482
483 /** Writes output files for YAML stream output */
484-static bool write_output_stream(JsonnetVm *vm, char *output, const std::string &output_file)
485-{
486-    std::ostream *o;
487-    std::ofstream f;
488-
489-    if (output_file.empty()) {
490-        o = &std::cout;
491-    } else {
492-        f.open(output_file.c_str());
493-        if (!f.good()) {
494-            std::string msg = "Writing to output file: " + output_file;
495-            perror(msg.c_str());
496-            return false;
497-        }
498-        o = &f;
499-    }
500-
501-    // If YAML stream output is used, then iterate over each string from
502-    // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
503-    // and add the --- and ... as defined by the YAML spec.
504-    std::vector<std::string> r;
505-    for (const char *c = output; *c != '\0';) {
506-        const char *json = c;
507-        while (*c != '\0')
508-            ++c;
509-        ++c;
510-        r.emplace_back(json);
511-    }
512-    jsonnet_realloc(vm, output, 0);
513-    for (const auto &str : r) {
514-        (*o) << "---\n";
515-        (*o) << str;
516-    }
517-    if (r.size() > 0)
518-        (*o) << "...\n";
519-    o->flush();
520-
521-    if (output_file.empty()) {
522-        std::cout.flush();
523-    } else {
524-        f.close();
525-        if (!f.good()) {
526-            std::string msg = "Writing to output file: " + output_file;
527-            perror(msg.c_str());
528-            return false;
529-        }
530+bool write_output_stream(char *output, const std::string &output_file) {
531+  std::ostream *o;
532+  std::ofstream f;
533+
534+  if (output_file.empty()) {
535+    o = &std::cout;
536+  } else {
537+    f.open(output_file.c_str());
538+    if (!f.good()) {
539+      std::string msg = "Writing to output file: " + output_file;
540+      perror(msg.c_str());
541+      return false;
542+    }
543+    o = &f;
544+  }
545+
546+  // If YAML stream output is used, then iterate over each string from
547+  // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
548+  // and add the --- and ... as defined by the YAML spec.
549+  std::vector<std::string> r;
550+  for (const char *c = output; *c != '\0';) {
551+    const char *json = c;
552+    while (*c != '\0') ++c;
553+    ++c;
554+    r.emplace_back(json);
555+  }
556+
557+  for (const auto &str : r) {
558+    (*o) << "---\n";
559+    (*o) << str;
560+  }
561+  if (r.size() > 0) (*o) << "...\n";
562+  o->flush();
563+
564+  if (output_file.empty()) {
565+    std::cout.flush();
566+  } else {
567+    f.close();
568+    if (!f.good()) {
569+      std::string msg = "Writing to output file: " + output_file;
570+      perror(msg.c_str());
571+      return false;
572     }
573+  }
574
575-    return true;
576-}
577-
578-int main(int argc, const char **argv)
579-{
580-    try {
581-        JsonnetVm *vm = jsonnet_make();
582-        JsonnetConfig config;
583-        if (const char *jsonnet_path_env = getenv("JSONNET_PATH")) {
584-            std::list<std::string> jpath;
585-            std::istringstream iss(jsonnet_path_env);
586-            std::string path;
587-            while (std::getline(iss, path, PATH_SEP)) {
588-                jpath.push_front(path);
589-            }
590-            for (const std::string &path : jpath) {
591-                jsonnet_jpath_add(vm, path.c_str());
592-            }
593-        }
594-        ArgStatus arg_status = process_args(argc, argv, &config, vm);
595-        if (arg_status != ARG_CONTINUE) {
596-            jsonnet_destroy(vm);
597-            return arg_status == ARG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
598-        }
599-
600-        // Evaluate input Jsonnet and handle any errors from Jsonnet VM.
601-        int error;
602-        char *output;
603-        assert(config.inputFiles.size() == 1);
604-
605-        // Read input file.
606-        std::string input;
607-        if (!read_input(config.filenameIsCode, &config.inputFiles[0], &input)) {
608-            jsonnet_destroy(vm);
609-            return EXIT_FAILURE;
610-        }
611-
612-        if (config.evalMulti) {
613-            output = jsonnet_evaluate_snippet_multi(
614-                vm, config.inputFiles[0].c_str(), input.c_str(), &error);
615-        } else if (config.evalStream) {
616-            output = jsonnet_evaluate_snippet_stream(
617-                vm, config.inputFiles[0].c_str(), input.c_str(), &error);
618-        } else {
619-            output = jsonnet_evaluate_snippet(
620-                vm, config.inputFiles[0].c_str(), input.c_str(), &error);
621-        }
622-
623-        if (error) {
624-            std::cerr << output;
625-            jsonnet_realloc(vm, output, 0);
626-            jsonnet_destroy(vm);
627-            return EXIT_FAILURE;
628-        }
629-
630-        // Write output JSON.
631-        if (config.evalMulti) {
632-            if (!write_multi_output_files(
633-                    vm, output, config.evalMultiOutputDir, config.outputFile)) {
634-                jsonnet_destroy(vm);
635-                return EXIT_FAILURE;
636-            }
637-        } else if (config.evalStream) {
638-            if (!write_output_stream(vm, output, config.outputFile)) {
639-                jsonnet_destroy(vm);
640-                return EXIT_FAILURE;
641-            }
642-        } else {
643-            bool successful = write_output_file(output, config.outputFile);
644-            jsonnet_realloc(vm, output, 0);
645-            if (!successful) {
646-                jsonnet_destroy(vm);
647-                return EXIT_FAILURE;
648-            }
649-        }
650-
651-        jsonnet_destroy(vm);
652-        return EXIT_SUCCESS;
653-
654-    } catch (const std::bad_alloc &) {
655-        // Avoid further allocation attempts
656-        fputs("Internal out-of-memory error (please report this)\n", stderr);
657-    } catch (const std::exception &e) {
658-        std::cerr << "Internal error (please report this): " << e.what() << std::endl;
659-    } catch (...) {
660-        std::cerr << "An unknown exception occurred (please report this)." << std::endl;
661-    }
662-    return EXIT_FAILURE;
663+  return true;
664 }
665