Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cmd/jsonnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void usage(std::ostream &o)
o << " -m / --multi <dir> 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 <n> Number of allowed stack frames\n";
o << " -t / --max-trace <n> Max length of stack trace before cropping\n";
o << " --gc-min-objects <n> Do not run garbage collector until this many\n";
Expand Down Expand Up @@ -167,6 +168,7 @@ static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config
std::vector<std::string> remaining_args;

unsigned i = 0;
bool trailing_newline = true;

for (; i < args.size(); ++i) {
const std::string &arg = args[i];
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
21 changes: 18 additions & 3 deletions core/libjsonnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ struct JsonnetVm {
VmNativeCallbackMap nativeCallbacks;
void *importCallbackContext;
bool stringOutput;
bool trailingNewline;
std::vector<std::string> jpaths;

FmtOpts fmtOpts;
Expand All @@ -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()) + "/");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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++;
}
Expand All @@ -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<std::string> documents =
jsonnet_vm_execute_stream(&alloc,
expr,
Expand Down
5 changes: 5 additions & 0 deletions cpp/libjsonnet++.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
3 changes: 3 additions & 0 deletions include/libjsonnet++.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions include/libjsonnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 32 additions & 20 deletions test_cmd/cmd_tests.source
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ JSONNETFMT_BIN="${JSONNETFMT_BIN:-../jsonnetfmt}"

OVERRIDE_DIR="${OVERRIDE_DIR:-.}"

UPDATE_GOLDENS="${UPDATE_GOLDENS:-0}"

separator() {
echo -e "\n-----------------------------\n"
}
Expand All @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions test_cmd/fmt_simple5.golden.stderr
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
ERROR: only one filename is allowed

1 change: 1 addition & 0 deletions test_cmd/help.golden.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Available options:
-m / --multi <dir> 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 <n> Number of allowed stack frames
-t / --max-trace <n> Max length of stack trace before cropping
--gc-min-objects <n> Do not run garbage collector until this many
Expand Down
1 change: 1 addition & 0 deletions test_cmd/no_args.golden.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Available options:
-m / --multi <dir> 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 <n> Number of allowed stack frames
-t / --max-trace <n> Max length of stack trace before cropping
--gc-min-objects <n> Do not run garbage collector until this many
Expand Down
1 change: 1 addition & 0 deletions test_cmd/nonewline1.golden.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
42
5 changes: 5 additions & 0 deletions test_cmd/nonewline2.golden.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"a": 1,
"b": 2,
"c": 3
}
1 change: 1 addition & 0 deletions test_cmd/nonewline3.golden.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
1 change: 1 addition & 0 deletions test_cmd/nonewline_multi1.golden.file1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"file1"
1 change: 1 addition & 0 deletions test_cmd/nonewline_multi1.golden.file2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"file2"
2 changes: 2 additions & 0 deletions test_cmd/nonewline_multi1.golden.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out/nonewline_multi1/file1
out/nonewline_multi1/file2
1 change: 1 addition & 0 deletions test_cmd/nonewline_multi2.golden.file1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file1
1 change: 1 addition & 0 deletions test_cmd/nonewline_multi2.golden.file2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file2
2 changes: 2 additions & 0 deletions test_cmd/nonewline_multi2.golden.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out/nonewline_multi2/file1
out/nonewline_multi2/file2
46 changes: 46 additions & 0 deletions test_cmd/nonewline_yaml1.golden.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
ERROR: cannot use --no-trailing-newline with --yaml-stream
Jsonnet commandline interpreter v0.21.0

jsonnet {<option>} <filename>

