\[
desugar_{expr}(e[e':e'':e'''], b) =
- desugar_{expr}(\texttt{std.slice}(e, e', e'', e'''), b)
+ desugar_{expr}(\texttt{\$std.slice}(e, e', e'', e'''), b)
\]
@@ -1318,27 +1318,27 @@
Desugaring
\[
- desugar_{expr}(e \mathop{==} e', b) = desugar_{expr}(\texttt{std.equals}(e, e'), b)
+ desugar_{expr}(e \mathop{==} e', b) = desugar_{expr}(\texttt{\$std.equals}(e, e'), b)
\]
\[
- desugar_{expr}(e \mathop{\%} e', b) = desugar_{expr}(\texttt{std.mod}(e, e'), b)
+ desugar_{expr}(e \mathop{\%} e', b) = desugar_{expr}(\texttt{\$std.mod}(e, e'), b)
\]
\[
desugar_{expr}(e \mathop{\texttt{in}} e', b) =
- desugar_{expr}(\texttt{std.objectHasEx}(e', e, \texttt{true}), b)
+ desugar_{expr}(\texttt{\$std.objectHasEx}(e', e, \texttt{true}), b)
\]
\[
desugar_{expr}(e \mathop{\texttt{in}} \texttt{super}, b) =
- desugar_{expr}(\texttt{std.objectHasEx}(\texttt{super}, e, \texttt{true}), b)
+ desugar_{expr}(\texttt{\$std.objectHasEx}(\texttt{super}, e, \texttt{true}), b)
\]
PHP
From dfde9c68ac5e8fce022b66ef4d3b5046e320b39d Mon Sep 17 00:00:00 2001
From: Oleg Parashchenko
Date: Mon, 14 Oct 2024 05:41:50 +0200
Subject: [PATCH 05/75] Restart the workflow
From 30ac79dac6b2bac66c950911af57305be06624cf Mon Sep 17 00:00:00 2001
From: Morten Mjelva
Date: Mon, 9 Dec 2024 23:44:22 +0100
Subject: [PATCH 06/75] Load py_runtime/py_runtime_pair from rules_python
---
MODULE.bazel | 1 +
platform_defs/BUILD | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/MODULE.bazel b/MODULE.bazel
index 94ac2641a..423af815a 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -1,6 +1,7 @@
module(name = "jsonnet", version = "0.0.0")
bazel_dep(name = "googletest", version = "1.11.0", repo_name = "com_google_googletest")
+bazel_dep(name = "rules_python", version = "0.40.0")
build_defs = use_extension("//tools/build_defs:extensions.bzl", "build_defs")
use_repo(
diff --git a/platform_defs/BUILD b/platform_defs/BUILD
index 930d7735d..d2a365772 100644
--- a/platform_defs/BUILD
+++ b/platform_defs/BUILD
@@ -1,4 +1,4 @@
-load("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair")
+load("@rules_python//python:defs.bzl", "py_runtime", "py_runtime_pair")
py_runtime(
name = "python3",
From f96c01ae3877bc9f75d7bcb111b1ee8d78c97548 Mon Sep 17 00:00:00 2001
From: Morten Mjelva
Date: Mon, 9 Dec 2024 23:47:05 +0100
Subject: [PATCH 07/75] Load py_library/py_binary from rules_python as well
---
python/BUILD | 2 ++
1 file changed, 2 insertions(+)
diff --git a/python/BUILD b/python/BUILD
index 12936ad26..089066c0c 100644
--- a/python/BUILD
+++ b/python/BUILD
@@ -1,3 +1,5 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
cc_binary(
name = "_jsonnet.so",
srcs = ["_jsonnet.c"],
From 4ab62a1cae9439b40c3f7a926607901ea7ffd980 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Fri, 17 Jan 2025 09:52:38 +0000
Subject: [PATCH 08/75] chore: update Python Bazel toolchain reference to use
@rules_python
See rules_python docs:
https://rules-python.readthedocs.io/en/latest/toolchains.html#
---
platform_defs/BUILD | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platform_defs/BUILD b/platform_defs/BUILD
index d2a365772..f4bc72237 100644
--- a/platform_defs/BUILD
+++ b/platform_defs/BUILD
@@ -16,6 +16,6 @@ py_runtime_pair(
toolchain(
name = "default_python3_toolchain",
toolchain = ":just_python3",
- toolchain_type = "@bazel_tools//tools/python:toolchain_type",
+ toolchain_type = "@rules_python//python:toolchain_type",
)
From 6542f410f9ac6836818313d0a0bf318634d2d830 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 18 Jan 2025 10:49:51 +0000
Subject: [PATCH 09/75] chore: reformat Bazel files using buildifier
---
MODULE.bazel | 5 +++-
WORKSPACE | 6 ++---
cpp/testdata/BUILD | 6 ++---
platform_defs/BUILD | 3 +--
python/BUILD | 2 +-
stdlib/BUILD | 2 +-
third_party/json/BUILD | 2 +-
third_party/md5/BUILD | 4 ++--
tools/build_defs/extensions.bzl | 8 +++----
tools/build_defs/python_repo.bzl | 41 ++++++++++++++++----------------
10 files changed, 41 insertions(+), 38 deletions(-)
diff --git a/MODULE.bazel b/MODULE.bazel
index 423af815a..2be900140 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -1,4 +1,7 @@
-module(name = "jsonnet", version = "0.0.0")
+module(
+ name = "jsonnet",
+ version = "0.0.0",
+)
bazel_dep(name = "googletest", version = "1.11.0", repo_name = "com_google_googletest")
bazel_dep(name = "rules_python", version = "0.40.0")
diff --git a/WORKSPACE b/WORKSPACE
index 97dfb640e..f10f2bb9a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -11,9 +11,9 @@ git_repository(
git_repository(
name = "com_google_googletest",
- remote = "https://github.com/google/googletest.git",
# If updating googletest version, also update CMakeLists.txt.in.
- commit = "2fe3bd994b3189899d93f1d5a881e725e046fdc2", # release: release-1.8.1
+ commit = "2fe3bd994b3189899d93f1d5a881e725e046fdc2", # release: release-1.8.1
+ remote = "https://github.com/google/googletest.git",
shallow_since = "1535728917 -0400",
)
@@ -22,5 +22,5 @@ register_toolchains("//platform_defs:default_python3_toolchain")
# This allows building C++ against python3 headers.
load("//tools/build_defs:python_repo.bzl", "python_headers")
-python_headers(name = "default_python3_headers")
+python_headers(name = "default_python3_headers")
diff --git a/cpp/testdata/BUILD b/cpp/testdata/BUILD
index b504c2c20..7f02498b9 100644
--- a/cpp/testdata/BUILD
+++ b/cpp/testdata/BUILD
@@ -1,11 +1,11 @@
-package(default_visibility = ["//visibility:public"])
-
load(
"@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl",
- "jsonnet_to_json_test",
"jsonnet_library",
+ "jsonnet_to_json_test",
)
+package(default_visibility = ["//visibility:public"])
+
jsonnet_to_json_test(
name = "example_test",
src = "example.jsonnet",
diff --git a/platform_defs/BUILD b/platform_defs/BUILD
index f4bc72237..2017b1f2b 100644
--- a/platform_defs/BUILD
+++ b/platform_defs/BUILD
@@ -3,8 +3,8 @@ load("@rules_python//python:defs.bzl", "py_runtime", "py_runtime_pair")
py_runtime(
name = "python3",
interpreter_path = "/usr/bin/python3",
- stub_shebang = "#!/usr/bin/env python3",
python_version = "PY3",
+ stub_shebang = "#!/usr/bin/env python3",
)
py_runtime_pair(
@@ -18,4 +18,3 @@ toolchain(
toolchain = ":just_python3",
toolchain_type = "@rules_python//python:toolchain_type",
)
-
diff --git a/python/BUILD b/python/BUILD
index 089066c0c..b648b15c6 100644
--- a/python/BUILD
+++ b/python/BUILD
@@ -23,8 +23,8 @@ py_test(
srcs = ["_jsonnet_test.py"],
data = [
"testdata/basic_check.jsonnet",
- "testdata/binary1230123.bin",
"testdata/binary123.bin",
+ "testdata/binary1230123.bin",
"testdata/trivial.jsonnet",
"testdata/trivial_no_eol.jsonnet",
],
diff --git a/stdlib/BUILD b/stdlib/BUILD
index 06d4ad634..519ca91be 100644
--- a/stdlib/BUILD
+++ b/stdlib/BUILD
@@ -7,8 +7,8 @@ filegroup(
cc_library(
name = "std",
- hdrs = ["std.jsonnet.h"],
srcs = [":gen-std-jsonnet-h"],
+ hdrs = ["std.jsonnet.h"],
includes = ["."],
linkstatic = 1,
)
diff --git a/third_party/json/BUILD b/third_party/json/BUILD
index 93a9710dd..896d2d1bc 100644
--- a/third_party/json/BUILD
+++ b/third_party/json/BUILD
@@ -1,4 +1,5 @@
licenses(["permissive"])
+
package(default_visibility = ["//visibility:public"])
cc_library(
@@ -10,4 +11,3 @@ cc_library(
],
includes = ["."],
)
-
diff --git a/third_party/md5/BUILD b/third_party/md5/BUILD
index 56103677d..cba3d668a 100644
--- a/third_party/md5/BUILD
+++ b/third_party/md5/BUILD
@@ -1,4 +1,5 @@
licenses(["permissive"])
+
package(default_visibility = ["//visibility:public"])
cc_library(
@@ -9,7 +10,6 @@ cc_library(
hdrs = [
"md5.h",
],
- linkopts = ["-lm"],
includes = ["."],
+ linkopts = ["-lm"],
)
-
diff --git a/tools/build_defs/extensions.bzl b/tools/build_defs/extensions.bzl
index 0a5aa700e..00e35c1c3 100644
--- a/tools/build_defs/extensions.bzl
+++ b/tools/build_defs/extensions.bzl
@@ -3,10 +3,10 @@ load(":python_repo.bzl", "python_headers")
def _impl(repository_ctx):
git_repository(
- name = "io_bazel_rules_jsonnet",
- commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f",
- remote = "https://github.com/bazelbuild/rules_jsonnet.git",
- shallow_since = "1556260764 +0200",
+ name = "io_bazel_rules_jsonnet",
+ commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f",
+ remote = "https://github.com/bazelbuild/rules_jsonnet.git",
+ shallow_since = "1556260764 +0200",
)
python_headers(name = "default_python3_headers")
diff --git a/tools/build_defs/python_repo.bzl b/tools/build_defs/python_repo.bzl
index 38d8b5f60..8e789e137 100644
--- a/tools/build_defs/python_repo.bzl
+++ b/tools/build_defs/python_repo.bzl
@@ -14,26 +14,27 @@ cc_library(
"""
def _impl(repository_ctx):
- rctx = repository_ctx
- if "/" in rctx.attr.path or "\\" in rctx.attr.path:
- # Canonicalize the path
- realpath = rctx.path(rctx.attr.path)
- else:
- # Find it in $PATH
- realpath = rctx.which(rctx.attr.path)
- rctx.symlink(realpath, "bin/python")
- include_path = rctx.execute([
- realpath, "-c", "import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())",
- ])
- if include_path.return_code != 0:
- fail("Failed to locate Python headers:\n" + include_path.stderr)
- rctx.symlink(include_path.stdout.strip(), "include")
- rctx.file(
- "WORKSPACE",
- 'workspace(name = "{}")\n'.format(rctx.name),
- )
- rctx.file("BUILD", build_file_contents)
-
+ rctx = repository_ctx
+ if "/" in rctx.attr.path or "\\" in rctx.attr.path:
+ # Canonicalize the path
+ realpath = rctx.path(rctx.attr.path)
+ else:
+ # Find it in $PATH
+ realpath = rctx.which(rctx.attr.path)
+ rctx.symlink(realpath, "bin/python")
+ include_path = rctx.execute([
+ realpath,
+ "-c",
+ "import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())",
+ ])
+ if include_path.return_code != 0:
+ fail("Failed to locate Python headers:\n" + include_path.stderr)
+ rctx.symlink(include_path.stdout.strip(), "include")
+ rctx.file(
+ "WORKSPACE",
+ 'workspace(name = "{}")\n'.format(rctx.name),
+ )
+ rctx.file("BUILD", build_file_contents)
python_headers = repository_rule(
implementation = _impl,
From 3aa1fabe0e3fe30aab2afb71157274d1752f1822 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 18 Jan 2025 13:44:58 +0000
Subject: [PATCH 10/75] chore: define a local jsonnet_json_golden_test rule
instead of using rules_jsonnet
Although rules_jsonnet is the appropriate Bazel module for external
Jsonnet users to use for their Jsonnet build actions, using it
internally within the jsonnet repo itself is actually a little difficult
as it creates a mutually recursive dependency.
Since the only thing it's being used for is the jsonnet_json_test rule,
and we have scripts to runs such tests anyway, it's relatively easy to
define our own jsonnet_json_golden_test rule and avoid the recursive
dependency.
---
cpp/testdata/BUILD | 25 +++------
test_suite/BUILD | 8 +++
tools/build_defs/golden_test.bzl | 87 ++++++++++++++++++++++++++++++++
3 files changed, 101 insertions(+), 19 deletions(-)
create mode 100644 test_suite/BUILD
create mode 100644 tools/build_defs/golden_test.bzl
diff --git a/cpp/testdata/BUILD b/cpp/testdata/BUILD
index 7f02498b9..c019693b1 100644
--- a/cpp/testdata/BUILD
+++ b/cpp/testdata/BUILD
@@ -1,40 +1,27 @@
-load(
- "@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl",
- "jsonnet_library",
- "jsonnet_to_json_test",
-)
+load("//tools/build_defs:golden_test.bzl", "jsonnet_json_golden_test")
package(default_visibility = ["//visibility:public"])
-jsonnet_to_json_test(
+jsonnet_json_golden_test(
name = "example_test",
src = "example.jsonnet",
golden = "example_golden.json",
)
-jsonnet_to_json_test(
+jsonnet_json_golden_test(
name = "importing_test",
src = "importing.jsonnet",
+ data = ["example.jsonnet"],
golden = "importing_golden.json",
- imports = ["."],
- deps = [":testlib"],
)
-jsonnet_to_json_test(
+jsonnet_json_golden_test(
name = "invalid_test",
src = "invalid.jsonnet",
- error = 1,
+ expect_error = True,
golden = "invalid.out",
)
-jsonnet_library(
- name = "testlib",
- srcs = [
- "example.jsonnet",
- "importing.jsonnet",
- ],
-)
-
filegroup(
name = "testdata",
srcs = [
diff --git a/test_suite/BUILD b/test_suite/BUILD
new file mode 100644
index 000000000..12c893782
--- /dev/null
+++ b/test_suite/BUILD
@@ -0,0 +1,8 @@
+package(default_visibility = ["//visibility:private"])
+
+# Export the tests.source so it can be used by Bazel tests elsewhere in the repo.
+sh_library(
+ name = "tests_sh_lib",
+ srcs = ["tests.source"],
+ visibility = ["//tools/build_defs:__pkg__"],
+)
diff --git a/tools/build_defs/golden_test.bzl b/tools/build_defs/golden_test.bzl
new file mode 100644
index 000000000..71e3da541
--- /dev/null
+++ b/tools/build_defs/golden_test.bzl
@@ -0,0 +1,87 @@
+"""Bazel rules for running golden tests."""
+
+def _gen_test_script(ctx):
+ return """\
+#!/bin/bash
+
+set -uo pipefail
+
+# We don't run 'init' or 'deinit' from tests.source, as these
+# just create & remove a TMP dir and Bazel deals with that for us;
+# instead we just need to ensure that the path for the Bazel
+# managed temp dir is available in TMP_DIR.
+
+TMP_DIR="$TEST_TMPDIR"
+VERBOSE=true
+DISABLE_ERROR_TESTS=
+SUMMARY_ONLY=
+
+source ./test_suite/tests.source
+
+GOLDEN_OUTPUT=$(cat '{golden_path}')
+
+test_eval '{jsonnet_path}' '{input_path}' '{expected_exit_code}' "$GOLDEN_OUTPUT" '{golden_kind}'
+
+if [ $FAILED -eq 0 ] ; then
+ echo "$0: All $EXECUTED test scripts pass."
+ exit 0
+else
+ echo "$0: FAILED: $FAILED / $EXECUTED"
+ exit 1
+fi
+""".format(
+ jsonnet_path = ctx.executable.jsonnet.short_path,
+ input_path = ctx.file.src.short_path,
+ expected_exit_code = 1 if ctx.attr.expect_error else 0,
+ golden_path = ctx.file.golden.short_path,
+ golden_kind = "PLAIN",
+ )
+
+def _jsonnet_json_golden_test_impl(ctx):
+ test_script = ctx.actions.declare_file(ctx.label.name)
+ ctx.actions.write(
+ output = test_script,
+ is_executable = True,
+ content = _gen_test_script(ctx),
+ )
+ return DefaultInfo(
+ executable = test_script,
+ runfiles = ctx.runfiles(
+ transitive_files = ctx.attr._test_sh_lib.files,
+ files = [
+ ctx.executable.jsonnet,
+ ctx.file.src,
+ ctx.file.golden,
+ ] + ctx.files.data,
+ ),
+ )
+
+jsonnet_json_golden_test = rule(
+ implementation = _jsonnet_json_golden_test_impl,
+ test = True,
+ attrs = {
+ "src": attr.label(
+ mandatory = True,
+ allow_single_file = True,
+ ),
+ "data": attr.label_list(allow_files = True),
+ "golden": attr.label(
+ mandatory = True,
+ allow_single_file = True,
+ ),
+ "jsonnet": attr.label(
+ default = "//cmd:jsonnet",
+ executable = True,
+ cfg = "exec",
+ ),
+ "expect_error": attr.bool(
+ doc = "If True, the golden file is the expected stderr output from jsonnet",
+ ),
+ "canonicalize_golden": attr.bool(
+ doc = "If True, the golden file will be reformatted prior to comparing against the jsonnet output",
+ ),
+ "_test_sh_lib": attr.label(
+ default = "//test_suite:tests_sh_lib",
+ ),
+ },
+)
From 6c31c582f49cf01c94afec02b7f7201c2f903fa8 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 18 Jan 2025 13:48:20 +0000
Subject: [PATCH 11/75] chore: remove the now-unnecessary dependency on
rules_jsonnet
---
MODULE.bazel | 1 -
WORKSPACE | 7 -------
tools/build_defs/extensions.bzl | 9 +--------
3 files changed, 1 insertion(+), 16 deletions(-)
diff --git a/MODULE.bazel b/MODULE.bazel
index 2be900140..7cdeaab8c 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -10,7 +10,6 @@ build_defs = use_extension("//tools/build_defs:extensions.bzl", "build_defs")
use_repo(
build_defs,
"default_python3_headers",
- "io_bazel_rules_jsonnet",
)
register_toolchains("//platform_defs:default_python3_toolchain")
diff --git a/WORKSPACE b/WORKSPACE
index f10f2bb9a..fdbbb1883 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -2,13 +2,6 @@ workspace(name = "jsonnet")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
-git_repository(
- name = "io_bazel_rules_jsonnet",
- commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f",
- remote = "https://github.com/bazelbuild/rules_jsonnet.git",
- shallow_since = "1556260764 +0200",
-)
-
git_repository(
name = "com_google_googletest",
# If updating googletest version, also update CMakeLists.txt.in.
diff --git a/tools/build_defs/extensions.bzl b/tools/build_defs/extensions.bzl
index 00e35c1c3..5437fb8f8 100644
--- a/tools/build_defs/extensions.bzl
+++ b/tools/build_defs/extensions.bzl
@@ -1,14 +1,7 @@
-load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
+# load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load(":python_repo.bzl", "python_headers")
def _impl(repository_ctx):
- git_repository(
- name = "io_bazel_rules_jsonnet",
- commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f",
- remote = "https://github.com/bazelbuild/rules_jsonnet.git",
- shallow_since = "1556260764 +0200",
- )
-
python_headers(name = "default_python3_headers")
build_defs = module_extension(
From 88e7354a6cf48a22fb5eb85f658ec40837cf8814 Mon Sep 17 00:00:00 2001
From: Daniel Pozo Escalona
Date: Sat, 18 Jan 2025 15:29:53 +0100
Subject: [PATCH 12/75] Document jpathdir option in Python binding (#1150)
---
doc/ref/bindings.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/ref/bindings.html b/doc/ref/bindings.html
index 030c6ab5b..7993aeab3 100644
--- a/doc/ref/bindings.html
+++ b/doc/ref/bindings.html
@@ -128,6 +128,7 @@
Python API
Keyword arguments to these functions are used to control the virtual machine. They are:
+
jpathdir (string or list of strings)
max_stack (number)
gc_min_objects (number)
gc_growth_trigger (number)
From a02a615def105c861fb02ef8bc5d89c9c5a3c25b Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 18 Jan 2025 16:13:44 +0000
Subject: [PATCH 13/75] chore: enable more triggers for the CI checks
This is mostly experimental; I'm hoping to be able to trigger checks
on some existing PRs...
---
.github/workflows/build_and_test.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index b7c08484a..367347a36 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -2,6 +2,7 @@ name: Build and Test
on:
pull_request:
+ types: [opened, reopened, synchronize, ready_for_review, review_requested]
branches-ignore:
- 'gh-pages'
push:
From 2b0122c180d89f077a98e2f7e4b608c298522478 Mon Sep 17 00:00:00 2001
From: Richard Barnes
Date: Sat, 18 Jan 2025 11:19:29 -0500
Subject: [PATCH 14/75] Make fallthroughs explicit and enable warnings around
this (#1161)
---
CMakeLists.txt | 4 ++--
core/desugarer.cpp | 2 +-
core/vm.cpp | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 213539449..84e554b6a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,8 +36,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR
${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
set(OPT "-O3")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Wextra -pedantic -std=c99 -O3 ${OPT}")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Woverloaded-virtual -pedantic -std=c++17 -fPIC ${OPT}")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Wextra -Wimplicit-fallthrough -pedantic -std=c99 -O3 ${OPT}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Wimplicit-fallthrough -Woverloaded-virtual -pedantic -std=c++17 -fPIC ${OPT}")
else()
# TODO: Windows support.
message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER_ID} not supported")
diff --git a/core/desugarer.cpp b/core/desugarer.cpp
index 6c38eb913..611c1545e 100644
--- a/core/desugarer.cpp
+++ b/core/desugarer.cpp
@@ -732,7 +732,7 @@ class Desugarer {
} break;
case BOP_MANIFEST_UNEQUAL: invert = true;
- /* fallthrough */
+ [[fallthrough]];
case BOP_MANIFEST_EQUAL: {
ast_ = equals(ast->location, ast->left, ast->right);
if (invert)
diff --git a/core/vm.cpp b/core/vm.cpp
index 0e21c82aa..7cc0a8655 100644
--- a/core/vm.cpp
+++ b/core/vm.cpp
@@ -2390,7 +2390,7 @@ class Interpreter {
stack.top().val2 = scratch;
stack.top().kind = FRAME_BINARY_OP;
}
- // Falls through.
+ [[fallthrough]];
case FRAME_BINARY_OP: {
const auto &ast = *static_cast(f.ast);
const Value &lhs = stack.top().val;
From 4487bfb8ba7ed7a3d91d79d4a1aa2205e7839558 Mon Sep 17 00:00:00 2001
From: Robert <766670+rben01@users.noreply.github.com>
Date: Sat, 18 Jan 2025 11:38:28 -0500
Subject: [PATCH 15/75] Added constant pi and several math functions (#1187)
Add constant pi and several math functions
Added:
* pi (up to IEEE 64-bit binary float precision)
* deg2rad, rad2deg
* atan2 (builtin using C/C++ std library)
* hypot (builtin using C/C++ std library)
* log2, log10
---
core/desugarer.cpp | 4 ++-
core/vm.cpp | 16 +++++++++
doc/_stdlib_gen/stdlib-content.jsonnet | 9 +++++
doc/ref/spec.html | 3 ++
doc/ref/stdlib.html | 5 +++
stdlib/std.jsonnet | 9 +++++
test_suite/stdlib.jsonnet | 48 +++++++++++++++++---------
7 files changed, 77 insertions(+), 17 deletions(-)
diff --git a/core/desugarer.cpp b/core/desugarer.cpp
index 611c1545e..3ed2b453e 100644
--- a/core/desugarer.cpp
+++ b/core/desugarer.cpp
@@ -36,7 +36,7 @@ struct BuiltinDecl {
std::vector params;
};
-static unsigned long max_builtin = 38;
+static unsigned long max_builtin = 40;
BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
{
switch (builtin) {
@@ -79,6 +79,8 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
case 36: return {U"parseYaml", {U"str"}};
case 37: return {U"encodeUTF8", {U"str"}};
case 38: return {U"decodeUTF8", {U"arr"}};
+ case 39: return {U"atan2", {U"y", U"x"}};
+ case 40: return {U"hypot", {U"a", U"b"}};
default:
std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl;
std::abort();
diff --git a/core/vm.cpp b/core/vm.cpp
index 7cc0a8655..27629fd67 100644
--- a/core/vm.cpp
+++ b/core/vm.cpp
@@ -937,6 +937,8 @@ class Interpreter {
builtins["parseYaml"] = &Interpreter::builtinParseYaml;
builtins["encodeUTF8"] = &Interpreter::builtinEncodeUTF8;
builtins["decodeUTF8"] = &Interpreter::builtinDecodeUTF8;
+ builtins["atan2"] = &Interpreter::builtinAtan2;
+ builtins["hypot"] = &Interpreter::builtinHypot;
DesugaredObject *stdlib = makeStdlibAST(alloc, "__internal__");
jsonnet_static_analysis(stdlib);
@@ -1099,6 +1101,20 @@ class Interpreter {
return nullptr;
}
+ const AST *builtinAtan2(const LocationRange &loc, const std::vector &args)
+ {
+ validateBuiltinArgs(loc, "atan2", args, {Value::NUMBER, Value::NUMBER});
+ scratch = makeNumberCheck(loc, std::atan2(args[0].v.d, args[1].v.d));
+ return nullptr;
+ }
+
+ const AST *builtinHypot(const LocationRange &loc, const std::vector &args)
+ {
+ validateBuiltinArgs(loc, "hypot", args, {Value::NUMBER, Value::NUMBER});
+ scratch = makeNumberCheck(loc, std::hypot(args[0].v.d, args[1].v.d));
+ return nullptr;
+ }
+
const AST *builtinType(const LocationRange &, const std::vector &args)
{
switch (args[0].t) {
diff --git a/doc/_stdlib_gen/stdlib-content.jsonnet b/doc/_stdlib_gen/stdlib-content.jsonnet
index c36e1c5a8..27b1d63e2 100644
--- a/doc/_stdlib_gen/stdlib-content.jsonnet
+++ b/doc/_stdlib_gen/stdlib-content.jsonnet
@@ -98,6 +98,8 @@ local html = import 'html.libsonnet';
std.pow(x, n)
std.exp(x)
std.log(x)
+
std.log2(x)
+
std.log10(x)
std.exponent(x)
std.mantissa(x)
std.floor(x)
@@ -109,12 +111,19 @@ local html = import 'html.libsonnet';
std.asin(x)
std.acos(x)
std.atan(x)
+
std.atan2(y, x)
+
std.deg2rad(x)
+
std.rad2deg(x)
+
std.hypot(a, b)
std.round(x)
std.isEven(x)
std.isOdd(x)
std.isInteger(x)
std.isDecimal(x)
+
+ The constant std.pi is also available.
+
The function std.mod(a, b) is what the % operator is desugared to. It performs
modulo arithmetic if the left hand side is a number, or if the left hand side is a string,
diff --git a/doc/ref/spec.html b/doc/ref/spec.html
index 9cfd6c01b..a9d7b852e 100644
--- a/doc/ref/spec.html
+++ b/doc/ref/spec.html
@@ -2825,7 +2825,10 @@
diff --git a/stdlib/std.jsonnet b/stdlib/std.jsonnet
index fccbe26d5..ec7ab8f4d 100644
--- a/stdlib/std.jsonnet
+++ b/stdlib/std.jsonnet
@@ -261,6 +261,15 @@ limitations under the License.
else
error 'Operator % cannot be used on types ' + std.type(a) + ' and ' + std.type(b) + '.',
+ // this is the most precision that will fit in a f64
+ pi:: 3.14159265358979311600,
+
+ deg2rad(x):: x * std.pi / 180,
+ rad2deg(x):: x * 180 / std.pi,
+
+ log2(x):: std.log(x) / std.log(2),
+ log10(x):: std.log(x) / std.log(10),
+
map(func, arr)::
if !std.isFunction(func) then
error ('std.map first param must be function, got ' + std.type(func))
diff --git a/test_suite/stdlib.jsonnet b/test_suite/stdlib.jsonnet
index fc1161ed5..1ea325c68 100644
--- a/test_suite/stdlib.jsonnet
+++ b/test_suite/stdlib.jsonnet
@@ -56,31 +56,47 @@ std.assertEqual(std.abs(33), 33) &&
std.assertEqual(std.abs(-33), 33) &&
std.assertEqual(std.abs(0), 0) &&
-// Ordinary (non-test) code can define pi as 2*std.acos(0)
-local pi = 3.14159265359;
-
-assertClose(std.sin(0.0 * pi), 0) &&
-assertClose(std.sin(0.5 * pi), 1) &&
-assertClose(std.sin(1.0 * pi), 0) &&
-assertClose(std.sin(1.5 * pi), -1) &&
-assertClose(std.sin(2.0 * pi), 0) &&
-assertClose(std.cos(0.0 * pi), 1) &&
-assertClose(std.cos(0.5 * pi), 0) &&
-assertClose(std.cos(1.0 * pi), -1) &&
-assertClose(std.cos(1.5 * pi), 0) &&
-assertClose(std.cos(2.0 * pi), 1) &&
+assertClose(std.sin(0.0 * std.pi), 0) &&
+assertClose(std.sin(0.5 * std.pi), 1) &&
+assertClose(std.sin(1.0 * std.pi), 0) &&
+assertClose(std.sin(1.5 * std.pi), -1) &&
+assertClose(std.sin(2.0 * std.pi), 0) &&
+assertClose(std.cos(0.0 * std.pi), 1) &&
+assertClose(std.cos(0.5 * std.pi), 0) &&
+assertClose(std.cos(1.0 * std.pi), -1) &&
+assertClose(std.cos(1.5 * std.pi), 0) &&
+assertClose(std.cos(2.0 * std.pi), 1) &&
assertClose(std.tan(0), 0) &&
-assertClose(std.tan(0.25 * pi), 1) &&
+assertClose(std.tan(0.25 * std.pi), 1) &&
assertClose(std.asin(0), 0) &&
assertClose(std.acos(1), 0) &&
-assertClose(std.asin(1), 0.5 * pi) &&
-assertClose(std.acos(0), 0.5 * pi) &&
+assertClose(std.asin(1), 0.5 * std.pi) &&
+assertClose(std.acos(0), 0.5 * std.pi) &&
assertClose(std.atan(0), 0) &&
+assertClose(std.atan2(1, 1), std.pi / 4) &&
+assertClose(std.atan2(-1, 1), -std.pi / 4) &&
+assertClose(std.atan2(1.2, -3.8), 2.835713782184941) && // arbitrary, done on a calculator
+assertClose(std.deg2rad(0), 0) &&
+assertClose(std.deg2rad(45), std.pi / 4) &&
+assertClose(std.deg2rad(90), std.pi / 2) &&
+assertClose(std.deg2rad(172), 3.0019663134302466) && // arbitrary, done on a calculator
+assertClose(std.rad2deg(std.pi / 4), 45) &&
+assertClose(std.rad2deg(std.pi / 2), 90) &&
+assertClose(std.rad2deg(3.0019663134302466), 172) && // arbitrary, done on a calculator
+assertClose(std.hypot(3, 4), 5) &&
+assertClose(std.hypot(5, 12), 13) &&
+assertClose(std.hypot(1, 1), std.sqrt(2)) &&
assertClose(std.log(std.exp(5)), 5) &&
assertClose(std.mantissa(1), 0.5) &&
assertClose(std.exponent(1), 1) &&
assertClose(std.mantissa(128), 0.5) &&
assertClose(std.exponent(128), 8) &&
+assertClose(std.log2(std.pow(2, -5)), -5) &&
+assertClose(std.log2(std.pow(2, 0)), 0) &&
+assertClose(std.log2(std.pow(2, std.pi)), std.pi) &&
+assertClose(std.log10(std.pow(10, -5)), -5) &&
+assertClose(std.log10(std.pow(10, 0)), 0) &&
+assertClose(std.log10(std.pow(10, std.pi)), std.pi) &&
std.assertEqual(std.clamp(-3, 0, 5), 0) &&
std.assertEqual(std.clamp(4, 0, 5), 4) &&
From 92b7747a1f61ebd09c9d013364a8f6de77732e0f Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sun, 19 Jan 2025 14:29:05 +0000
Subject: [PATCH 16/75] fix decode_utf8 for codepoints >= U+010000
Fixes #1181.
Add unit test cases to cover UTF-8 decode/encode.
---
CMakeLists.txt | 2 +-
core/BUILD | 9 ++++
core/CMakeLists.txt | 3 ++
core/unicode.h | 2 +-
core/unicode_test.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 128 insertions(+), 2 deletions(-)
create mode 100644 core/unicode_test.cpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 84e554b6a..77331f6ff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -133,6 +133,6 @@ if (BUILD_TESTS)
# target runs tests without building them.
add_custom_target(run_tests COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS libjsonnet_test libjsonnet_test_file libjsonnet_test_snippet
- jsonnet parser_test lexer_test libjsonnet++_test libjsonnet_test_locale
+ jsonnet unicode_test parser_test lexer_test libjsonnet++_test libjsonnet_test_locale
)
endif()
diff --git a/core/BUILD b/core/BUILD
index 62b4459f5..c84744ad3 100644
--- a/core/BUILD
+++ b/core/BUILD
@@ -69,3 +69,12 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "unicode_test",
+ srcs = ["unicode_test.cpp"],
+ deps = [
+ ":libjsonnet",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 048addb24..8ab37775c 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -87,6 +87,9 @@ function(add_test_executable test_name)
endfunction()
if (BUILD_TESTS)
+ add_test_executable(unicode_test)
+ add_test(unicode_test ${GLOBAL_OUTPUT_PATH}/unicode_test)
+
add_test_executable(lexer_test)
add_test(lexer_test ${GLOBAL_OUTPUT_PATH}/lexer_test)
diff --git a/core/unicode.h b/core/unicode.h
index 56484cc2d..b28fcd4b7 100644
--- a/core/unicode.h
+++ b/core/unicode.h
@@ -114,7 +114,7 @@ static inline char32_t decode_utf8(const std::string &str, size_t &i)
if ((c3 & 0xC0) != 0x80) {
return JSONNET_CODEPOINT_ERROR;
}
- return ((c0 & 0x7) << 24ul) | ((c1 & 0x3F) << 12ul) | ((c2 & 0x3F) << 6) | (c3 & 0x3F);
+ return ((c0 & 0x7) << 18ul) | ((c1 & 0x3F) << 12ul) | ((c2 & 0x3F) << 6) | (c3 & 0x3F);
} else {
return JSONNET_CODEPOINT_ERROR;
}
diff --git a/core/unicode_test.cpp b/core/unicode_test.cpp
new file mode 100644
index 000000000..c34365ee9
--- /dev/null
+++ b/core/unicode_test.cpp
@@ -0,0 +1,114 @@
+/*
+Copyright 2025 Google Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include
+#include
+#include
+#include
+#include "unicode.h"
+#include "gtest/gtest.h"
+
+namespace jsonnet::internal {
+namespace {
+
+void testEncodeDecode(char32_t codepoint, const std::string &expect_utf8) {
+ std::string buffer;
+ size_t len = encode_utf8(codepoint, buffer);
+ EXPECT_EQ(len, expect_utf8.size());
+ EXPECT_EQ(buffer, expect_utf8);
+
+ size_t at = 0;
+ char32_t decoded = decode_utf8(expect_utf8, at);
+ EXPECT_EQ(decoded, codepoint);
+ EXPECT_EQ(at, expect_utf8.size() - 1);
+}
+
+TEST(Unicode, TestUTF8)
+{
+ // ASCII encodes as itself.
+ testEncodeDecode(0x00, std::string("\x00", 1));
+ testEncodeDecode(0x41, "A");
+ testEncodeDecode(0x7f, "\x7f");
+
+ testEncodeDecode(0x80, "\xc2\x80");
+ testEncodeDecode(0x100, "\xc4\x80");
+ testEncodeDecode(0x7ff, "\xdf\xbf");
+
+ testEncodeDecode(0x800, "\xe0\xa0\x80");
+ testEncodeDecode(0x1482, "\xe1\x92\x82");
+ testEncodeDecode(0xffff, "\xef\xbf\xbf");
+
+ testEncodeDecode(0x010000, "\xf0\x90\x80\x80");
+ testEncodeDecode(0x01f600, "\xf0\x9f\x98\x80"); // U+1F600 "Grinning Face"
+ testEncodeDecode(0x0f057e, "\xf3\xb0\x95\xbe"); // U+F057E Private use area character
+ testEncodeDecode(0x10ffff, "\xf4\x8f\xbf\xbf");
+}
+
+TEST(Unicode, TestUTF8RejectBad)
+{
+ const auto test_cases = std::array{
+ "\x80", // Continuation byte without leading byte
+ "\xa0", // Continuation byte without leading byte
+ "\xbf", // Continuation byte without leading byte
+ "\xc0", // Leading byte for 2-byte sequence (missing tail)
+ "\xe0", // Leading byte for 3-byte sequence (missing tail)
+ "\xf0", // Leading byte for 4-byte sequence (missing tail)
+ "\xf8\x83\x83\x83", // Invalid leading byte
+ "\xe0\x80", // Leading byte for 3-byte sequence (missing tail)
+ "\xf0\x80", // Leading byte for 4-byte sequence (missing tail)
+ "\xf0\x80\x80", // Leading byte for 4-byte sequence (missing tail)
+ "\xc0\xcf", // Leading byte for 2-byte sequence (incorrect tail)
+ "\xe0\xcf", // Leading byte for 3-byte sequence (incorrect tail)
+ "\xf0\xcf", // Leading byte for 4-byte sequence (incorrect tail)
+ "\xe0\xcf\x80", // Leading byte for 3-byte sequence (incorrect tail)
+ "\xf0\xcf\x80", // Leading byte for 4-byte sequence (incorrect tail)
+ "\xe0\x80\xcf", // Leading byte for 3-byte sequence (incorrect tail)
+ "\xf0\x80\xcf", // Leading byte for 4-byte sequence (incorrect tail)
+ "\xf0\x80\x80\xcf", // Leading byte for 4-byte sequence (incorrect tail)
+ "\xf0\x80\xcf\x80", // Leading byte for 4-byte sequence (incorrect tail)
+ "\xf0\xcf\x80\x80", // Leading byte for 4-byte sequence (incorrect tail)
+ };
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ const auto str = test_cases[i];
+ size_t at = 0;
+ char32_t c = decode_utf8(str, at);
+
+ EXPECT_EQ(c, JSONNET_CODEPOINT_ERROR) << "expect decode to reject. case " << i << std::endl;
+ }
+}
+
+TEST(Unicode, TestUTF8RoundTripExhaustive)
+{
+ // Encode every Unicode code-point as UTF-8 and verify that
+ // it decodes to the same value.
+ std::string buffer;
+ for (int x = 0; x < JSONNET_CODEPOINT_MAX; ++x) {
+ if (x == JSONNET_CODEPOINT_ERROR) {
+ continue;
+ }
+ buffer.clear();
+ encode_utf8(x, buffer);
+
+ size_t at = 0;
+ char32_t y = decode_utf8(buffer, at);
+ EXPECT_NE(y, JSONNET_CODEPOINT_ERROR) << "UTF-8 roundtrip failed for codepoint " << x << " decode rejects" << std::endl;
+ EXPECT_EQ(x, y) << "UTF-8 roundtrip failed for codepoint " << x << " converts to " << y << std::endl;
+ EXPECT_EQ(at, buffer.size() - 1) << "UTF-8 roundtrip failed for codepoint " << x << " decodes incorrect length" << std::endl;
+ }
+}
+
+} // namespace
+} // namespace jsonnet::internal
From 5a4e8e34cc1fe841bdebb983646b9b9ae8fa8ca4 Mon Sep 17 00:00:00 2001
From: Tim Vergenz
Date: Mon, 20 Jan 2025 14:25:51 -0500
Subject: [PATCH 17/75] feat: add `|||-` chomped text block syntax (#1175)
* feat: add |||- chomped text block syntax
Resolves #289
* revert MathJax change
The optable in the MathJax code is not related to Jsonnet's syntax, and
should not have `|||-` added.
---------
Co-authored-by: John Bartholomew
---
core/formatter.cpp | 9 +++++++-
core/lexer.cpp | 11 ++++++++++
doc/js/codemirror-mode-jsonnet.js | 2 +-
doc/ref/spec.html | 22 +++++++++++--------
test_suite/text_block.jsonnet | 27 ++++++++++++++++++++++++
test_suite/text_block.jsonnet.fmt.golden | 26 +++++++++++++++++++++++
6 files changed, 86 insertions(+), 11 deletions(-)
diff --git a/core/formatter.cpp b/core/formatter.cpp
index cad3369c1..ac6d4b1e3 100644
--- a/core/formatter.cpp
+++ b/core/formatter.cpp
@@ -500,7 +500,11 @@ class Unparser {
o << encode_utf8(ast->value);
o << "'";
} else if (ast->tokenKind == LiteralString::BLOCK) {
- o << "|||\n";
+ o << "|||";
+ if (ast->value.back() != U'\n') {
+ o << "-";
+ }
+ o << "\n";
if (ast->value.c_str()[0] != U'\n')
o << ast->blockIndent;
for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
@@ -513,6 +517,9 @@ class Unparser {
o << ast->blockIndent;
}
}
+ if (ast->value.back() != U'\n') {
+ o << "\n";
+ }
o << ast->blockTermIndent << "|||";
} else if (ast->tokenKind == LiteralString::VERBATIM_DOUBLE) {
o << "@\"";
diff --git a/core/lexer.cpp b/core/lexer.cpp
index 31599f4d7..10319875c 100644
--- a/core/lexer.cpp
+++ b/core/lexer.cpp
@@ -704,6 +704,13 @@ Tokens jsonnet_lex(const std::string &filename, const char *input)
// Text block
if (*c == '|' && *(c + 1) == '|' && *(c + 2) == '|') {
c += 3; // Skip the "|||".
+
+ bool chomp_trailing_nl = false;
+ if (*c == '-') {
+ chomp_trailing_nl = true;
+ c++;
+ }
+
while (is_horz_ws(*c)) ++c; // Chomp whitespace at end of line.
if (*c != '\n') {
auto msg = "text block syntax requires new line after |||.";
@@ -762,6 +769,10 @@ Tokens jsonnet_lex(const std::string &filename, const char *input)
c += 3; // Leave after the last |
data = block.str();
kind = Token::STRING_BLOCK;
+ if (chomp_trailing_nl) {
+ assert(data.back() == '\n');
+ data.pop_back();
+ }
break; // Out of the while loop.
}
}
diff --git a/doc/js/codemirror-mode-jsonnet.js b/doc/js/codemirror-mode-jsonnet.js
index b0b826bbe..ec8649746 100644
--- a/doc/js/codemirror-mode-jsonnet.js
+++ b/doc/js/codemirror-mode-jsonnet.js
@@ -176,7 +176,7 @@
}
// Enter text block.
- if (stream.match(/\|\|\|/)) {
+ if (stream.match(/\|\|\|-?/)) {
state.textBlock = true;
state.textBlockIndent = null;
return "string";
diff --git a/doc/ref/spec.html b/doc/ref/spec.html
index a9d7b852e..e21350545 100644
--- a/doc/ref/spec.html
+++ b/doc/ref/spec.html
@@ -168,15 +168,19 @@
Lexing
subsequent non-quoted '
- Text block, beginning with |||, followed by optional whitespace and a
- new-line. The next non-empty line must be prefixed with some non-zero length
- whitespace W. The block ends at the first subsequent line that is non-empty
- and does not begin with W, and it is an error if this line does not contain
- some optional whitespace followed by |||. The content of the string is
- the concatenation of all the lines between the two |||, which either
- begin with W (in which case that prefix is stripped) or they are empty lines
- (in which case they remain as empty lines). The line ending style in the file is
- preserved in the string. This form cannot be used in import statements.
+ Text block, beginning with ||| followed by an optional
+ -, then optional whitespace and a new-line. The next non-empty
+ line must be prefixed with some non-zero length whitespace W. The
+ block ends at the first subsequent line that is non-empty and does not begin
+ with W, and it is an error if this line does not contain some
+ optional whitespace followed by |||. The content of the string
+ is the concatenation of all the lines between the two |||,
+ which either begin with W (in which case that prefix is stripped) or
+ they are empty lines (in which case they remain as empty lines). The line
+ ending style in the file is preserved in the string. If the beginning
+ ||| was followed by - then the final new-line is
+ stripped from the resulting string. This form cannot be used in
+ import statements.
diff --git a/test_suite/text_block.jsonnet b/test_suite/text_block.jsonnet
index b57eb4971..db958f848 100644
--- a/test_suite/text_block.jsonnet
+++ b/test_suite/text_block.jsonnet
@@ -58,6 +58,33 @@ local bash_mixed = |||
std.assertEqual(bash_golden, bash_mixed) &&
+// Chomp trailing newline
+local str1 = |||-
+ foo bar baz
+|||;
+
+std.assertEqual(str1, "foo bar baz") &&
+
+
+// Chomp just one trailing newline
+local str1 = |||-
+ foo bar baz
+
+|||;
+
+std.assertEqual(str1, "foo bar baz\n") &&
+
+
+// Concatenate chomped blocks
+local str1 = |||-
+ foo bar baz
+||| + " " + |||-
+ biz buzz
+|||;
+
+std.assertEqual(str1, "foo bar baz biz buzz") &&
+
+
// More indent
local str1 = |||
text
diff --git a/test_suite/text_block.jsonnet.fmt.golden b/test_suite/text_block.jsonnet.fmt.golden
index 7c7ae6e25..ac9d41334 100644
--- a/test_suite/text_block.jsonnet.fmt.golden
+++ b/test_suite/text_block.jsonnet.fmt.golden
@@ -58,6 +58,32 @@ local bash_mixed = |||
std.assertEqual(bash_golden, bash_mixed) &&
+// Chomp trailing newline
+local str1 = |||-
+ foo bar baz
+|||;
+
+std.assertEqual(str1, 'foo bar baz') &&
+
+
+// Chomp just one trailing newline
+local str1 = |||
+ foo bar baz
+|||;
+
+std.assertEqual(str1, 'foo bar baz\n') &&
+
+
+// Concatenate chomped blocks
+local str1 = |||-
+ foo bar baz
+||| + ' ' + |||-
+ biz buzz
+|||;
+
+std.assertEqual(str1, 'foo bar baz biz buzz') &&
+
+
// More indent
local str1 = |||
text
From 5408df29610f7c61e570d62c334f0bc41d817c93 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Tue, 18 Feb 2025 21:06:45 +0000
Subject: [PATCH 18/75] chore: jsonnetfmt the jsonnet source files in doc/
directory
---
doc/_stdlib_gen/stdlib.jsonnet | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/_stdlib_gen/stdlib.jsonnet b/doc/_stdlib_gen/stdlib.jsonnet
index 00841cf87..33d3da129 100644
--- a/doc/_stdlib_gen/stdlib.jsonnet
+++ b/doc/_stdlib_gen/stdlib.jsonnet
@@ -73,7 +73,7 @@ local group(group_spec, prefix) =
];
local stdlibPage = [
- in_panel(html.h1({id: 'standard_library'}, 'Standard Library')),
+ in_panel(html.h1({ id: 'standard_library' }, 'Standard Library')),
'',
in_panel(content.intro),
'',
From 02cb454879ef0c558e0427b23eae89d2d7e10378 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Tue, 18 Feb 2025 21:09:01 +0000
Subject: [PATCH 19/75] chore: update generated stdlib reference html
---
doc/ref/stdlib.html | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html
index 79703bbbb..5236bc4a3 100644
--- a/doc/ref/stdlib.html
+++ b/doc/ref/stdlib.html
@@ -250,12 +250,16 @@
std.atan2(y, x)
std.deg2rad(x)
std.rad2deg(x)
+
std.hypot(a, b)
std.round(x)
std.isEven(x)
std.isOdd(x)
std.isInteger(x)
std.isDecimal(x)
+
+ The constant std.pi is also available.
+
The function std.mod(a, b) is what the % operator is desugared to. It performs
modulo arithmetic if the left hand side is a number, or if the left hand side is a string,
From 8f48de6a4802f3f861df505027e867eed7fbf02e Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Tue, 18 Feb 2025 21:10:14 +0000
Subject: [PATCH 20/75] docs: fix formatting of examples for std.manifest*
functions
fixes #1205
---
doc/_stdlib_gen/stdlib-content.jsonnet | 31 +++--
doc/_stdlib_gen/stdlib.jsonnet | 5 +-
doc/ref/stdlib.html | 160 ++++++++++++++++++-------
3 files changed, 138 insertions(+), 58 deletions(-)
diff --git a/doc/_stdlib_gen/stdlib-content.jsonnet b/doc/_stdlib_gen/stdlib-content.jsonnet
index 27b1d63e2..c2090e7a9 100644
--- a/doc/_stdlib_gen/stdlib-content.jsonnet
+++ b/doc/_stdlib_gen/stdlib-content.jsonnet
@@ -1,5 +1,14 @@
local html = import 'html.libsonnet';
+local exampleDocMultiline(mid, ex) =
+ html.spaceless([
+ html.p({}, 'Example:'),
+ html.pre({}, ex.input),
+ html.p({}, mid),
+ html.pre({}, ex.output),
+ ])
+;
+
{
intro: html.paragraphs([
|||
@@ -772,7 +781,7 @@ local html = import 'html.libsonnet';
|||),
],
examples: [
- {
+ exampleDocMultiline('Yields a string containing this JSON:', {
input: |||
std.manifestJsonEx(
{
@@ -796,8 +805,8 @@ local html = import 'html.libsonnet';
y: { a: 1, b: 2, c: [1, 2] },
}, ' '
),
- },
- {
+ }),
+ exampleDocMultiline('Yields a string containing this JSON:', {
input: |||
std.manifestJsonEx(
{
@@ -812,7 +821,7 @@ local html = import 'html.libsonnet';
y: { a: 1, b: [1, 2] },
}, '', ' ', ' : '
),
- },
+ }),
],
},
{
@@ -824,7 +833,7 @@ local html = import 'html.libsonnet';
it calls std.manifestJsonEx with a 4-space indent:
|||,
examples: [
- {
+ exampleDocMultiline('Yields a string containing this JSON:', {
input: |||
std.manifestJson(
{
@@ -848,7 +857,7 @@ local html = import 'html.libsonnet';
y: { a: 1, b: 2, c: [1, 2] },
}
),
- },
+ }),
],
},
{
@@ -860,7 +869,7 @@ local html = import 'html.libsonnet';
it calls std.manifestJsonEx:
|||,
examples: [
- {
+ exampleDocMultiline('Yields a string containing this JSON:', {
input: |||
std.manifestJsonMinified(
{
@@ -884,7 +893,7 @@ local html = import 'html.libsonnet';
y: { a: 1, b: 2, c: [1, 2] },
}
),
- },
+ }),
],
},
{
@@ -1055,7 +1064,7 @@ local html = import 'html.libsonnet';
one or more whitespaces that are used for indentation:
|||,
examples: [
- {
+ exampleDocMultiline('Yields a string containing this TOML file:', {
input: |||
std.manifestTomlEx({
key1: "value",
@@ -1094,7 +1103,7 @@ local html = import 'html.libsonnet';
],
}, ' ')
),
- },
+ }),
],
},
],
@@ -1602,7 +1611,7 @@ local html = import 'html.libsonnet';
params: ['o'],
availableSince: '0.20.0',
description: |||
- Returns an array of objects from the given object, each object having two fields:
+ Returns an array of objects from the given object, each object having two fields:
key (string) and value (object). Does not include hidden fields.
|||,
},
diff --git a/doc/_stdlib_gen/stdlib.jsonnet b/doc/_stdlib_gen/stdlib.jsonnet
index 33d3da129..c2a7b8ede 100644
--- a/doc/_stdlib_gen/stdlib.jsonnet
+++ b/doc/_stdlib_gen/stdlib.jsonnet
@@ -15,7 +15,10 @@ local exampleDoc(ex) =
else
html.spaceless([html.code({}, ex.input), ' yields ', html.code({}, manifestJsonSingleLine(ex.output))])
;
- html.p({}, html.spaceless(['Example: ', exRep, '.']))
+ if std.objectHas(ex, 'type') then
+ ex
+ else
+ html.p({}, html.spaceless(['Example: ', exRep, '.']))
;
local hgroup(body) = html.div({ class: 'hgroup' }, body);
diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html
index 5236bc4a3..dae270f3c 100644
--- a/doc/ref/stdlib.html
+++ b/doc/ref/stdlib.html
@@ -1549,20 +1549,42 @@
key1 = "value"
+key2 = 1
+
+[section]
+ a = 1
+ b = "str"
+ c = false
+ d = [
+ 1,
+ "s",
+ [ 2, 3 ]
+ ]
+
+ [section.subsection]
+ k = "v"
+
+[[sectionArray]]
+ k = "v1"
+ v = 123
+
+[[sectionArray]]
+ c = "value2"
+ k = "v2"
@@ -3083,7 +3151,7 @@
- Returns an array of objects from the given object, each object having two fields:
+ Returns an array of objects from the given object, each object having two fields:
key (string) and value (object). Does not include hidden fields.
From 25323cce127ee0b308527e8d3b2cb15502fe6821 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Fri, 21 Feb 2025 22:03:18 +0000
Subject: [PATCH 21/75] ci: trigger release workflow manually not on
push-to-tag
---
.github/workflows/release.yml | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d497b3e48..3b4d773be 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,10 +15,7 @@
# Cut a release whenever a new tag is pushed to the repo.
name: Release
-on:
- push:
- tags:
- - "*.*.*"
+on: [workflow_dispatch]
jobs:
build:
From 4a983cd2ae16cd12f8ae3983350221723d908671 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Fri, 21 Feb 2025 23:31:26 +0000
Subject: [PATCH 22/75] ci: rename Python build and publish workflow
---
.github/workflows/{build_python_wheel.yml => publish-python.yml} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename .github/workflows/{build_python_wheel.yml => publish-python.yml} (100%)
diff --git a/.github/workflows/build_python_wheel.yml b/.github/workflows/publish-python.yml
similarity index 100%
rename from .github/workflows/build_python_wheel.yml
rename to .github/workflows/publish-python.yml
From 0566bd76becfd63cce17c7d819a427e0ef8e946b Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Fri, 21 Feb 2025 22:03:36 +0000
Subject: [PATCH 23/75] ci: initial Python build-and-publish workflow
Manually triggered. Only set up to publish to Test PyPI for now.
---
.github/workflows/publish-python.yml | 88 ++++++++++++++++++++++++----
pyproject.toml | 3 +
2 files changed, 81 insertions(+), 10 deletions(-)
create mode 100644 pyproject.toml
diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml
index d0d1fb613..6ded6307b 100644
--- a/.github/workflows/publish-python.yml
+++ b/.github/workflows/publish-python.yml
@@ -1,21 +1,56 @@
-name: Build Python Wheel
+name: Build and Publish Python Package
+
+# Be very careful granting extra permissions here, as this workflow uses third party actions.
+# Prefer to pin to exact commits for third-party actions. I'm making an exception for actions
+# maintained by GitHub itself.
# For now, just trigger this workflow manually.
-on: [workflow_dispatch]
+on:
+ workflow_dispatch:
+ inputs:
+ upload_to_pypi:
+ description: "Upload generate package files to PyPI"
+ required: true
+ type: boolean
+ use_prod_pypi:
+ description: "Use production PyPI not Test PyPI"
+ required: false
+ type: boolean
jobs:
+ build_sdist:
+ name: Build Python sdist
+ runs-on: ubuntu-24.04
+ permissions:
+ contents: read
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.13"
+ cache: "pip"
+
+ - run: pip install 'build==1.2.2'
+
+ - name: Build sdist
+ run: python3 -m build --sdist
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: sdist
+ path: ./dist/*.gz
+ if-no-files-found: error
+
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
- #os: [ubuntu-20.04, windows-latest, macos-latest]
- os: [ubuntu-20.04, windows-latest]
+ #os: [ubuntu-22.04, windows-latest, macos-latest]
+ os: [ubuntu-22.04, windows-latest]
- # We're using third party actions (not just the official GitHub-maintained actions),
- # so it's very important to minimise the ability for this workflow to do anything dangerous.
- # Grant it only read access to the repository content (which is public anyway); it shouldn't
- # need anything else.
permissions:
contents: read
@@ -23,7 +58,7 @@ jobs:
- uses: actions/checkout@v4
- name: Build wheels
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@ee63bf16da6cddfb925f542f2c7b59ad50e93969 # v2.22.0
env:
# Skip PyPy, 32-bit Windows, 32-bit Linux, and CPython before 3.9.
# See https://cibuildwheel.readthedocs.io/en/stable/options/#examples_1
@@ -33,5 +68,38 @@ jobs:
- uses: actions/upload-artifact@v4
with:
- name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
+ name: cibw-wheels-${{ matrix.os }}
path: ./wheelhouse/*.whl
+ if-no-files-found: error
+
+ upload_to_test_pypi:
+ name: Upload packages to TEST PyPI
+ needs: [build_sdist, build_wheels]
+ runs-on: ubuntu-24.04
+ if: "${{ inputs.upload_to_pypi && !inputs.use_prod_pypi }}"
+ permissions:
+ contents: read
+ id-token: write # Needed for PyPI Trusted Publishing
+ environment:
+ name: testpypi
+ url: https://test.pypi.org/p/jsonnet
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ path: cibw-wheels
+ pattern: cibw-wheels-*
+ - uses: actions/download-artifact@v4
+ with:
+ path: sdist
+ name: sdist
+ - name: Flatten wheels to one directory
+ run: |
+ mkdir dist
+ find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';'
+ find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';'
+ - name: Publish to TEST PyPI
+ uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
+ with:
+ verbose: true
+ print-hash: true
+ repository-url: https://test.pypi.org/legacy/
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..fed528d4a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
From 05ac3b564abcdc4f1dcc54a45ffa1a440ad6ce42 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 00:08:39 +0000
Subject: [PATCH 24/75] fix Python build version extraction from libjsonnet.h
---
setup.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index 914b90a37..200dc0ab3 100644
--- a/setup.py
+++ b/setup.py
@@ -41,7 +41,9 @@ def get_version():
"""
Parses the version out of libjsonnet.h
"""
- rx = re.compile(r'^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"v([0-9.]+)"\s*$')
+ rx = re.compile(
+ r'^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"v([0-9.]+(?:-?[a-z][a-z0-9]*)?)"\s*$'
+ )
with open(os.path.join(DIR, "include/libjsonnet.h")) as f:
for line in f:
m = rx.match(line)
From 55743c3b595abf57ccdbbfd9f8ae11f93a632e8b Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 00:13:36 +0000
Subject: [PATCH 25/75] ci: build and test on Ubuntu 22.04 runner; 20.04 is
going away very soon
---
.github/workflows/build_and_test.yml | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 367347a36..0518ded4b 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -4,10 +4,10 @@ on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review, review_requested]
branches-ignore:
- - 'gh-pages'
+ - "gh-pages"
push:
branches:
- - 'master'
+ - "master"
tags:
- v*
@@ -17,7 +17,7 @@ permissions:
jobs:
check_code_format:
name: Check code formatting
- runs-on: 'ubuntu-20.04'
+ runs-on: "ubuntu-22.04"
# The code currently doesn't pass formatting.
# Enable this workflow job once it does, to detect regressions.
if: false
@@ -29,7 +29,7 @@ jobs:
plain_makefile:
name: Build and test using plain Makefile
- runs-on: 'ubuntu-20.04'
+ runs-on: "ubuntu-22.04"
steps:
- uses: actions/checkout@v4
- name: Build (make all)
@@ -41,7 +41,7 @@ jobs:
python_module:
name: Build and test Python module
- runs-on: 'ubuntu-20.04'
+ runs-on: "ubuntu-22.04"
steps:
- uses: actions/checkout@v4
- name: Build
@@ -53,7 +53,7 @@ jobs:
cmake_build:
name: Build and test using CMake
- runs-on: 'ubuntu-20.04'
+ runs-on: "ubuntu-22.04"
steps:
- uses: actions/checkout@v4
- name: Configure (cmake)
@@ -70,7 +70,7 @@ jobs:
bazel_build:
name: Build and test using Bazel
- runs-on: 'ubuntu-20.04'
+ runs-on: "ubuntu-22.04"
steps:
- uses: actions/checkout@v4
- name: Build
From 3325b4b03e03d6e967480d1efb1983bbe78e9b67 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 00:57:26 +0000
Subject: [PATCH 26/75] ci: update the release workflow to do a few extra
things
This updates to a newer version of the release helper action, and makes
some other adjustments:
- The release is created as a draft.
- Release notes are appended.
- The jsonnet version is extracted directly from the libjsonnet.h
header, so that it can get version even when running the workflow from
an experimental branch.
- If the workflow is not run at a tag, or the tag doesn't match the
actual jsonnet version as declared in the header, then the generated
archive will have a (partial) commit sha included in its name, so that
it can be distinguished and identified as non-canonical.
---
...archive_and_notes.sh => create_archive.sh} | 29 ++++++++++++------
.github/workflows/release.yml | 30 +++++++++++++------
2 files changed, 41 insertions(+), 18 deletions(-)
rename .github/workflows/{create_archive_and_notes.sh => create_archive.sh} (56%)
diff --git a/.github/workflows/create_archive_and_notes.sh b/.github/workflows/create_archive.sh
similarity index 56%
rename from .github/workflows/create_archive_and_notes.sh
rename to .github/workflows/create_archive.sh
index 460ea3f74..cb7a03992 100755
--- a/.github/workflows/create_archive_and_notes.sh
+++ b/.github/workflows/create_archive.sh
@@ -17,14 +17,25 @@ set -o errexit -o nounset -o pipefail
# Set by GH actions, see
# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
-TAG=${GITHUB_REF_NAME}
+JSONNET_VERSION="$(grep -Ee '^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"[^"]+"\s*$' include/libjsonnet.h | sed -E -e 's/[^"]+"([^"]+)".*/\1/')"
+
+VERSION_SUFFIX=
+if [[ "${GITHUB_REF_TYPE}" != 'tag' || "${GITHUB_REF_NAME}" != "${JSONNET_VERSION}" ]]; then
+ >&2 echo 'WARNING: Jsonnet library version in header does not match release ref. Adding commit suffix.'
+ VERSION_SUFFIX="-${GITHUB_SHA:0:9}"
+fi
+
# A prefix is added to better match the GitHub generated archives.
-PREFIX="jsonnet-${TAG}"
-ARCHIVE="jsonnet-$TAG.tar.gz"
-git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE
-SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}')
+PREFIX="jsonnet-${JSONNET_VERSION}${VERSION_SUFFIX}"
+ARCHIVE="jsonnet-${JSONNET_VERSION}${VERSION_SUFFIX}.tar.gz"
+git archive --format=tar --prefix=${PREFIX}/ "${GITHUB_SHA}" | gzip > "$ARCHIVE"
+ARCHIVE_SHA=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}')
+
+echo "archive_sha256=${ARCHIVE_SHA}" >> "$GITHUB_OUTPUT"
+echo "jsonnet_version=${JSONNET_VERSION}" >> "$GITHUB_OUTPUT"
+echo "jsonnet_version_permanent=${JSONNET_VERSION}${VERSION_SUFFIX}" >> "$GITHUB_OUTPUT"
-cat > release_notes.txt << EOF
+cat > bazel_dep_release_notes.txt << EOF
### Importing Jsonnet in a project that uses Bazel
#### Using Bzlmod with Bazel 6
@@ -32,7 +43,7 @@ cat > release_notes.txt << EOF
Add to your \`MODULE.bazel\` file:
\`\`\`starlark
-bazel_dep(name = "jsonnet", version = "${TAG}")
+bazel_dep(name = "jsonnet", version = "${JSONNET_VERSION}")
\`\`\`
#### Using WORKSPACE
@@ -44,9 +55,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "jsonnet",
- sha256 = "${SHA}",
+ sha256 = "${ARCHIVE_SHA}",
strip_prefix = "${PREFIX}",
- url = "https://github.com/google/jsonnet/releases/download/${TAG}/jsonnet-${TAG}.tar.gz",
+ url = "https://github.com/google/jsonnet/releases/download/${JSONNET_VERSION}/${ARCHIVE}",
)
\`\`\`
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3b4d773be..ba3965fdb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,21 +15,33 @@
# Cut a release whenever a new tag is pushed to the repo.
name: Release
-on: [workflow_dispatch]
+on:
+ workflow_dispatch:
+ inputs:
+ prerelease:
+ description: If set, publish this as a release candidate / prerelease version.
+ type: boolean
+ required: true
jobs:
build:
- runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v2
- - name: Create release archive and notes
- run: .github/workflows/create_archive_and_notes.sh
+ uses: actions/checkout@v4
+ - name: Create release archive
+ id: create_archive
+ run: .github/workflows/create_archive.sh
- name: Release
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
- # Use GH feature to populate the changelog automatically
- generate_release_notes: true
- body_path: release_notes.txt
+ name: ${{ steps.create_archive.outputs.jsonnet_version }}
+ tag_name: ${{ steps.create_archive.outputs.jsonnet_version }}
+ prerelease: ${{ inputs.prerelease }}
+ draft: true
fail_on_unmatched_files: true
+ body_path: bazel_dep_release_notes.txt
+ append_body: true
files: jsonnet-*.tar.gz
From 162652994aefbebf4d4a763f88e553d483a015da Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 01:35:58 +0000
Subject: [PATCH 27/75] ci: add support for publishing to real (not test) PyPI
---
.github/workflows/publish-python.yml | 30 ++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml
index 6ded6307b..4f2b27a28 100644
--- a/.github/workflows/publish-python.yml
+++ b/.github/workflows/publish-python.yml
@@ -72,6 +72,36 @@ jobs:
path: ./wheelhouse/*.whl
if-no-files-found: error
+ upload_to_prod_pypi:
+ name: Upload packages to PyPI
+ needs: [build_sdist, build_wheels]
+ runs-on: ubuntu-24.04
+ if: "${{ inputs.upload_to_pypi && inputs.use_prod_pypi && startsWith(github.ref, 'refs/tags') }}"
+ permissions:
+ contents: read
+ id-token: write # Needed for PyPI Trusted Publishing
+ environment:
+ name: pypi
+ url: https://pypi.org/p/jsonnet
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ path: cibw-wheels
+ pattern: cibw-wheels-*
+ - uses: actions/download-artifact@v4
+ with:
+ path: sdist
+ name: sdist
+ - name: Flatten wheels to one directory
+ run: |
+ mkdir dist
+ find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';'
+ find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';'
+ - name: Publish to PyPI
+ uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
+ with:
+ print-hash: true
+
upload_to_test_pypi:
name: Upload packages to TEST PyPI
needs: [build_sdist, build_wheels]
From fcead4955b1c800204ec3fa0dd94307030b1f4b6 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 15:03:50 +0000
Subject: [PATCH 28/75] add explicit std::array<> template args to support
C++14 build
Not all the build systems make it easy to specify the C++ standard
to use (somehow, Bazel doesn't). C++14 doesn't support deducing
the template arguments for std::array. But it seems like there's
only one place that needs to be changed to make a C++14 build
work ok.
---
core/unicode_test.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/unicode_test.cpp b/core/unicode_test.cpp
index c34365ee9..c16bd2b5a 100644
--- a/core/unicode_test.cpp
+++ b/core/unicode_test.cpp
@@ -59,7 +59,7 @@ TEST(Unicode, TestUTF8)
TEST(Unicode, TestUTF8RejectBad)
{
- const auto test_cases = std::array{
+ const auto test_cases = std::array{
"\x80", // Continuation byte without leading byte
"\xa0", // Continuation byte without leading byte
"\xbf", // Continuation byte without leading byte
From 2eccc0ae7f799fab12bb6759c606116599c8190a Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 15:37:28 +0000
Subject: [PATCH 29/75] ci: allow building from prepare-release without adding
git hash suffix
Gradually trying to work out a reasonable release workflow. Something
like:
- Update version number etc on branch "prepare-release"
- Trigger release workflow: This generates a source archive and a draft
GitHub release object. The GitHub release has a propsed tag that
comes from the version in libjsonnet.h.
- Trigger Python publish workflow: This generates Python packages,
then waits for approval before publishing to PyPI.
- Fill in release notes in GitHub. Do any last minute checks.
Consider actually downloading the release artifacts and checking
them locally.
- Approve the Python publish workflow.
- Publish the GitHub release (which will create the version tag)
- Fast-forward master to the new version.
It's a little ugly, but the benefits here are:
- All the final build artifacts are generated before any publishing
so they can be checked (manually or otherwise).
- Release notes can be edited in GitHub.
- The tag is created at publish time.
---
.github/workflows/create_archive.sh | 36 ++++-------------------------
.github/workflows/release.yml | 2 --
2 files changed, 5 insertions(+), 33 deletions(-)
diff --git a/.github/workflows/create_archive.sh b/.github/workflows/create_archive.sh
index cb7a03992..5e0da8177 100755
--- a/.github/workflows/create_archive.sh
+++ b/.github/workflows/create_archive.sh
@@ -15,12 +15,14 @@
set -o errexit -o nounset -o pipefail
-# Set by GH actions, see
-# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
JSONNET_VERSION="$(grep -Ee '^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"[^"]+"\s*$' include/libjsonnet.h | sed -E -e 's/[^"]+"([^"]+)".*/\1/')"
+# GITHUB_REF is set by GH actions, see
+# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
+
VERSION_SUFFIX=
-if [[ "${GITHUB_REF_TYPE}" != 'tag' || "${GITHUB_REF_NAME}" != "${JSONNET_VERSION}" ]]; then
+if [[ ( "${GITHUB_REF_TYPE}" != 'branch' || "${GITHUB_REF_NAME}" != "prepare-release" ) &&
+ ( "${GITHUB_REF_TYPE}" != 'tag' || "${GITHUB_REF_NAME}" != "${JSONNET_VERSION}" ) ]]; then
>&2 echo 'WARNING: Jsonnet library version in header does not match release ref. Adding commit suffix.'
VERSION_SUFFIX="-${GITHUB_SHA:0:9}"
fi
@@ -34,31 +36,3 @@ ARCHIVE_SHA=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}')
echo "archive_sha256=${ARCHIVE_SHA}" >> "$GITHUB_OUTPUT"
echo "jsonnet_version=${JSONNET_VERSION}" >> "$GITHUB_OUTPUT"
echo "jsonnet_version_permanent=${JSONNET_VERSION}${VERSION_SUFFIX}" >> "$GITHUB_OUTPUT"
-
-cat > bazel_dep_release_notes.txt << EOF
-### Importing Jsonnet in a project that uses Bazel
-
-#### Using Bzlmod with Bazel 6
-
-Add to your \`MODULE.bazel\` file:
-
-\`\`\`starlark
-bazel_dep(name = "jsonnet", version = "${JSONNET_VERSION}")
-\`\`\`
-
-#### Using WORKSPACE
-
-Paste this snippet into your \`WORKSPACE\` file:
-
-\`\`\`starlark
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-http_archive(
- name = "jsonnet",
- sha256 = "${ARCHIVE_SHA}",
- strip_prefix = "${PREFIX}",
- url = "https://github.com/google/jsonnet/releases/download/${JSONNET_VERSION}/${ARCHIVE}",
-)
-\`\`\`
-
-EOF
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ba3965fdb..b8ba3fbf2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -42,6 +42,4 @@ jobs:
prerelease: ${{ inputs.prerelease }}
draft: true
fail_on_unmatched_files: true
- body_path: bazel_dep_release_notes.txt
- append_body: true
files: jsonnet-*.tar.gz
From 94a21f1eafd39376291e9dca7d15fe9cb4191b2d Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 15:45:31 +0000
Subject: [PATCH 30/75] ci: run build_and_test workflow on pushes to
prepare-release
---
.github/workflows/build_and_test.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 0518ded4b..9af18e3e0 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -8,6 +8,7 @@ on:
push:
branches:
- "master"
+ - "prepare-release"
tags:
- v*
From 061e7abef095c24dbf82bdec4d7a00ed3d3cf39d Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 16:26:27 +0000
Subject: [PATCH 31/75] ci: restructure publish-python to reduce duplication
Instead of having two mostly-identical jobs for publishing to PyPI
(one for prod one for test), have one job and take an environment
selector as a workflow input.
---
.github/workflows/publish-python.yml | 53 +++++++---------------------
1 file changed, 13 insertions(+), 40 deletions(-)
diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml
index 4f2b27a28..49bd7862b 100644
--- a/.github/workflows/publish-python.yml
+++ b/.github/workflows/publish-python.yml
@@ -12,10 +12,13 @@ on:
description: "Upload generate package files to PyPI"
required: true
type: boolean
- use_prod_pypi:
- description: "Use production PyPI not Test PyPI"
- required: false
- type: boolean
+ pypi_environment:
+ description: "Which PyPI instance to publish to"
+ required: true
+ type: choice
+ options:
+ - testpypi
+ - pypi
jobs:
build_sdist:
@@ -72,17 +75,17 @@ jobs:
path: ./wheelhouse/*.whl
if-no-files-found: error
- upload_to_prod_pypi:
- name: Upload packages to PyPI
+ upload_to_pypi:
+ name: "Upload packages to PyPI"
needs: [build_sdist, build_wheels]
runs-on: ubuntu-24.04
- if: "${{ inputs.upload_to_pypi && inputs.use_prod_pypi && startsWith(github.ref, 'refs/tags') }}"
+ if: "${{ inputs.upload_to_pypi }}"
permissions:
contents: read
id-token: write # Needed for PyPI Trusted Publishing
environment:
- name: pypi
- url: https://pypi.org/p/jsonnet
+ name: "${{ inputs.pypi_environment }}"
+ url: "${{ inputs.pypi_environment == 'pypi' && 'https://pypi.org/p/jsonnet' || 'https://test.pypi.org/p/jsonnet' }}"
steps:
- uses: actions/download-artifact@v4
with:
@@ -98,38 +101,8 @@ jobs:
find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';'
find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';'
- name: Publish to PyPI
- uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
- with:
- print-hash: true
-
- upload_to_test_pypi:
- name: Upload packages to TEST PyPI
- needs: [build_sdist, build_wheels]
- runs-on: ubuntu-24.04
- if: "${{ inputs.upload_to_pypi && !inputs.use_prod_pypi }}"
- permissions:
- contents: read
- id-token: write # Needed for PyPI Trusted Publishing
- environment:
- name: testpypi
- url: https://test.pypi.org/p/jsonnet
- steps:
- - uses: actions/download-artifact@v4
- with:
- path: cibw-wheels
- pattern: cibw-wheels-*
- - uses: actions/download-artifact@v4
- with:
- path: sdist
- name: sdist
- - name: Flatten wheels to one directory
- run: |
- mkdir dist
- find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';'
- find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';'
- - name: Publish to TEST PyPI
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
with:
verbose: true
print-hash: true
- repository-url: https://test.pypi.org/legacy/
+ repository-url: "${{ inputs.pypi_environment == 'testpypi' && 'https://test.pypi.org/legacy/' || '' }}"
From 2c3b51491a67ab7b24aecfc23fa3f73f68135129 Mon Sep 17 00:00:00 2001
From: John Bartholomew
Date: Sat, 22 Feb 2025 14:33:29 +0000
Subject: [PATCH 32/75] release: prepare to release v0.21.0-rc1
This should be followed shortly by v0.21.0, ideally with no further changes.
But since this is the first release for nearly 2 years it seems prudent to
be a little cautious and publish an actual release candidate first.
---
MODULE.bazel | 2 +-
cpp/CMakeLists.txt | 2 +-
doc/_stdlib_gen/stdlib-content.jsonnet | 26 +-
doc/ref/stdlib.html | 3749 ---------------------
include/libjsonnet.h | 6 +-
test_cmd/fmt_help.golden.stdout | 2 +-
test_cmd/fmt_help.golden.stdout.golang | 2 +-
test_cmd/fmt_no_args.golden.stderr | 2 +-
test_cmd/fmt_no_args.golden.stderr.golang | 2 +-
test_cmd/fmt_version1.golden.stdout | 2 +-
test_cmd/fmt_version2.golden.stdout | 2 +-
test_cmd/help.golden.stdout.cpp | 2 +-
test_cmd/help.golden.stdout.golang | 2 +-
test_cmd/no_args.golden.stderr.cpp | 2 +-
test_cmd/no_args.golden.stderr.golang | 2 +-
test_cmd/version1.golden.stdout.cpp | 2 +-
test_cmd/version1.golden.stdout.golang | 2 +-
test_cmd/version2.golden.stdout.cpp | 2 +-
test_cmd/version2.golden.stdout.golang | 2 +-
19 files changed, 32 insertions(+), 3781 deletions(-)
diff --git a/MODULE.bazel b/MODULE.bazel
index 7cdeaab8c..cef573755 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -1,6 +1,6 @@
module(
name = "jsonnet",
- version = "0.0.0",
+ version = "0.21.0-rc1",
)
bazel_dep(name = "googletest", version = "1.11.0", repo_name = "com_google_googletest")
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 500f65106..8e730fc3c 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -15,7 +15,7 @@ target_link_libraries(libjsonnet++ libjsonnet)
# CMake prepends CMAKE_SHARED_LIBRARY_PREFIX to shared libraries, so without
# this step the output would be |liblibjsonnet|.
set_target_properties(libjsonnet++ PROPERTIES OUTPUT_NAME jsonnet++
- VERSION "0.20.0"
+ VERSION "0.21.0-rc1"
SOVERSION "0"
PUBLIC_HEADER "${LIB_HEADER}")
install(TARGETS libjsonnet++
diff --git a/doc/_stdlib_gen/stdlib-content.jsonnet b/doc/_stdlib_gen/stdlib-content.jsonnet
index c2090e7a9..316d4d7b1 100644
--- a/doc/_stdlib_gen/stdlib-content.jsonnet
+++ b/doc/_stdlib_gen/stdlib-content.jsonnet
@@ -400,7 +400,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'trim',
params: ['str'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: |||
Returns a copy of string after eliminating leading and trailing whitespaces.
|||,
@@ -408,7 +408,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'equalsIgnoreCase',
params: ['str1', 'str2'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: |||
Returns true if the the given str1 is equal to str2 by doing case insensitive comparison, false otherwise.
|||,
@@ -1347,7 +1347,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'flattenDeepArray',
params: ['value'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: |||
Concatenate an array containing values and arrays into a single flattened array.
|||,
@@ -1433,7 +1433,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'minArray',
params: ['arr', 'keyF', 'onEmpty'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: html.paragraphs([
|||
Return the min of all element in arr.
@@ -1443,7 +1443,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'maxArray',
params: ['arr', 'keyF', 'onEmpty'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: html.paragraphs([
|||
Return the max of all element in arr.
@@ -1453,7 +1453,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'contains',
params: ['arr', 'elem'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: html.paragraphs([
|||
Return true if given elem is present in arr, false otherwise.
@@ -1473,7 +1473,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'remove',
params: ['arr', 'elem'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: html.paragraphs([
|||
Remove first occurrence of elem from arr.
@@ -1483,7 +1483,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'removeAt',
params: ['arr', 'idx'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: html.paragraphs([
|||
Remove element at idx index from arr.
@@ -1650,7 +1650,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'objectRemoveKey',
params: ['obj', 'key'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: |||
Returns a new object after removing the given key from object.
|||,
@@ -1717,7 +1717,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'sha1',
params: ['s'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: [
html.p({}, |||
Encodes the given value into an SHA1 string.
@@ -1730,7 +1730,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'sha256',
params: ['s'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: [
html.p({}, |||
Encodes the given value into an SHA256 string.
@@ -1743,7 +1743,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'sha512',
params: ['s'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: [
html.p({}, |||
Encodes the given value into an SHA512 string.
@@ -1756,7 +1756,7 @@ local exampleDocMultiline(mid, ex) =
{
name: 'sha3',
params: ['s'],
- availableSince: 'upcoming',
+ availableSince: '0.21.0',
description: [
html.p({}, |||
Encodes the given value into an SHA3 string.
diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html
index dae270f3c..e69de29bb 100644
--- a/doc/ref/stdlib.html
+++ b/doc/ref/stdlib.html
@@ -1,3749 +0,0 @@
----
-# AUTOGENERATED FILE. DO NOT EDIT BY HAND!
-layout: default
-title: Standard Library
----
-
-
-
-
-
- Standard Library
-
-
-
-
-
-
-
-
-
-
- This page describes the functions available in Jsonnet's standard library, i.e. the object
- implicitly bound to the std variable. Some of the standard library functions
- can be implemented in Jsonnet. Their code can be found in the std.jsonnet file.
- The behavior of some of the other functions, i.e. the ones that expose extra functionality
- not otherwise available to programmers, is described formally in the specification.
-
-
- The standard library is implicitly added to all Jsonnet programs by enclosing them in a
- local construct. For example, if the program given by the user is {x: "foo"},
- then the actual code executed would be local std = { ... }; {x: "foo"}. The
- functions in the standard library are all hidden fields of the std object.
-
-
- Note: Some of these functions marked available since v0.10.0 were actually available earlier.
-
-
-
-
-
-
-
-
-
-
- External Variables
-
-
-
-
-
-
-
-
-
-
- std.extVar(x)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- If an external variable with the given name was defined, return its value. Otherwise, raise an error.
-
-
-
-
-
-
-
-
-
-
-
-
- Types and Reflection
-
-
-
-
-
-
-
-
-
-
- std.thisFile
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Note that this is a field. It contains the current Jsonnet filename as a string.
-
-
-
-
-
-
-
-
-
-
-
- std.type(x)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Return a string that indicates the type of the value. The possible return values are:
- "array", "boolean", "function", "null", "number", "object", and "string".
-
-
- The following functions are also available and return a boolean:
- std.isArray(v), std.isBoolean(v), std.isFunction(v),
- std.isNumber(v), std.isObject(v), and
- std.isString(v).
-
-
-
-
-
-
-
-
-
-
-
- std.length(x)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Depending on the type of the value given, either returns the number of elements in the
- array, the number of codepoints in the string, the number of parameters in the function, or
- the number of fields in the object. Raises an error if given a primitive value, i.e.
- null, true or false.
-
-
-
-
-
-
-
-
-
-
-
- std.prune(a)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Recursively remove all "empty" members of a. "Empty" is defined as zero
- length `arrays`, zero length `objects`, or `null` values.
- The argument a may have any type.
-
-
-
-
-
-
-
-
-
-
-
-
- Mathematical Utilities
-
-
-
-
-
-
-
-
-
- The following mathematical functions are available:
-
-
-
std.abs(n)
-
std.sign(n)
-
std.max(a, b)
-
std.min(a, b)
-
std.pow(x, n)
-
std.exp(x)
-
std.log(x)
-
std.log2(x)
-
std.log10(x)
-
std.exponent(x)
-
std.mantissa(x)
-
std.floor(x)
-
std.ceil(x)
-
std.sqrt(x)
-
std.sin(x)
-
std.cos(x)
-
std.tan(x)
-
std.asin(x)
-
std.acos(x)
-
std.atan(x)
-
std.atan2(y, x)
-
std.deg2rad(x)
-
std.rad2deg(x)
-
std.hypot(a, b)
-
std.round(x)
-
std.isEven(x)
-
std.isOdd(x)
-
std.isInteger(x)
-
std.isDecimal(x)
-
-
- The constant std.pi is also available.
-
-
- The function std.mod(a, b) is what the % operator is desugared to. It performs
- modulo arithmetic if the left hand side is a number, or if the left hand side is a string,
- it does Python-style string formatting with std.format().
-
-
- The functions std.isEven(x) and std.isOdd(x) use integral part of a
- floating number to test for even or odd.
-
-
-
-
-
-
-
-
-
-
- std.clamp(x, minVal, maxVal)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.15.0.
-
-
-
- Clamp a value to fit within the range [minVal, maxVal].
- Equivalent to std.max(minVal, std.min(x, maxVal)).
-
-
- Example: std.clamp(-3, 0, 5) yields 0.
-
-
- Example: std.clamp(4, 0, 5) yields 4.
-
-
- Example: std.clamp(7, 0, 5) yields 5.
-
-
-
-
-
-
-
-
-
-
-
- Assertions and Debugging
-
-
-
-
-
-
-
-
-
-
- std.assertEqual(a, b)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Ensure that a == b. Returns true or throws an error message.
-
-
-
-
-
-
-
-
-
-
-
-
- String Manipulation
-
-
-
-
-
-
-
-
-
-
- std.toString(a)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Convert the given argument to a string.
-
-
-
-
-
-
-
-
-
-
-
- std.codepoint(str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns the positive integer representing the unicode codepoint of the character in the
- given single-character string. This function is the inverse of std.char(n).
-
-
-
-
-
-
-
-
-
-
-
- std.char(n)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns a string of length one whose only unicode codepoint has integer id n.
- This function is the inverse of std.codepoint(str).
-
-
-
-
-
-
-
-
-
-
-
- std.substr(str, from, len)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns a string that is the part of s that starts at offset from
- and is len codepoints long. If the string s is shorter than
- from+len, the suffix starting at position from will be returned.
-
-
-
-
-
-
-
-
-
-
-
- std.findSubstr(pat, str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns an array that contains the indexes of all occurrences of pat in
- str.
-
-
-
-
-
-
-
-
-
-
-
- std.startsWith(a, b)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns whether the string a is prefixed by the string b.
-
-
-
-
-
-
-
-
-
-
-
- std.endsWith(a, b)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns whether the string a is suffixed by the string b.
-
-
-
-
-
-
-
-
-
-
-
- std.stripChars(str, chars)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.15.0.
-
-
-
- Removes characters chars from the beginning and from the end of str.
-
-
- Example: std.stripChars(" test test test ", " ") yields "test test test".
-
- As std.split(str, c) but will stop after maxsplits splits, thereby the largest
- array it will return has length maxsplits + 1. A limit of -1 means unlimited.
-
-
- Note: Versions up to and including 0.18.0 require c to be a single character.
-
- Format the string str using the values in vals. The values can be
- an array, an object, or in other cases are treated as if they were provided in a singleton
- array. The string formatting follows the same rules as
- Python. The % operator can be used as a shorthand for this function.
-
- Example: "Hello %s, age %d" % ["Foo", 25] yields "Hello Foo, age 25".
-
-
- Example: "Hello %(name)s, age %(age)d" % {age: 25, name: "Foo"} yields "Hello Foo, age 25".
-
-
-
-
-
-
-
-
-
-
- std.escapeStringBash(str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Wrap str in single quotes, and escape any single quotes within str
- by changing them to a sequence '"'"'. This allows injection of arbitrary strings
- as arguments of commands in bash scripts.
-
-
-
-
-
-
-
-
-
-
-
- std.escapeStringDollars(str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Convert $ to $$ in str. This allows injection of arbitrary strings into
- systems that use $ for string interpolation (like Terraform).
-
-
-
-
-
-
-
-
-
-
-
- std.escapeStringJson(str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Convert str to allow it to be embedded in a JSON representation, within a
- string. This adds quotes, escapes backslashes, and escapes unprintable characters.
-
- Parses a YAML string. This is provided as a "best-effort" mechanism and should not be relied on to provide
- a fully standards compliant YAML parser. YAML is a superset of JSON, consequently "downcasting" or
- manifestation of YAML into JSON or Jsonnet values will only succeed when using the subset of YAML that is
- compatible with JSON. The parser does not support YAML documents with scalar values at the root. The
- root node of a YAML document must start with either a YAML sequence or map to be successfully parsed.
-
- Encode a string using UTF8. Returns an array of numbers
- representing bytes.
-
-
-
-
-
-
-
-
-
-
-
- std.decodeUTF8(arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.13.0.
-
-
-
- Decode an array of numbers representing bytes using UTF8.
- Returns a string.
-
-
-
-
-
-
-
-
-
-
-
-
- Manifestation
-
-
-
-
-
-
-
-
-
-
- std.manifestIni(ini)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Convert the given structure to a string in INI format. This
- allows using Jsonnet's
- object model to build a configuration to be consumed by an application expecting an INI
- file. The data is in the form of a set of sections, each containing a key/value mapping.
- These examples should make it clear:
-
- Convert the given value to a JSON-like form that is compatible with Python. The chief
- differences are True / False / None instead of true / false / null.
-
- Convert the given object to a JSON-like form that is compatible with Python. The key
- difference to std.manifestPython is that the top level is represented as a list
- of Python global variables.
-
- Convert the given object to a JSON form. indent is a string containing
- one or more whitespaces that are used for indentation. newline is
- by default \n and is inserted where a newline would normally be used
- to break long lines. key_val_sep is used to separate the key and value
- of an object field:
-
- Convert the given value to a YAML form. Note that std.manifestJson could also
- be used for this purpose, because any JSON is also valid YAML. But this function will
- produce more canonical-looking YAML.
-
- JsonML is designed to preserve "mixed-mode content" (i.e., textual data outside of or next
- to elements). This includes the whitespace needed to avoid having all the XML on one line,
- which is meaningful in XML. In order to have whitespace in the XML output, it must be
- present in the JsonML input:
-
key1 = "value"
-key2 = 1
-
-[section]
- a = 1
- b = "str"
- c = false
- d = [
- 1,
- "s",
- [ 2, 3 ]
- ]
-
- [section.subsection]
- k = "v"
-
-[[sectionArray]]
- k = "v1"
- v = 123
-
-[[sectionArray]]
- c = "value2"
- k = "v2"
-
-
-
-
-
-
-
-
-
-
- Arrays
-
-
-
-
-
-
-
-
-
-
- std.makeArray(sz, func)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Create a new array of sz elements by calling func(i) to initialize
- each element. Func is expected to be a function that takes a single parameter, the index of
- the element it should initialize.
-
- Returns whether x occurs in arr.
- Argument arr may be an array or a string.
-
-
-
-
-
-
-
-
-
-
-
- std.count(arr, x)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Return the number of times that x occurs in arr.
-
-
-
-
-
-
-
-
-
-
-
- std.find(value, arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns an array that contains the indexes of all occurrences of value in
- arr.
-
-
-
-
-
-
-
-
-
-
-
- std.map(func, arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Apply the given function to every element of the array to form a new array.
-
-
-
-
-
-
-
-
-
-
-
- std.mapWithIndex(func, arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Similar to map above, but it also passes to the function the element's
- index in the array. The function func is expected to take the index as the
- first parameter and the element as the second.
-
-
-
-
-
-
-
-
-
-
-
- std.filterMap(filter_func, map_func, arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- It first filters, then maps the given array, using the two functions provided.
-
-
-
-
-
-
-
-
-
-
-
- std.flatMap(func, arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Apply the given function to every element of arr to form a new array then flatten the result.
- The argument arr must be an array or a string. If arr is an array, function func must return an array.
- If arr is a string, function func must return an string.
-
-
- The std.flatMap function can be thought of as a generalized std.map,
- with each element mapped to 0, 1 or more elements.
-
- Return a new array containing all the elements of arr for which the
- func function returns true.
-
-
-
-
-
-
-
-
-
-
-
- std.foldl(func, arr, init)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Classic foldl function. Calls the function on the result of the previous function call and
- each array element, or init in the case of the initial element. Traverses the
- array from left to right.
-
-
-
-
-
-
-
-
-
-
-
- std.foldr(func, arr, init)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Classic foldr function. Calls the function on the result of the previous function call and
- each array element, or init in the case of the initial element. Traverses the
- array from right to left.
-
-
-
-
-
-
-
-
-
-
-
- std.range(from, to)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Return an array of ascending numbers between the two limits, inclusively.
-
-
-
-
-
-
-
-
-
-
-
- std.repeat(what, count)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.15.0.
-
-
-
- Repeats an array or a string what a number of times specified by an integer count.
-
- If sep is a string, then arr must be an array of strings, in which
- case they are concatenated with sep used as a delimiter. If sep
- is an array, then arr must be an array of arrays, in which case the arrays are
- concatenated in the same way, to produce a single array.
-
- Concatenate an array of strings into a text file with newline characters after each string.
- This is suitable for constructing bash scripts and the like.
-
-
-
-
-
-
-
-
-
-
-
- std.flattenArrays(arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Concatenate an array of arrays into a single array.
-
- Optional argument keyF is a single argument function used to extract comparison key from each array element.
- Default value is identity function keyF=function(x) x.
-
-
-
-
-
-
-
-
-
-
-
- std.uniq(arr, keyF=id)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Removes successive duplicates. When given a sorted array, removes all duplicates.
-
-
- Optional argument keyF is a single argument function used to extract comparison key from each array element.
- Default value is identity function keyF=function(x) x.
-
-
-
-
-
-
-
-
-
-
-
- std.all(arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.19.0.
-
-
-
- Return true if all elements of arr is true, false otherwise. all([]) evaluates to true.
-
-
- It's an error if 1) arr is not an array, or 2) arr contains non-boolean values.
-
-
-
-
-
-
-
-
-
-
-
- std.any(arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.19.0.
-
-
-
- Return true if any element of arr is true, false otherwise. any([]) evaluates to false.
-
-
- It's an error if 1) arr is not an array, or 2) arr contains non-boolean values.
-
-
-
-
-
-
-
-
-
-
-
- std.sum(arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.20.0.
-
-
-
- Return sum of all element in arr.
-
-
-
-
-
-
-
-
-
-
-
- std.minArray(arr, keyF, onEmpty)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Return the min of all element in arr.
-
-
-
-
-
-
-
-
-
-
-
- std.maxArray(arr, keyF, onEmpty)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Return the max of all element in arr.
-
-
-
-
-
-
-
-
-
-
-
- std.contains(arr, elem)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Return true if given elem is present in arr, false otherwise.
-
-
-
-
-
-
-
-
-
-
-
- std.avg(arr)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.20.0.
-
-
-
- Return average of all element in arr.
-
-
-
-
-
-
-
-
-
-
-
- std.remove(arr, elem)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Remove first occurrence of elem from arr.
-
-
-
-
-
-
-
-
-
-
-
- std.removeAt(arr, idx)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Remove element at idx index from arr.
-
-
-
-
-
-
-
-
-
-
-
-
- Sets
-
-
-
-
-
-
-
-
-
- Sets are represented as ordered arrays without duplicates.
-
-
- Note that the std.set* functions rely on the uniqueness and ordering
- on arrays passed to them to work. This can be guaranteed by using std.set(arr).
- If that is not the case, the functions will quietly return non-meaningful results.
-
-
- All set.set* functions accept keyF function of one argument, which can be
- used to extract key to use from each element. All Set operations then use extracted key for the purpose
- of identifying uniqueness. Default value is identity function local id = function(x) x.
-
-
-
-
-
-
-
-
-
-
- std.set(arr, keyF=id)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Shortcut for std.uniq(std.sort(arr)).
-
-
-
-
-
-
-
-
-
-
-
- std.setInter(a, b, keyF=id)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Set intersection operation (values in both a and b).
-
-
-
-
-
-
-
-
-
-
-
- std.setUnion(a, b, keyF=id)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Set union operation (values in any of a or b). Note that + on sets will simply
- concatenate
- the arrays, possibly forming an array that is not a set (due to not being ordered without
- duplicates).
-
- Set difference operation (values in a but not b).
-
-
-
-
-
-
-
-
-
-
-
- std.setMember(x, arr, keyF=id)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns true if x is a member of array, otherwise false.
-
-
-
-
-
-
-
-
-
-
-
-
- Objects
-
-
-
-
-
-
-
-
-
-
- std.get(o, f, default=null, inc_hidden=true)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.18.0.
-
-
-
- Returns the object's field if it exists or default value otherwise.
- inc_hidden controls whether to include hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectHas(o, f)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns true if the given object has the field (given as a string), otherwise
- false. Raises an error if the arguments are not object and string
- respectively. Returns false if the field is hidden.
-
-
-
-
-
-
-
-
-
-
-
- std.objectFields(o)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Returns an array of strings, each element being a field from the given object. Does not include
- hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectValues(o)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.17.0.
-
-
-
- Returns an array of the values in the given object. Does not include hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectKeysValues(o)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.20.0.
-
-
-
- Returns an array of objects from the given object, each object having two fields:
- key (string) and value (object). Does not include hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectHasAll(o, f)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- As std.objectHas but also includes hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectFieldsAll(o)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- As std.objectFields but also includes hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectValuesAll(o)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.17.0.
-
-
-
- As std.objectValues but also includes hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectKeysValuesAll(o)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.20.0.
-
-
-
- As std.objectKeysValues but also includes hidden fields.
-
-
-
-
-
-
-
-
-
-
-
- std.objectRemoveKey(obj, key)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Returns a new object after removing the given key from object.
-
-
-
-
-
-
-
-
-
-
-
- std.mapWithKey(func, obj)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Apply the given function to all fields of the given object, also passing
- the field name. The function func is expected to take the
- field name as the first parameter and the field value as the second.
-
-
-
-
-
-
-
-
-
-
-
-
- Encoding
-
-
-
-
-
-
-
-
-
-
- std.base64(input)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Encodes the given value into a base64 string. The encoding sequence is A-Za-z0-9+/ with
- =
- to pad the output to a multiple of 4 characters. The value can be a string or an array of
- numbers, but the codepoints / numbers must be in the 0 to 255 range. The resulting string
- has no line breaks.
-
-
-
-
-
-
-
-
-
-
-
- std.base64DecodeBytes(str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Decodes the given base64 string into an array of bytes (number values). Currently assumes
- the input string has no linebreaks and is padded to a multiple of 4 (with the = character).
- In other words, it consumes the output of std.base64().
-
-
-
-
-
-
-
-
-
-
-
- std.base64Decode(str)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Deprecated, use std.base64DecodeBytes and decode the string explicitly (e.g. with std.decodeUTF8) instead.
-
-
- Behaves like std.base64DecodeBytes() except returns a naively encoded string instead of an array of bytes.
-
-
-
-
-
-
-
-
-
-
-
- std.md5(s)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Encodes the given value into an MD5 string.
-
-
-
-
-
-
-
-
-
-
-
- std.sha1(s)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Encodes the given value into an SHA1 string.
-
-
- This function is only available in Go version of jsonnet.
-
-
-
-
-
-
-
-
-
-
-
- std.sha256(s)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Encodes the given value into an SHA256 string.
-
-
- This function is only available in Go version of jsonnet.
-
-
-
-
-
-
-
-
-
-
-
- std.sha512(s)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Encodes the given value into an SHA512 string.
-
-
- This function is only available in Go version of jsonnet.
-
-
-
-
-
-
-
-
-
-
-
- std.sha3(s)
-
-
-
-
-
-
-
-
-
-
- Available in upcoming release.
-
-
-
- Encodes the given value into an SHA3 string.
-
-
- This function is only available in Go version of jsonnet.
-
-
-
-
-
-
-
-
-
-
-
-
- Booleans
-
-
-
-
-
-
-
-
-
-
- std.xor(x, y)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.20.0.
-
-
-
- Returns the xor of the two given booleans.
-
-
-
-
-
-
-
-
-
-
-
- std.xnor(x, y)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.20.0.
-
-
-
- Returns the xnor of the two given booleans.
-
-
-
-
-
-
-
-
-
-
-
-
- JSON Merge Patch
-
-
-
-
-
-
-
-
-
-
- std.mergePatch(target, patch)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.10.0.
-
-
-
- Applies patch to target
- according to RFC7396
-
-
-
-
-
-
-
-
-
-
-
-
- Debugging
-
-
-
-
-
-
-
-
-
-
- std.trace(str, rest)
-
-
-
-
-
-
-
-
-
-
- Available since version 0.11.0.
-
-
-
- Outputs the given string str to stderr and
- returns rest as the result.
-
-
- Example:
-
-
-
local conditionalReturn(cond, in1, in2) =
- if (cond) then
- std.trace('cond is true returning '
- + std.toString(in1), in1)
- else
- std.trace('cond is false returning '
- + std.toString(in2), in2);
-
-{
- a: conditionalReturn(true, { b: true }, { c: false }),
-}
-
-
diff --git a/include/libjsonnet.h b/include/libjsonnet.h
index e86f1662f..15d6f822c 100644
--- a/include/libjsonnet.h
+++ b/include/libjsonnet.h
@@ -24,14 +24,14 @@ limitations under the License.
* of using the library.
*/
-/** The version string of th Jsonnet interpreter.
+/** The version string of the Jsonnet interpreter.
*
* This is currently grepped out of this file by setup.py, Makefile, and CMakeLists.txt so be aware
* of that when making changes.
*
- * If this isn't the sae as jsonnet_version() then you've got a mismatched binary / header.
+ * If this isn't the same as jsonnet_version() then you've got a mismatched binary / header.
*/
-#define LIB_JSONNET_VERSION "v0.20.0"
+#define LIB_JSONNET_VERSION "v0.21.0-rc1"
/** Return the version string of the Jsonnet interpreter. Conforms to semantic versioning
* https://semver.org/ If this does not match LIB_JSONNET_VERSION then there is a mismatch between
diff --git a/test_cmd/fmt_help.golden.stdout b/test_cmd/fmt_help.golden.stdout
index 652f276bc..33b1ca597 100644
--- a/test_cmd/fmt_help.golden.stdout
+++ b/test_cmd/fmt_help.golden.stdout
@@ -1,4 +1,4 @@
-Jsonnet reformatter v0.20.0
+Jsonnet reformatter v0.21.0-rc1
jsonnetfmt {