diff --git a/cmd/jsonnet.cpp b/cmd/jsonnet.cpp
index fc8d507b..7cbeeda4 100644
--- a/cmd/jsonnet.cpp
+++ b/cmd/jsonnet.cpp
@@ -58,6 +58,7 @@ void usage(std::ostream &o)
o << " -m / --multi
Write multiple files to the directory, list files on stdout\n";
o << " -y / --yaml-stream Write output as a YAML stream of JSON documents\n";
o << " -S / --string Expect a string, manifest as plain text\n";
+ o << " --no-trailing-newline Do not add a trailing newline to the output\n";
o << " -s / --max-stack Number of allowed stack frames\n";
o << " -t / --max-trace Max length of stack trace before cropping\n";
o << " --gc-min-objects Do not run garbage collector until this many\n";
@@ -167,6 +168,7 @@ static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config
std::vector remaining_args;
unsigned i = 0;
+ bool trailing_newline = true;
for (; i < args.size(); ++i) {
const std::string &arg = args[i];
@@ -323,6 +325,10 @@ static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config
config->evalStream = true;
} else if (arg == "-S" || arg == "--string") {
jsonnet_string_output(vm, 1);
+ } else if (arg == "--no-trailing-newline") {
+ // Keep track so we can check for mutually incompatible args.
+ trailing_newline = false;
+ jsonnet_set_trailing_newline(vm, 0);
} else if (arg.length() > 1 && arg[0] == '-') {
std::cerr << "ERROR: unrecognized argument: " << arg << std::endl;
return ARG_FAILURE;
@@ -331,6 +337,12 @@ static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config
}
}
+ if (config->evalStream && !trailing_newline) {
+ std::cerr << "ERROR: cannot use --no-trailing-newline with --yaml-stream" << std::endl;
+ usage(std::cerr);
+ return ARG_FAILURE;
+ }
+
const char *want = config->filenameIsCode ? "code" : "filename";
if (remaining_args.size() == 0) {
std::cerr << "ERROR: must give " << want << "\n" << std::endl;
diff --git a/core/libjsonnet.cpp b/core/libjsonnet.cpp
index 730cef8e..375cf00b 100644
--- a/core/libjsonnet.cpp
+++ b/core/libjsonnet.cpp
@@ -218,6 +218,7 @@ struct JsonnetVm {
VmNativeCallbackMap nativeCallbacks;
void *importCallbackContext;
bool stringOutput;
+ bool trailingNewline;
std::vector jpaths;
FmtOpts fmtOpts;
@@ -231,6 +232,7 @@ struct JsonnetVm {
importCallback(default_import_callback),
importCallbackContext(this),
stringOutput(false),
+ trailingNewline(true),
fmtDebugDesugaring(false)
{
jpaths.emplace_back("/usr/share/jsonnet-" + std::string(jsonnet_version()) + "/");
@@ -368,6 +370,11 @@ void jsonnet_string_output(struct JsonnetVm *vm, int v)
vm->stringOutput = bool(v);
}
+void jsonnet_set_trailing_newline(struct JsonnetVm *vm, int enable)
+{
+ vm->trailingNewline = bool(enable);
+}
+
void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx)
{
vm->importCallback = cb;
@@ -562,7 +569,9 @@ static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename, c
vm->importCallback,
vm->importCallbackContext,
vm->stringOutput);
- json_str += "\n";
+ if (vm->trailingNewline) {
+ json_str += "\n";
+ }
*error = false;
return from_string(vm, json_str);
} break;
@@ -593,8 +602,10 @@ static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename, c
i += pair.first.length() + 1;
memcpy(&buf[i], pair.second.c_str(), pair.second.length());
i += pair.second.length();
- buf[i] = '\n';
- i++;
+ if (vm->trailingNewline) {
+ buf[i] = '\n';
+ i++;
+ }
buf[i] = '\0';
i++;
}
@@ -604,6 +615,10 @@ static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename, c
} break;
case STREAM: {
+ if (!vm->trailingNewline) {
+ *error = true;
+ return from_string(vm, "INTERNAL: trailing-newline is required for streamed output");
+ }
std::vector documents =
jsonnet_vm_execute_stream(&alloc,
expr,
diff --git a/cpp/libjsonnet++.cpp b/cpp/libjsonnet++.cpp
index 2b1a8483..28c9efae 100644
--- a/cpp/libjsonnet++.cpp
+++ b/cpp/libjsonnet++.cpp
@@ -58,6 +58,11 @@ void Jsonnet::setStringOutput(bool string_output)
::jsonnet_string_output(vm_, string_output);
}
+void Jsonnet::setTrailingNewline(bool enable)
+{
+ ::jsonnet_set_trailing_newline(vm_, enable);
+}
+
void Jsonnet::addImportPath(const std::string& path)
{
::jsonnet_jpath_add(vm_, path.c_str());
diff --git a/include/libjsonnet++.h b/include/libjsonnet++.h
index 9a2ccc3e..cf26c3c3 100644
--- a/include/libjsonnet++.h
+++ b/include/libjsonnet++.h
@@ -60,6 +60,9 @@ class Jsonnet {
/// Set whether to expect a string as output and don't JSON encode it.
void setStringOutput(bool string_output);
+ /// Set whether to include a trailing newline in manifested/string output.
+ void setTrailingNewline(bool enable);
+
/// Set the number of lines of stack trace to display (0 to display all).
void setMaxTrace(uint32_t lines);
diff --git a/include/libjsonnet.h b/include/libjsonnet.h
index 64ed91cb..85931441 100644
--- a/include/libjsonnet.h
+++ b/include/libjsonnet.h
@@ -57,6 +57,9 @@ void jsonnet_gc_growth_trigger(struct JsonnetVm *vm, double v);
/** Expect a string as output and don't JSON encode it. */
void jsonnet_string_output(struct JsonnetVm *vm, int v);
+/** Enable/disable trailing newline in manifested/string output. */
+void jsonnet_set_trailing_newline(struct JsonnetVm *vm, int enable);
+
/** Callback used to load imports.
*
* The returned char* should be allocated with jsonnet_realloc. It will be cleaned up by
diff --git a/test_cmd/cmd_tests.source b/test_cmd/cmd_tests.source
index 012c4d22..c725981e 100644
--- a/test_cmd/cmd_tests.source
+++ b/test_cmd/cmd_tests.source
@@ -25,6 +25,8 @@ JSONNETFMT_BIN="${JSONNETFMT_BIN:-../jsonnetfmt}"
OVERRIDE_DIR="${OVERRIDE_DIR:-.}"
+UPDATE_GOLDENS="${UPDATE_GOLDENS:-0}"
+
separator() {
echo -e "\n-----------------------------\n"
}
@@ -34,31 +36,41 @@ check_file_sed() {
local RESULT_FILENAME="$2"
local GOLDEN_FILENAME="$3"
local SED_SCRIPT="$4"
+ local HAS_DIFF
- local GOLDEN_CONTENT="$(< "$GOLDEN_FILENAME")"
- local RESULT_CONTENT
if [[ -z "$SED_SCRIPT" ]]; then
- RESULT_CONTENT="$(< "$RESULT_FILENAME")"
- else
- RESULT_CONTENT="$(sed --silent -E -e "$SED_SCRIPT" "$RESULT_FILENAME")"
+ SED_SCRIPT='p'
fi
- if [ "${RESULT_CONTENT}" != "${GOLDEN_CONTENT}" ] ; then
+
+ cmp --quiet "$GOLDEN_FILENAME" <(sed --silent -E -e "$SED_SCRIPT" "$RESULT_FILENAME")
+ HAS_DIFF="$?"
+
+ if [ "${HAS_DIFF}" -eq 2 ]; then
FAILED=$((FAILED + 1))
- if [ -z "${SUMMARY_ONLY:-}" ]; then
- printf "\033[31;1mFAIL\033[0m \033[1m(stdout mismatch)\033[0m: \033[36m%s\033[0m\n" "$TEST"
- echo "Result output:"
- echo "${RESULT_CONTENT}"
- echo "Expected output:"
- echo "${GOLDEN_CONTENT}"
- echo "Diff:"
- # Using git diff for pretty format and pretty colors
- git --no-pager diff --color --no-index "$GOLDEN_FILENAME" <(echo "${RESULT_CONTENT}")
- # The output is often quite long, let's repeat the filename once more
- # to avoid wasting time on looking for it
- printf "\nTEST ABOVE: \033[36m%s\033[0m\n" "$TEST"
- separator
- fi
+ printf "\033[31;1mERROR\033[0m \033[1m(cmp failed)\033[0m: \033[36m%s\033[0m\n" "$TEST"
return 1
+ elif [ "${HAS_DIFF}" -ne 0 ] ; then
+ if [[ "${UPDATE_GOLDENS}" -eq 1 ]]; then
+ printf "\033[31;1mUPDATING\033[0m \033[1m(stdout mismatch)\033[0m: \033[36m%s\033[0m\n" "$TEST"
+ > "${GOLDEN_FILENAME}" sed --silent -E -e "$SED_SCRIPT" "$RESULT_FILENAME"
+ else
+ FAILED=$((FAILED + 1))
+ if [ -z "${SUMMARY_ONLY:-}" ]; then
+ printf "\033[31;1mFAIL\033[0m \033[1m(stdout mismatch)\033[0m: \033[36m%s\033[0m\n" "$TEST"
+ echo "Result output:"
+ sed --silent -E -e "$SED_SCRIPT" "$RESULT_FILENAME"
+ echo "Expected output:"
+ cat "${GOLDEN_FILENAME}"
+ echo "Diff:"
+ # Using git diff for pretty format and pretty colors
+ git --no-pager diff --color --no-index "$GOLDEN_FILENAME" <(sed --silent -E -e "$SED_SCRIPT" "$RESULT_FILENAME")
+ # The output is often quite long, let's repeat the filename once more
+ # to avoid wasting time on looking for it
+ printf "\nTEST ABOVE: \033[36m%s\033[0m\n" "$TEST"
+ separator
+ fi
+ return 1
+ fi
fi
}
diff --git a/test_cmd/fmt_simple5.golden.stderr b/test_cmd/fmt_simple5.golden.stderr
index 4895264a..44e26d66 100644
--- a/test_cmd/fmt_simple5.golden.stderr
+++ b/test_cmd/fmt_simple5.golden.stderr
@@ -1 +1,2 @@
ERROR: only one filename is allowed
+
diff --git a/test_cmd/help.golden.stdout b/test_cmd/help.golden.stdout
index fd820394..60e26ee8 100644
--- a/test_cmd/help.golden.stdout
+++ b/test_cmd/help.golden.stdout
@@ -10,6 +10,7 @@ Available options:
-m / --multi Write multiple files to the directory, list files on stdout
-y / --yaml-stream Write output as a YAML stream of JSON documents
-S / --string Expect a string, manifest as plain text
+ --no-trailing-newline Do not add a trailing newline to the output
-s / --max-stack Number of allowed stack frames
-t / --max-trace Max length of stack trace before cropping
--gc-min-objects Do not run garbage collector until this many
diff --git a/test_cmd/no_args.golden.stderr b/test_cmd/no_args.golden.stderr
index 099fa8c3..9c913635 100644
--- a/test_cmd/no_args.golden.stderr
+++ b/test_cmd/no_args.golden.stderr
@@ -12,6 +12,7 @@ Available options:
-m / --multi Write multiple files to the directory, list files on stdout
-y / --yaml-stream Write output as a YAML stream of JSON documents
-S / --string Expect a string, manifest as plain text
+ --no-trailing-newline Do not add a trailing newline to the output
-s / --max-stack Number of allowed stack frames
-t / --max-trace Max length of stack trace before cropping
--gc-min-objects Do not run garbage collector until this many
diff --git a/test_cmd/nonewline1.golden.stdout b/test_cmd/nonewline1.golden.stdout
new file mode 100644
index 00000000..f70d7bba
--- /dev/null
+++ b/test_cmd/nonewline1.golden.stdout
@@ -0,0 +1 @@
+42
\ No newline at end of file
diff --git a/test_cmd/nonewline2.golden.stdout b/test_cmd/nonewline2.golden.stdout
new file mode 100644
index 00000000..18d664bb
--- /dev/null
+++ b/test_cmd/nonewline2.golden.stdout
@@ -0,0 +1,5 @@
+{
+ "a": 1,
+ "b": 2,
+ "c": 3
+}
\ No newline at end of file
diff --git a/test_cmd/nonewline3.golden.stdout b/test_cmd/nonewline3.golden.stdout
new file mode 100644
index 00000000..b6fc4c62
--- /dev/null
+++ b/test_cmd/nonewline3.golden.stdout
@@ -0,0 +1 @@
+hello
\ No newline at end of file
diff --git a/test_cmd/nonewline_multi1.golden.file1 b/test_cmd/nonewline_multi1.golden.file1
new file mode 100644
index 00000000..cffcb609
--- /dev/null
+++ b/test_cmd/nonewline_multi1.golden.file1
@@ -0,0 +1 @@
+"file1"
\ No newline at end of file
diff --git a/test_cmd/nonewline_multi1.golden.file2 b/test_cmd/nonewline_multi1.golden.file2
new file mode 100644
index 00000000..edb69cd7
--- /dev/null
+++ b/test_cmd/nonewline_multi1.golden.file2
@@ -0,0 +1 @@
+"file2"
\ No newline at end of file
diff --git a/test_cmd/nonewline_multi1.golden.stdout b/test_cmd/nonewline_multi1.golden.stdout
new file mode 100644
index 00000000..ae4999cf
--- /dev/null
+++ b/test_cmd/nonewline_multi1.golden.stdout
@@ -0,0 +1,2 @@
+out/nonewline_multi1/file1
+out/nonewline_multi1/file2
diff --git a/test_cmd/nonewline_multi2.golden.file1 b/test_cmd/nonewline_multi2.golden.file1
new file mode 100644
index 00000000..08219db9
--- /dev/null
+++ b/test_cmd/nonewline_multi2.golden.file1
@@ -0,0 +1 @@
+file1
\ No newline at end of file
diff --git a/test_cmd/nonewline_multi2.golden.file2 b/test_cmd/nonewline_multi2.golden.file2
new file mode 100644
index 00000000..30d67d46
--- /dev/null
+++ b/test_cmd/nonewline_multi2.golden.file2
@@ -0,0 +1 @@
+file2
\ No newline at end of file
diff --git a/test_cmd/nonewline_multi2.golden.stdout b/test_cmd/nonewline_multi2.golden.stdout
new file mode 100644
index 00000000..c5a178cc
--- /dev/null
+++ b/test_cmd/nonewline_multi2.golden.stdout
@@ -0,0 +1,2 @@
+out/nonewline_multi2/file1
+out/nonewline_multi2/file2
diff --git a/test_cmd/nonewline_yaml1.golden.stderr b/test_cmd/nonewline_yaml1.golden.stderr
new file mode 100644
index 00000000..05896ea0
--- /dev/null
+++ b/test_cmd/nonewline_yaml1.golden.stderr
@@ -0,0 +1,46 @@
+ERROR: cannot use --no-trailing-newline with --yaml-stream
+Jsonnet commandline interpreter v0.21.0
+
+jsonnet {