Available options:
-h / --help This message
-e / --exec Treat filename as code
-J / --jpath <dir> Specify an additional library search dir (right-most wins)
-o / --output-file <file> Write to the output file rather than stdout
-m / --multi <dir> 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 <n> Number of allowed stack frames
-t / --max-trace <n> Max length of stack trace before cropping
--gc-min-objects <n> Do not run garbage collector until this many
--gc-growth-trigger <n> Run garbage collector after this amount of object growth
--version Print version
Available options for specifying values of 'external' variables:
Provide the value as a string:
-V / --ext-str <var>[=<val>] If <val> is omitted, get from environment var <var>
--ext-str-file <var>=<file> Read the string from the file
Provide a value as Jsonnet code:
--ext-code <var>[=<code>] If <code> is omitted, get from environment var <var>
--ext-code-file <var>=<file> Read the code from the file
Available options for specifying values of 'top-level arguments':
Provide the value as a string:
-A / --tla-str <var>[=<val>] If <val> is omitted, get from environment var <var>
--tla-str-file <var>=<file> Read the string from the file
Provide a value as Jsonnet code:
--tla-code <var>[=<code>] If <code> is omitted, get from environment var <var>
--tla-code-file <var>=<file> Read the code from the file
Environment variables:
JSONNET_PATH is a colon (semicolon on Windows) separated list of directories added
in reverse order before the paths specified by --jpath (i.e. left-most wins)
E.g. JSONNET_PATH=a:b jsonnet -J c -J d is equivalent to:
JSONNET_PATH=d:c:a:b jsonnet
jsonnet -J b -J a -J c -J d

In all cases:
<filename> can be - (stdin)
Multichar options are expanded e.g. -abc becomes -a -b -c.
The -- option suppresses option processing for subsequent arguments.
Note that since filenames and jsonnet programs can begin with -, it is advised to
use -- if the argument is unknown, e.g. jsonnet -- "$FILENAME".
19 changes: 17 additions & 2 deletions test_cmd/run_cmd_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pushd "${DIR}"
}

unset SED_STDOUT SED_STDERR
# A sed command to replace the OUT_DIR at the start of the line.
SED_REPLACE_OUT_DIR="s@^${OUT_DIR%/}@out@;p"

# And now the tests:

Expand Down Expand Up @@ -107,7 +109,6 @@ do_test "max_trace3" 1 -t 20 -e 'local f(n, c=0) = if n == 0 then error "whee" e
do_test "max_trace4" 1 -t 0 -e 'local f(n, c=0) = if n == 0 then error "whee" else f(n - 1, c + n); f(100)'
do_test "max_trace5" 1 -t -1 -e 'true'

SED_REPLACE_OUT_DIR="s@^${OUT_DIR%/}@out@;p"
SED_STDOUT="${SED_REPLACE_OUT_DIR}"
if do_test "multi1" 0 -m "${OUT_DIR}/multi1" -e '{ file1: "file1", file2: "file2" }'; then
check_file "multi1" "${OUT_DIR}/multi1/file1" "multi1.golden.file1"
Expand All @@ -123,7 +124,21 @@ if do_test "multi3" 0 -m "${OUT_DIR}/multi3" -o "${OUT_DIR}/multi3/list" -e '{ f
check_file "multi3" "${OUT_DIR}/multi3/file2" "multi3.golden.file2"
check_file_sed "multi3" "${OUT_DIR}/multi3/list" "multi3.golden.list" "${SED_REPLACE_OUT_DIR}"
fi
unset -v SED_REPLACE_OUT_DIR

do_test "nonewline1" 0 --no-trailing-newline -e '42'
do_test "nonewline2" 0 --no-trailing-newline test.jsonnet
do_test "nonewline3" 0 --no-trailing-newline --string -e '"hello"'
SED_STDOUT="${SED_REPLACE_OUT_DIR}"
if do_test "nonewline_multi1" 0 --no-trailing-newline --multi "${OUT_DIR}/nonewline_multi1" -e '{ file1: "file1", file2: "file2" }'; then
check_file "nonewline_multi1" "${OUT_DIR}/nonewline_multi1/file1" "nonewline_multi1.golden.file1"
check_file "nonewline_multi1" "${OUT_DIR}/nonewline_multi1/file2" "nonewline_multi1.golden.file2"
fi
if do_test "nonewline_multi2" 0 --no-trailing-newline --multi "${OUT_DIR}/nonewline_multi2" --string -e '{ file1: "file1", file2: "file2" }'; then
check_file "nonewline_multi2" "${OUT_DIR}/nonewline_multi2/file1" "nonewline_multi2.golden.file1"
check_file "nonewline_multi2" "${OUT_DIR}/nonewline_multi2/file2" "nonewline_multi2.golden.file2"
fi
unset -v SED_STDOUT
do_test "nonewline_yaml1" 1 --no-trailing-newline --yaml-stream -e '[{a:1},{b:2},{c:3}]'

do_test "multi4" 1 -m -- -e 'null'
do_test "yaml1" 0 -y -e '[1,2,3]'
Expand Down