Merge pull request #54 from Manwi23:jsonnet

PiperOrigin-RevId: 335017461
Change-Id: I583444057bb4d8c8c33694fbba4d3717bb22101a
This commit is contained in:
Copybara-Service 2020-10-02 06:47:41 -07:00
commit 569c7d84b0
31 changed files with 2315 additions and 0 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,9 @@
[submodule "oss-internship-2020/sapi_lodepng/lodepng"]
path = oss-internship-2020/lodepng/lodepng
url = https://github.com/lvandeve/lodepng
[submodule "oss-internship-2020/jsonnet/jsonnet"]
path = oss-internship-2020/jsonnet/jsonnet
url = https://github.com/google/jsonnet.git
[submodule "oss-internship-2020/openjpeg/openjpeg"]
path = oss-internship-2020/openjpeg/openjpeg
url = https://github.com/uclouvain/openjpeg.git

View File

@ -0,0 +1,58 @@
# Copyright 2020 Google LLC
#
# 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.
cmake_minimum_required(VERSION 3.10)
project(jsonnet-sapi C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_subdirectory(jsonnet)
add_subdirectory(examples)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "")
set(SAPI_ENABLE_TESTS OFF CACHE BOOL "")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
add_sapi_library(jsonnet_sapi
FUNCTIONS c_jsonnet_evaluate_snippet
c_jsonnet_make
c_jsonnet_destroy
c_read_input
c_write_output_file
c_jsonnet_realloc
c_jsonnet_evaluate_snippet_multi
c_write_multi_output_files
c_write_output_stream
c_jsonnet_evaluate_snippet_stream
c_jsonnet_fmt_snippet
INPUTS jsonnet_helper.h
LIBRARY jsonnet_helper
LIBRARY_NAME Jsonnet
NAMESPACE ""
)
target_include_directories(jsonnet_sapi INTERFACE
"${PROJECT_BINARY_DIR}"
)
target_link_libraries(jsonnet_sapi PUBLIC jsonnet_helper)
add_subdirectory(tests)

View File

@ -0,0 +1,84 @@
# Jsonnet Sandboxed API
This library provides sandboxed version of the
[Jsonnet](https://github.com/google/jsonnet) library.
## Examples
The `examples/` directory contains code to produce three command-line tools --
`jsonnet_sandboxed`, `jsonnet_yaml_stream_sandboxed` and
`jsonnet_multiple_files_sandboxed` to evaluate jsonnet code. The first one
enables the user to evaluate jsonnet code held in one file and writing to one
output file. The second evaluates one jsonnet file into one file, which can be
interepreted as YAML stream. The third one is for evaluating one jsonnet file
into multiple output files. All three tools are based on what can be found
[here](https://github.com/google/jsonnet/blob/master/cmd/jsonnet.cpp).
Apart from these, there is also a file producing `jsonnet_formatter_sandboxed`
executable. It is based on a tool found from
[here](https://github.com/google/jsonnet/blob/master/cmd/jsonnetfmt.cpp). It is
a jsonnet code formatter -- it changes poorly written jsonnet files into their
canonical form.
## Build
To build these examples, after cloning the whole Sandbox API project, you also
need to run
```
git submodule update --init --recursive
```
anywhere in the project tree in order to clone the `jsonnet` submodule.
Then in the `sandboxed-api/oss-internship-2020/jsonnet` run
```
mkdir build && cd build
cmake -G Ninja
ninja
```
To run `jsonnet_sandboxed` (or `jsonnet_yaml_stream_sandboxed` or
`jsonnet_formatter_sandboxed` in a similar way):
```
cd examples
./jsonnet_sandboxed \
absolute/path/to/the/input_file.jsonnet \
absolute/path/to/the/output_file
```
To run `jsonnet_mutiple_files_sandboxed`:
```
cd examples
./jsonnet_mutiple_files_sandboxed \
absolute/path/to/the/input_file.jsonnet \
absolute/path/to/the/output_directory
```
All three tools support evaluating one input file (possibly relying on multiple
other files, e.x. by jsonnet `import` command; the files must be held in the
same directory as input file) into one or more output files. Example jsonnet
codes to evaluate in a one-in-one-out manner can be found
[here](https://github.com/google/jsonnet/tree/master/examples). Example code
producing multiple output files or YAML stream files can be found in the
`examples/jsonnet_codes` directory (along with some other examples copied with
minimal changes from the library files), in files called
`multiple_files_example.jsonnet` and `yaml_stream_example.jsonnet`,
respectively. In the `examples/jsonnet_codes_expected_output` directory one can
found outputs the mentioned above files' evaluation should produce.
The formatter reads one input file and produces one output file as a result.
Example code for this tool can also be found in `examples/jsonnet_codes`
directory, in a file called `formatter_example.jsonnet`.
## Testing
A few tests prepared with a use of
[Google Test](https://github.com/google/googletest) framework can be found in
the `tests/` directory. To run them type:
```
cd tests
./tests
```

View File

@ -0,0 +1,75 @@
# Copyright 2020 Google LLC
#
# 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.
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gen_files")
file(COPY "${PROJECT_SOURCE_DIR}/jsonnet/cmd/jsonnet.cpp" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
file(COPY "${PROJECT_SOURCE_DIR}/jsonnet.patch" DESTINATION "${PROJECT_BINARY_DIR}/gen_files/")
add_custom_command(
OUTPUT ${PROJECT_BINARY_DIR}/gen_files/write_helper.cc
COMMAND cd ${PROJECT_BINARY_DIR}/gen_files && patch < ${PROJECT_SOURCE_DIR}/jsonnet.patch > /dev/null
COMMAND mv ${PROJECT_BINARY_DIR}/gen_files/jsonnet.cpp ${PROJECT_BINARY_DIR}/gen_files/write_helper.cc
)
list(APPEND JSONNET_SAPI_HEADERS
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/headers
${PROJECT_BINARY_DIR}/gen_files
)
add_library(jsonnet_helper STATIC
${PROJECT_SOURCE_DIR}/jsonnet_helper.cc
${PROJECT_SOURCE_DIR}/jsonnet_helper.h
${PROJECT_SOURCE_DIR}/jsonnet/cmd/utils.h
${PROJECT_SOURCE_DIR}/jsonnet/cmd/utils.cpp
${PROJECT_BINARY_DIR}/gen_files/write_helper.cc
)
target_include_directories(jsonnet_helper PUBLIC
${JSONNET_SAPI_HEADERS}
)
target_link_libraries(jsonnet_helper
libjsonnet_for_binaries
)
foreach(exe base multiple_files yaml_stream formatter)
add_executable(jsonnet_${exe}_sandboxed
jsonnet_${exe}_example.cc
)
target_link_libraries(jsonnet_${exe}_sandboxed PRIVATE
libjsonnet
jsonnet_helper
jsonnet_sapi
sandbox2::file_base
sandbox2::fileops
sapi::sapi
)
target_include_directories(jsonnet_${exe}_sandboxed PUBLIC
${JSONNET_SAPI_HEADERS}
)
endforeach()
add_executable(jsonnet_base_transacted
jsonnet_base_transaction.cc
)
target_link_libraries(jsonnet_base_transacted PRIVATE
libjsonnet
jsonnet_helper
jsonnet_sapi
sapi::sapi
)

View File

@ -0,0 +1,95 @@
// Copyright 2020 Google LLC
//
// 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 <cstdlib>
#include <iostream>
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetBaseSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue()) << "Jsonnet code evaluation failed: "
<< error.GetValue() << "\n"
<< "Make sure all files used by your jsonnet file "
"are in the same directory as your file.";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,88 @@
// Copyright 2020 Google LLC
//
// 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 "jsonnet_base_transaction.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
absl::Status JsonnetTransaction::Main() {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
JsonnetApi api(sandbox());
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
TRANSACTION_FAIL_IF_NOT(error.GetValue() == 0,
"Jsonnet code evaluation failed.");
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
TRANSACTION_FAIL_IF_NOT(success, "Writing to output file failed.");
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
JsonnetTransaction jsonnet_transaction(in_file, out_file);
auto result = jsonnet_transaction.Run();
LOG(INFO) << "Transaction result: " << result.message();
CHECK(result.ok());
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,50 @@
/*
Copyright 2015 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.
*/
{
concat_array: [1, 2, 3] + [4],
concat_string: '123' + 4,
equality1: 1 == '1',
equality2: [{}, { x: 3 - 1 }]
== [{}, { x: 2 }],
ex1: 1 + 2 * 3 / (4 + 5),
// Bitwise operations first cast to int.
ex2: self.ex1 | 3,
// Modulo operator.
ex3: self.ex1 % 2,
// Boolean logic
ex4: (4 > 3) && (1 <= 3) || false,
// Mixing objects together
obj: { a: 1, b: 2 } + { b: 3, c: 4 },
// Test if a field is in an object
obj_member: 'foo' in { foo: 1 },
// String formatting
str1: 'The value of self.ex2 is '
+ self.ex2 + '.',
str2: 'The value of self.ex2 is %g.'
% self.ex2,
str3: 'ex1=%0.2f, ex2=%0.2f'
% [self.ex1, self.ex2],
// By passing self, we allow ex1 and ex2 to
// be extracted internally.
str4: 'ex1=%(ex1)0.2f, ex2=%(ex2)0.2f'
% self,
// Do textual templating of entire files:
str5: |||
ex1=%(ex1)0.2f
ex2=%(ex2)0.2f
||| % self,
}

View File

@ -0,0 +1,32 @@
// Copyright 2020 Google LLC
//
// 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.
// This is a poorly written jsonnet file. Given to the formatter executable will be changed into a canonical jsonnet file form.
local b = import "somefile.libsonnet"; # comment
local a = import "differentfile.libsonnet"; // another comment in different style
local SomeStuff = {bar: "foo"};
local funtion_to_do_addition(x,y)=x+y;
{
"this": ((3)) ,
"that that":
funtion_to_do_addition(4,2),
arrArr: [[
1, 2, 5
],
3, 10, 19
]
} + SomeStuff

View File

@ -0,0 +1,30 @@
/*
Copyright 2015 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.
*/
local martinis = import 'martinis.libsonnet';
{
'Vodka Martini': martinis['Vodka Martini'],
Manhattan: {
ingredients: [
{ kind: 'Rye', qty: 2.5 },
{ kind: 'Sweet Red Vermouth', qty: 1 },
{ kind: 'Angostura', qty: 'dash' },
],
garnish: importstr 'garnish.txt',
served: 'Straight Up',
},
}

View File

@ -0,0 +1,26 @@
// Copyright 2020 Google LLC
//
// 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.
// This is a jsonnet code which evaluates to mutliple output files.
{
"first_file.json": {
name: 'This is the first file created by the multiple-files example code.',
caption: 'The other one\'s name is -> ' + $["second_file.json"].name,
},
"second_file.json": {
name: 'And that is the other one.',
caption: 'If it was the first one, variable name would hold what\'s in <first_name> variable.',
first_name: $["first_file.json"].name,
},
}

View File

@ -0,0 +1,29 @@
/*
Copyright 2015 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.
*/
local utils = import 'utils.libsonnet';
{
Negroni: {
// Divide 3oz among the 3 ingredients.
ingredients: utils.equal_parts(3, [
'Farmers Gin',
'Sweet Red Vermouth',
'Campari',
]),
garnish: 'Orange Peel',
served: 'On The Rocks',
},
}

View File

@ -0,0 +1,26 @@
/*
Copyright 2015 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.
*/
{
equal_parts(size, ingredients)::
// Define a function-scoped variable.
local qty = size / std.length(ingredients);
// Return an array.
[
{ kind: i, qty: qty }
for i in ingredients
],
}

View File

@ -0,0 +1,26 @@
// Copyright 2020 Google LLC
//
// 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.
// This is a jsonnet code which evaluates to json file, which can be interpreted as YAML stream.
local
first_object = {
name: 'First object\'s name.',
age: 'Just created!',
},
second_object = {
name: 'Hi, my name is <second_object>.',
sibling: first_object.name
};
[first_object, second_object]

View File

@ -0,0 +1,26 @@
{
"concat_array": [
1,
2,
3,
4
],
"concat_string": "1234",
"equality1": false,
"equality2": true,
"ex1": 1.6666666666666665,
"ex2": 3,
"ex3": 1.6666666666666665,
"ex4": true,
"obj": {
"a": 1,
"b": 3,
"c": 4
},
"obj_member": true,
"str1": "The value of self.ex2 is 3.",
"str2": "The value of self.ex2 is 3.",
"str3": "ex1=1.67, ex2=3.00",
"str4": "ex1=1.67, ex2=3.00",
"str5": "ex1=1.67\nex2=3.00\n"
}

View File

@ -0,0 +1,4 @@
{
"caption": "The other one's name is -> And that is the other one.",
"name": "This is the first file created by the multiple-files example code."
}

View File

@ -0,0 +1,20 @@
{
"Negroni": {
"garnish": "Orange Peel",
"ingredients": [
{
"kind": "Farmers Gin",
"qty": 1
},
{
"kind": "Sweet Red Vermouth",
"qty": 1
},
{
"kind": "Campari",
"qty": 1
}
],
"served": "On The Rocks"
}
}

View File

@ -0,0 +1,5 @@
{
"caption": "If it was the first one, variable name would hold what's in <first_name> variable.",
"first_name": "This is the first file created by the multiple-files example code.",
"name": "And that is the other one."
}

View File

@ -0,0 +1,11 @@
---
{
"age": "Just created!",
"name": "First object's name."
}
---
{
"name": "Hi, my name is <second_object>.",
"sibling": "First object's name."
}
...

View File

@ -0,0 +1,128 @@
// Copyright 2020 Google LLC
//
// 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 <libgen.h>
#include <syscall.h>
#include <cstdlib>
#include <iostream>
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
class JsonnetSapiSandbox : public JsonnetSandbox {
public:
explicit JsonnetSapiSandbox(std::string in_file, std::string out_file)
: in_file_(std::move(in_file)), out_file_(std::move(out_file)) {}
// We need only the input file here, not the whole input directory
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
})
.AddDirectoryAt(dirname(&out_file_[0]), "/output", /*is_ro=*/false)
.AddFile(in_file_, true)
.BuildOrDie();
}
private:
std::string in_file_;
std::string out_file_;
};
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetSapiSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_fmt_snippet(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue()) << "Jsonnet code evaluation failed: "
<< error.GetValue() << "\n";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,132 @@
// Copyright 2020 Google LLC
//
// 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 <libgen.h>
#include <syscall.h>
#include <cstdlib>
#include <iostream>
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
class JsonnetSapiSandbox : public JsonnetSandbox {
public:
explicit JsonnetSapiSandbox(std::string in_file, std::string out_directory)
: in_file_(std::move(in_file)),
out_directory_(std::move(out_directory)) {}
// We need a slightly different policy than the default one
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
})
.AddDirectoryAt(sandbox2::file::CleanPath(&out_directory_[0]),
"/output",
/*is_ro=*/false)
.AddDirectoryAt(dirname(&in_file_[0]), "/input", true)
.BuildOrDie();
}
private:
std::string in_file_;
std::string out_directory_;
};
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetSapiSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet_multi(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue()) << "Jsonnet code evaluation failed: "
<< error.GetValue() << "\n"
<< "Make sure all files used by your jsonnet file "
"are in the same directory as your file.";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,95 @@
// Copyright 2020 Google LLC
//
// 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 <cstdlib>
#include <iostream>
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
#include "sandboxed_api/sandbox2/util/fileops.h"
#include "sandboxed_api/sandbox2/util/path.h"
absl::Status JsonnetMain(std::string in_file, std::string out_file) {
using sandbox2::file::JoinPath;
using sandbox2::file_util::fileops::Basename;
// Initialize sandbox.
JsonnetBaseSandbox sandbox(in_file, out_file);
SAPI_RETURN_IF_ERROR(sandbox.Init())
JsonnetApi api(&sandbox);
// Initialize library's main structure.
SAPI_ASSIGN_OR_RETURN(JsonnetVm * jsonnet_vm, api.c_jsonnet_make());
sapi::v::RemotePtr vm_pointer(jsonnet_vm);
// Read input file.
std::string in_file_in_sandboxee(JoinPath("/input", Basename(in_file)));
sapi::v::ConstCStr in_file_var(in_file_in_sandboxee.c_str());
SAPI_ASSIGN_OR_RETURN(char* input,
api.c_read_input(false, in_file_var.PtrBefore()));
// Process jsonnet data.
sapi::v::RemotePtr input_pointer(input);
sapi::v::Int error;
SAPI_ASSIGN_OR_RETURN(char* output, api.c_jsonnet_evaluate_snippet_stream(
&vm_pointer, in_file_var.PtrBefore(),
&input_pointer, error.PtrAfter()));
CHECK(!error.GetValue())
<< "Jsonnet code evaluation failed: " << error.GetValue() << "\n"
<< "Make sure all files used by your jsonnet file are in the same "
"directory as your file.";
// Write data to file.
std::string out_file_in_sandboxee(JoinPath("/output", Basename(out_file)));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
sapi::v::RemotePtr output_pointer(output);
SAPI_ASSIGN_OR_RETURN(
bool success,
api.c_write_output_file(&output_pointer, out_file_var.PtrBefore()));
CHECK(success) << "Writing to output file failed: " << success;
// Clean up.
SAPI_ASSIGN_OR_RETURN(char* result,
api.c_jsonnet_realloc(&vm_pointer, &output_pointer, 0));
SAPI_RETURN_IF_ERROR(api.c_jsonnet_destroy(&vm_pointer));
SAPI_RETURN_IF_ERROR(api.c_free_input(&input_pointer));
return absl::OkStatus();
}
int main(int argc, char* argv[]) {
using sandbox2::file_util::fileops::Basename;
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (!(argc == 3)) {
std::cerr << "Usage:\n"
<< Basename(argv[0])
<< " /absolute/path/to/INPUT.jsonnet /absolute/path/to/OUTPUT\n";
return EXIT_FAILURE;
}
std::string in_file(argv[1]);
std::string out_file(argv[2]);
absl::Status status = JsonnetMain(in_file, out_file);
if (!status.ok()) {
LOG(ERROR) << "Failed: " << status.ToString();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,55 @@
// Copyright 2020 Google LLC
//
// 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.
#ifndef JSONNET_BASE_SANDBOX_H_
#define JSONNET_BASE_SANDBOX_H_
#include <libgen.h>
#include <syscall.h>
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/transaction.h"
#include "sandboxed_api/vars.h"
class JsonnetBaseSandbox : public JsonnetSandbox {
public:
explicit JsonnetBaseSandbox(std::string in_file, std::string out_file)
: in_file_(in_file), out_file_(out_file) {}
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder *) override {
return sandbox2::PolicyBuilder()
.AllowStaticStartup()
.AllowOpen()
.AllowRead()
.AllowWrite()
.AllowStat()
.AllowSystemMalloc()
.AllowExit()
.AllowSyscalls({
__NR_futex,
__NR_close,
})
.AddDirectoryAt(dirname(&out_file_[0]), "/output", /*is_ro=*/false)
.AddDirectoryAt(dirname(&in_file_[0]), "/input", true)
.BuildOrDie();
}
private:
std::string in_file_;
std::string out_file_;
};
#endif // JSONNET_BASE_SANDBOX_H_

View File

@ -0,0 +1,38 @@
// Copyright 2020 Google LLC
//
// 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.
#ifndef JSNONNET_BASE_TRANSACTION_H_
#define JSNONNET_BASE_TRANSACTION_H_
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
class JsonnetTransaction : public sapi::Transaction {
public:
JsonnetTransaction(std::string in_file, std::string out_file)
: sapi::Transaction(
std::make_unique<JsonnetBaseSandbox>(in_file, out_file)),
in_file_(in_file),
out_file_(out_file) {
sapi::Transaction::set_retry_count(0); // Try once, no retries
sapi::Transaction::SetTimeLimit(0); // Infinite time limit
}
private:
std::string in_file_;
std::string out_file_;
absl::Status Main() override;
};
#endif // JSNONNET_BASE_TRANSACTION_H_

View File

@ -0,0 +1,57 @@
// Copyright 2020 Google LLC
//
// 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.
#ifndef JSONNET_TESTS_H_
#define JSONNET_TESTS_H_
#include <unistd.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <streambuf>
#include <string>
#include "jsonnet_base_sandbox.h" // NOLINT(build/include)
#include "jsonnet_sapi.sapi.h" // NOLINT(build/include)
#include "gtest/gtest.h"
#include "sandboxed_api/util/flag.h"
#include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/util/status_matchers.h"
class JsonnetTestHelper {
protected:
enum Evaluation { kBase, kMultipleFiles, kYamlStream };
void TestSetUp();
void TestTearDown();
void ReadInput(const char* filename);
void EvaluateJsonnetCode(Evaluation type, bool expected_correct);
void WriteOutput(const char* filename_or_directory, Evaluation type);
std::string ReadOutput(const char* filename);
std::unique_ptr<JsonnetBaseSandbox> sandbox_;
std::unique_ptr<JsonnetApi> api_;
std::unique_ptr<sapi::v::RemotePtr> input_;
std::unique_ptr<sapi::v::RemotePtr> output_;
std::unique_ptr<sapi::v::RemotePtr> vm_;
std::string input_filename_in_sandboxee_;
bool jsonnet_vm_was_used_;
bool input_was_read_;
};
#endif // JSONNET_TESTS_H_

@ -0,0 +1 @@
Subproject commit 3e25595d5c4acd32a1c3951a57471986b90d3bad

View File

@ -0,0 +1,664 @@
--- jsonnet.cpp 2020-09-09 12:15:33.687539042 +0000
+++ write_helper.cpp 2020-09-25 15:38:37.317147682 +0000
@@ -14,559 +14,125 @@
limitations under the License.
*/
-#include <cassert>
-#include <cstdlib>
-#include <cstring>
+// We need two functions defined in jsonnet.cpp file, used for writing output
+// (multiple files and yaml streams) -- with minor changes (e.x. return type).
-#include <exception>
#include <fstream>
#include <iostream>
-#include <list>
#include <map>
-#include <sstream>
-#include <string>
#include <vector>
-#include "utils.h"
+#include "jsonnet_helper.h" // NOLINT(build/include)
-extern "C" {
-#include <libjsonnet.h>
-}
-
-#ifdef _WIN32
-const char PATH_SEP = ';';
-#else
-const char PATH_SEP = ':';
-#endif
-
-void version(std::ostream &o)
-{
- o << "Jsonnet commandline interpreter " << jsonnet_version() << std::endl;
-}
-
-void usage(std::ostream &o)
-{
- version(o);
- o << "\n";
- o << "jsonnet {<option>} <filename>\n";
- o << "\n";
- o << "Available options:\n";
- o << " -h / --help This message\n";
- o << " -e / --exec Treat filename as code\n";
- o << " -J / --jpath <dir> Specify an additional library search dir (right-most wins)\n";
- o << " -o / --output-file <file> Write to the output file rather than stdout\n";
- o << " -m / --multi <dir> Write multiple files to the directory, list files on stdout\n";
- o << " -y / --yaml-stream Write output as a YAML stream of JSON documents\n";
- o << " -S / --string Expect a string, manifest as plain text\n";
- o << " -s / --max-stack <n> Number of allowed stack frames\n";
- o << " -t / --max-trace <n> Max length of stack trace before cropping\n";
- o << " --gc-min-objects <n> Do not run garbage collector until this many\n";
- o << " --gc-growth-trigger <n> Run garbage collector after this amount of object growth\n";
- o << " --version Print version\n";
- o << "Available options for specifying values of 'external' variables:\n";
- o << "Provide the value as a string:\n";
- o << " -V / --ext-str <var>[=<val>] If <val> is omitted, get from environment var <var>\n";
- o << " --ext-str-file <var>=<file> Read the string from the file\n";
- o << "Provide a value as Jsonnet code:\n";
- o << " --ext-code <var>[=<code>] If <code> is omitted, get from environment var <var>\n";
- o << " --ext-code-file <var>=<file> Read the code from the file\n";
- o << "Available options for specifying values of 'top-level arguments':\n";
- o << "Provide the value as a string:\n";
- o << " -A / --tla-str <var>[=<val>] If <val> is omitted, get from environment var <var>\n";
- o << " --tla-str-file <var>=<file> Read the string from the file\n";
- o << "Provide a value as Jsonnet code:\n";
- o << " --tla-code <var>[=<code>] If <code> is omitted, get from environment var <var>\n";
- o << " --tla-code-file <var>=<file> Read the code from the file\n";
- o << "Environment variables:\n";
- o << "JSONNET_PATH is a colon (semicolon on Windows) separated list of directories added\n";
- o << "in reverse order before the paths specified by --jpath (i.e. left-most wins)\n";
- o << "E.g. JSONNET_PATH=a:b jsonnet -J c -J d is equivalent to:\n";
- o << "JSONNET_PATH=d:c:a:b jsonnet\n";
- o << "jsonnet -J b -J a -J c -J d\n";
- o << "\n";
- o << "In all cases:\n";
- o << "<filename> can be - (stdin)\n";
- o << "Multichar options are expanded e.g. -abc becomes -a -b -c.\n";
- o << "The -- option suppresses option processing for subsequent arguments.\n";
- o << "Note that since filenames and jsonnet programs can begin with -, it is advised to\n";
- o << "use -- if the argument is unknown, e.g. jsonnet -- \"$FILENAME\".";
- o << std::endl;
-}
-
-/** Class for representing configuration read from command line flags. */
-struct JsonnetConfig {
- std::vector<std::string> inputFiles;
- std::string outputFile;
- bool filenameIsCode;
-
- // EVAL flags
- bool evalMulti;
- bool evalStream;
- std::string evalMultiOutputDir;
-
- JsonnetConfig()
- : filenameIsCode(false),
- evalMulti(false),
- evalStream(false)
- {
- }
-};
-
-bool get_var_val(const std::string &var_val, std::string &var, std::string &val)
-{
- size_t eq_pos = var_val.find_first_of('=', 0);
- if (eq_pos == std::string::npos) {
- var = var_val;
- const char *val_cstr = ::getenv(var.c_str());
- if (val_cstr == nullptr) {
- std::cerr << "ERROR: environment variable " << var << " was undefined." << std::endl;
- return false;
- }
- val = val_cstr;
- } else {
- var = var_val.substr(0, eq_pos);
- val = var_val.substr(eq_pos + 1, std::string::npos);
- }
- return true;
-}
-
-bool get_var_file(const std::string &var_file, const std::string &imp, std::string &var, std::string &val)
-{
- size_t eq_pos = var_file.find_first_of('=', 0);
- if (eq_pos == std::string::npos) {
- std::cerr << "ERROR: argument not in form <var>=<file> \"" << var_file << "\"."
- << std::endl;
- return false;
- }
- var = var_file.substr(0, eq_pos);
- const std::string path = var_file.substr(eq_pos + 1, std::string::npos);
-
- size_t b, e;
- val.erase().append(imp).append(" @'");
- // duplicate all the single quotes in @path to make a quoted string
- for (b = 0; (e = path.find("'", b)) != std::string::npos; b = e + 1) {
- val.append(path.substr(b, e - b + 1)).push_back('\'');
+/** Writes output files for multiple file output */
+bool write_multi_output_files(char *output, const std::string &output_dir, bool show_output_file_names) {
+ // If multiple file output is used, then iterate over each string from
+ // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
+ // construct pairs of filename and content, and write each output file.
+ std::map<std::string, std::string> r;
+ for (const char *c = output; *c != '\0';) {
+ const char *filename = c;
+ const char *c2 = c;
+ while (*c2 != '\0') ++c2;
+ ++c2;
+ const char *json = c2;
+ while (*c2 != '\0') ++c2;
+ ++c2;
+ c = c2;
+ r[filename] = json;
+ }
+
+ std::ostream *o;
+ std::ofstream f;
+
+ o = &std::cout;
+
+ for (const auto &pair : r) {
+ const std::string &new_content = pair.second;
+ const std::string &filename = output_dir + pair.first;
+ if (show_output_file_names) {
+ (*o) << filename << std::endl;
}
- val.append(path.substr(b)).push_back('\'');
-
- return true;
-}
-
-enum ArgStatus {
- ARG_CONTINUE,
- ARG_SUCCESS,
- ARG_FAILURE,
-};
-
-/** Parse the command line arguments, configuring the Jsonnet VM context and
- * populating the JsonnetConfig.
- */
-static ArgStatus process_args(int argc, const char **argv, JsonnetConfig *config, JsonnetVm *vm)
-{
- auto args = simplify_args(argc, argv);
- std::vector<std::string> remaining_args;
-
- unsigned i = 0;
-
- for (; i < args.size(); ++i) {
- const std::string &arg = args[i];
- if (arg == "-h" || arg == "--help") {
- usage(std::cout);
- return ARG_SUCCESS;
- } else if (arg == "-v" || arg == "--version") {
- version(std::cout);
- return ARG_SUCCESS;
- } else if (arg == "-e" || arg == "--exec") {
- config->filenameIsCode = true;
- } else if (arg == "-o" || arg == "--output-file") {
- std::string output_file = next_arg(i, args);
- if (output_file.length() == 0) {
- std::cerr << "ERROR: -o argument was empty string" << std::endl;
- return ARG_FAILURE;
- }
- config->outputFile = output_file;
- } else if (arg == "--") {
- // All subsequent args are not options.
- while ((++i) < args.size())
- remaining_args.push_back(args[i]);
- break;
- } else if (arg == "-s" || arg == "--max-stack") {
- long l = strtol_check(next_arg(i, args));
- if (l < 1) {
- std::cerr << "ERROR: invalid --max-stack value: " << l << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_max_stack(vm, l);
- } else if (arg == "-J" || arg == "--jpath") {
- std::string dir = next_arg(i, args);
- if (dir.length() == 0) {
- std::cerr << "ERROR: -J argument was empty string" << std::endl;
- return ARG_FAILURE;
- }
- if (dir[dir.length() - 1] != '/') {
- dir += '/';
- }
- jsonnet_jpath_add(vm, dir.c_str());
- } else if (arg == "-V" || arg == "--ext-str") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_var(vm, var.c_str(), val.c_str());
- } else if (arg == "-E" || arg == "--var" || arg == "--env") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval -E, --var and --env are deprecated,"
- << " please use -V or --ext-str." << std::endl;
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_var(vm, var.c_str(), val.c_str());
- } else if (arg == "--ext-str-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "importstr", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "-F" || arg == "--file") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval -F and --file are deprecated,"
- << " please use --ext-str-file." << std::endl;
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "importstr", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--ext-code") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--code-var" || arg == "--code-env") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval --code-var and --code-env are deprecated,"
- << " please use --ext-code." << std::endl;
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--ext-code-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "import", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--code-file") {
- // TODO(dcunnin): Delete this in a future release.
- std::cerr << "WARNING: jsonnet eval --code-file is deprecated,"
- << " please use --ext-code-file." << std::endl;
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "import", var, val))
- return ARG_FAILURE;
- jsonnet_ext_code(vm, var.c_str(), val.c_str());
- } else if (arg == "-A" || arg == "--tla-str") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_tla_var(vm, var.c_str(), val.c_str());
- } else if (arg == "--tla-str-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "importstr", var, val))
- return ARG_FAILURE;
- jsonnet_tla_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--tla-code") {
- std::string var, val;
- if (!get_var_val(next_arg(i, args), var, val))
- return ARG_FAILURE;
- jsonnet_tla_code(vm, var.c_str(), val.c_str());
- } else if (arg == "--tla-code-file") {
- std::string var, val;
- if (!get_var_file(next_arg(i, args), "import", var, val))
- return ARG_FAILURE;
- jsonnet_tla_code(vm, var.c_str(), val.c_str());
-
- } else if (arg == "--gc-min-objects") {
- long l = strtol_check(next_arg(i, args));
- if (l < 0) {
- std::cerr << "ERROR: invalid --gc-min-objects value: " << l << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_gc_min_objects(vm, l);
- } else if (arg == "-t" || arg == "--max-trace") {
- long l = strtol_check(next_arg(i, args));
- if (l < 0) {
- std::cerr << "ERROR: invalid --max-trace value: " << l << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_max_trace(vm, l);
- } else if (arg == "--gc-growth-trigger") {
- std::string num = next_arg(i, args);
- char *ep;
- double v = std::strtod(num.c_str(), &ep);
- if (*ep != '\0' || num.length() == 0) {
- std::cerr << "ERROR: invalid number \"" << num << "\"" << std::endl;
- return ARG_FAILURE;
- }
- if (v < 0) {
- std::cerr << "ERROR: invalid --gc-growth-trigger \"" << num << "\""
- << std::endl;
- return ARG_FAILURE;
- }
- jsonnet_gc_growth_trigger(vm, v);
- } else if (arg == "-m" || arg == "--multi") {
- config->evalMulti = true;
- std::string output_dir = next_arg(i, args);
- if (output_dir.length() == 0) {
- std::cerr << "ERROR: -m argument was empty string" << std::endl;
- return ARG_FAILURE;
- }
- if (output_dir[output_dir.length() - 1] != '/') {
- output_dir += '/';
- }
- config->evalMultiOutputDir = output_dir;
- } else if (arg == "-y" || arg == "--yaml-stream") {
- config->evalStream = true;
- } else if (arg == "-S" || arg == "--string") {
- jsonnet_string_output(vm, 1);
- } else if (arg.length() > 1 && arg[0] == '-') {
- std::cerr << "ERROR: unrecognized argument: " << arg << std::endl;
- return ARG_FAILURE;
- } else {
- remaining_args.push_back(args[i]);
+ {
+ std::ifstream exists(filename.c_str());
+ if (exists.good()) {
+ std::string existing_content;
+ existing_content.assign(std::istreambuf_iterator<char>(exists),
+ std::istreambuf_iterator<char>());
+ if (existing_content == new_content) {
+ // Do not bump the timestamp on the file if its content is
+ // the same. This may trigger other tools (e.g. make) to do
+ // unnecessary work.
+ continue;
}
+ }
}
-
- const char *want = config->filenameIsCode ? "code" : "filename";
- if (remaining_args.size() == 0) {
- std::cerr << "ERROR: must give " << want << "\n" << std::endl;
- usage(std::cerr);
- return ARG_FAILURE;
- }
-
- if (remaining_args.size() > 1) {
- std::string filename = remaining_args[0];
- std::cerr << "ERROR: only one " << want << " is allowed\n" << std::endl;
- return ARG_FAILURE;
- }
- config->inputFiles = remaining_args;
- return ARG_CONTINUE;
-}
-
-/** Writes output files for multiple file output */
-static bool write_multi_output_files(JsonnetVm *vm, char *output, const std::string &output_dir,
- const std::string &output_file)
-{
- // If multiple file output is used, then iterate over each string from
- // the sequence of strings returned by jsonnet_evaluate_snippet_multi,
- // construct pairs of filename and content, and write each output file.
- std::map<std::string, std::string> r;
- for (const char *c = output; *c != '\0';) {
- const char *filename = c;
- const char *c2 = c;
- while (*c2 != '\0')
- ++c2;
- ++c2;
- const char *json = c2;
- while (*c2 != '\0')
- ++c2;
- ++c2;
- c = c2;
- r[filename] = json;
- }
- jsonnet_realloc(vm, output, 0);
-
- std::ostream *o;
std::ofstream f;
-
- if (output_file.empty()) {
- o = &std::cout;
- } else {
- f.open(output_file.c_str());
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
- o = &f;
+ f.open(filename.c_str());
+ if (!f.good()) {
+ std::string msg = "Opening output file: " + filename;
+ perror(msg.c_str());
+ return false;
+ }
+ f << new_content;
+ f.close();
+ if (!f.good()) {
+ std::string msg = "Writing to output file: " + filename;
+ perror(msg.c_str());
+ return false;
}
+ }
- for (const auto &pair : r) {
- const std::string &new_content = pair.second;
- const std::string &filename = output_dir + pair.first;
- (*o) << filename << std::endl;
- {
- std::ifstream exists(filename.c_str());
- if (exists.good()) {
- std::string existing_content;
- existing_content.assign(std::istreambuf_iterator<char>(exists),
- std::istreambuf_iterator<char>());
- if (existing_content == new_content) {
- // Do not bump the timestamp on the file if its content is
- // the same. This may trigger other tools (e.g. make) to do
- // unnecessary work.
- continue;
- }
- }
- }
- std::ofstream f;
- f.open(filename.c_str());
- if (!f.good()) {
- std::string msg = "Opening output file: " + filename;
- perror(msg.c_str());
- return false;
- }
- f << new_content;
- f.close();
- if (!f.good()) {
- std::string msg = "Writing to output file: " + filename;
- perror(msg.c_str());
- return false;
- }
- }
+ std::cout.flush();
- if (output_file.empty()) {
- std::cout.flush();
- } else {
- f.close();
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
- }
- return true;
+ return true;
}
/** Writes output files for YAML stream output */
-static bool write_output_stream(JsonnetVm *vm, char *output, const std::string &output_file)
-{
- std::ostream *o;
- std::ofstream f;
-
- if (output_file.empty()) {
- o = &std::cout;
- } else {
- f.open(output_file.c_str());
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
- o = &f;
- }
-
- // If YAML stream output is used, then iterate over each string from
- // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
- // and add the --- and ... as defined by the YAML spec.
- std::vector<std::string> r;
- for (const char *c = output; *c != '\0';) {
- const char *json = c;
- while (*c != '\0')
- ++c;
- ++c;
- r.emplace_back(json);
- }
- jsonnet_realloc(vm, output, 0);
- for (const auto &str : r) {
- (*o) << "---\n";
- (*o) << str;
- }
- if (r.size() > 0)
- (*o) << "...\n";
- o->flush();
-
- if (output_file.empty()) {
- std::cout.flush();
- } else {
- f.close();
- if (!f.good()) {
- std::string msg = "Writing to output file: " + output_file;
- perror(msg.c_str());
- return false;
- }
+bool write_output_stream(char *output, const std::string &output_file) {
+ std::ostream *o;
+ std::ofstream f;
+
+ if (output_file.empty()) {
+ o = &std::cout;
+ } else {
+ f.open(output_file.c_str());
+ if (!f.good()) {
+ std::string msg = "Writing to output file: " + output_file;
+ perror(msg.c_str());
+ return false;
+ }
+ o = &f;
+ }
+
+ // If YAML stream output is used, then iterate over each string from
+ // the sequence of strings returned by jsonnet_evaluate_snippet_stream,
+ // and add the --- and ... as defined by the YAML spec.
+ std::vector<std::string> r;
+ for (const char *c = output; *c != '\0';) {
+ const char *json = c;
+ while (*c != '\0') ++c;
+ ++c;
+ r.emplace_back(json);
+ }
+
+ for (const auto &str : r) {
+ (*o) << "---\n";
+ (*o) << str;
+ }
+ if (r.size() > 0) (*o) << "...\n";
+ o->flush();
+
+ if (output_file.empty()) {
+ std::cout.flush();
+ } else {
+ f.close();
+ if (!f.good()) {
+ std::string msg = "Writing to output file: " + output_file;
+ perror(msg.c_str());
+ return false;
}
+ }
- return true;
-}
-
-int main(int argc, const char **argv)
-{
- try {
- JsonnetVm *vm = jsonnet_make();
- JsonnetConfig config;
- if (const char *jsonnet_path_env = getenv("JSONNET_PATH")) {
- std::list<std::string> jpath;
- std::istringstream iss(jsonnet_path_env);
- std::string path;
- while (std::getline(iss, path, PATH_SEP)) {
- jpath.push_front(path);
- }
- for (const std::string &path : jpath) {
- jsonnet_jpath_add(vm, path.c_str());
- }
- }
- ArgStatus arg_status = process_args(argc, argv, &config, vm);
- if (arg_status != ARG_CONTINUE) {
- jsonnet_destroy(vm);
- return arg_status == ARG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
- }
-
- // Evaluate input Jsonnet and handle any errors from Jsonnet VM.
- int error;
- char *output;
- assert(config.inputFiles.size() == 1);
-
- // Read input file.
- std::string input;
- if (!read_input(config.filenameIsCode, &config.inputFiles[0], &input)) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
-
- if (config.evalMulti) {
- output = jsonnet_evaluate_snippet_multi(
- vm, config.inputFiles[0].c_str(), input.c_str(), &error);
- } else if (config.evalStream) {
- output = jsonnet_evaluate_snippet_stream(
- vm, config.inputFiles[0].c_str(), input.c_str(), &error);
- } else {
- output = jsonnet_evaluate_snippet(
- vm, config.inputFiles[0].c_str(), input.c_str(), &error);
- }
-
- if (error) {
- std::cerr << output;
- jsonnet_realloc(vm, output, 0);
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
-
- // Write output JSON.
- if (config.evalMulti) {
- if (!write_multi_output_files(
- vm, output, config.evalMultiOutputDir, config.outputFile)) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
- } else if (config.evalStream) {
- if (!write_output_stream(vm, output, config.outputFile)) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
- } else {
- bool successful = write_output_file(output, config.outputFile);
- jsonnet_realloc(vm, output, 0);
- if (!successful) {
- jsonnet_destroy(vm);
- return EXIT_FAILURE;
- }
- }
-
- jsonnet_destroy(vm);
- return EXIT_SUCCESS;
-
- } catch (const std::bad_alloc &) {
- // Avoid further allocation attempts
- fputs("Internal out-of-memory error (please report this)\n", stderr);
- } catch (const std::exception &e) {
- std::cerr << "Internal error (please report this): " << e.what() << std::endl;
- } catch (...) {
- std::cerr << "An unknown exception occurred (please report this)." << std::endl;
- }
- return EXIT_FAILURE;
+ return true;
}

View File

@ -0,0 +1,76 @@
// Copyright 2020 Google LLC
//
// 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 "jsonnet_helper.h" // NOLINT(build/include)
#include <cstring>
struct JsonnetVm* c_jsonnet_make(void) {
return jsonnet_make();
}
void c_jsonnet_destroy(struct JsonnetVm* vm) { return jsonnet_destroy(vm); }
char* c_jsonnet_evaluate_snippet(struct JsonnetVm* vm, const char* filename,
char* snippet, int* error) {
return jsonnet_evaluate_snippet(vm, filename, snippet, error);
}
char* c_jsonnet_evaluate_snippet_multi(struct JsonnetVm* vm,
const char* filename,
const char* snippet, int* error) {
return jsonnet_evaluate_snippet_multi(vm, filename, snippet, error);
}
char* c_jsonnet_evaluate_snippet_stream(struct JsonnetVm* vm,
const char* filename,
const char* snippet, int* error) {
return jsonnet_evaluate_snippet_stream(vm, filename, snippet, error);
}
char* c_read_input(bool filename_is_code, const char* filename) {
std::string s_filename(filename);
std::string s_input;
bool check = read_input(filename_is_code, &s_filename, &s_input);
char* c_input = strdup(s_input.c_str());
if (check) return c_input;
return nullptr;
}
void c_free_input(char* input) { free(input); }
bool c_write_output_file(const char* output, const char* output_file) {
std::string s_output_file(output_file);
return write_output_file(output, s_output_file);
}
bool c_write_multi_output_files(char* output, char* output_dir,
bool show_output_file_names) {
std::string s_output_dir(output_dir);
return write_multi_output_files(output, s_output_dir, show_output_file_names);
}
bool c_write_output_stream(char* output, char* output_file) {
std::string s_output_file(output_file);
return write_output_stream(output, s_output_file);
}
char* c_jsonnet_realloc(struct JsonnetVm* vm, char* str, size_t sz) {
return jsonnet_realloc(vm, str, sz);
}
char* c_jsonnet_fmt_snippet(struct JsonnetVm* vm, const char* filename,
const char* snippet, int* error) {
return jsonnet_fmt_snippet(vm, filename, snippet, error);
}

View File

@ -0,0 +1,66 @@
// Copyright 2020 Google LLC
//
// 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.
#ifndef JSONNET_HELPER_H_
#define JSONNET_HELPER_H_
extern "C" {
#include <libjsonnet.h> // NOLINT(build/include)
#include <libjsonnet_fmt.h> // NOLINT(build/include)
}
#include "jsonnet/cmd/utils.h" // NOLINT(build/include)
extern "C" struct JsonnetVm* c_jsonnet_make(void);
extern "C" void c_jsonnet_destroy(struct JsonnetVm* vm);
extern "C" char* c_jsonnet_evaluate_snippet(struct JsonnetVm* vm,
const char* filename, char* snippet,
int* error);
extern "C" char* c_jsonnet_evaluate_snippet_multi(struct JsonnetVm* vm,
const char* filename,
const char* snippet,
int* error);
extern "C" char* c_jsonnet_evaluate_snippet_stream(struct JsonnetVm* vm,
const char* filename,
const char* snippet,
int* error);
extern "C" char* c_read_input(bool filename_is_code, const char* filename);
extern "C" void c_free_input(char* input);
extern "C" bool c_write_output_file(const char* output,
const char* output_file);
extern "C" char* c_jsonnet_realloc(struct JsonnetVm* vm, char* str, size_t sz);
extern "C" bool c_write_multi_output_files(char* output, char* output_dir,
bool show_output_file_names);
bool write_multi_output_files(char* output, const std::string& output_dir,
bool show_output_file_names);
extern "C" bool c_write_output_stream(char* output, char* output_file);
bool write_output_stream(char* output, const std::string& output_file);
extern "C" char* c_jsonnet_fmt_snippet(struct JsonnetVm* vm,
const char* filename,
const char* snippet, int* error);
#endif // JSONNET_HELPER_H_

View File

@ -0,0 +1,42 @@
# Copyright 2020 Google LLC
#
# 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(GoogleTest)
# We need to prepare convenient directories so the tests will be able to access them
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/tests/tests_input)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/tests/tests_output)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/tests/tests_expected_output)
add_custom_target(test_preparation ALL
COMMAND cp ${PROJECT_SOURCE_DIR}/examples/jsonnet_codes/* ${PROJECT_BINARY_DIR}/tests/tests_input
COMMAND cp ${PROJECT_SOURCE_DIR}/examples/jsonnet_codes_expected_output/* ${PROJECT_BINARY_DIR}/tests/tests_expected_output
)
add_executable(tests
${PROJECT_SOURCE_DIR}/headers/jsonnet_tests.h
jsonnet_tests.cc
jsonnet_tests_utils.cc
)
target_include_directories(tests PUBLIC
${PROJECT_SOURCE_DIR}/headers
)
target_link_libraries(tests
jsonnet_sapi sapi::sapi
gtest gmock gtest_main
)
gtest_discover_tests(tests)

View File

@ -0,0 +1,113 @@
// Copyright 2020 Google LLC
//
// 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 "jsonnet_tests.h" // NOLINT(build/include)
namespace {
class JsonnetTest : public JsonnetTestHelper, public testing::Test {
protected:
void SetUp() override { TestSetUp(); }
void TearDown() override { TestTearDown(); }
};
// Basic test
TEST_F(JsonnetTest, SetUp_TearDown) {
ASSERT_FALSE(jsonnet_vm_was_used_);
ASSERT_FALSE(input_was_read_);
}
// One file evaluation to one file
TEST_F(JsonnetTest, One_file_no_dependencies) {
constexpr char kInputFile[] = "arith.jsonnet";
constexpr char kOutputFile[] = "arith_output";
constexpr char kOutputToRead[] = "tests_output/arith_output";
constexpr char kOutputToExpect[] = "tests_expected_output/arith.golden";
Read_input(kInputFile);
EvaluateJsonnetCode(kBase, true);
WriteOutput(kOutputFile, kBase);
std::string produced_output = ReadOutput(kOutputToRead);
std::string expected_output = ReadOutput(kOutputToExpect);
ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
}
// One file evaluating to one file, dependent on some other files
TEST_F(JsonnetTest, One_file_some_dependencies) {
constexpr char kInputFile[] = "negroni.jsonnet";
constexpr char kOutputFile[] = "negroni_output";
constexpr char kOutputToRead[] = "tests_output/negroni_output";
constexpr char kOutputToExpect[] = "tests_expected_output/negroni.golden";
ReadInput(kInputFile);
EvaluateJsonnetCode(kBase, true);
WriteOutput(kOutputFile, kBase);
const std::string produced_output = Read_output(kOutputToRead);
const std::string expected_output = Read_output(kOutputToExpect);
ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
}
// One file evaluating to two files
TEST_F(JsonnetTest, Multiple_files) {
constexpr char kInputFile[] = "multiple_files_example.jsonnet";
constexpr char kOutputFile[] = "";
constexpr char kOutputToRead1[] = "tests_output/first_file.json";
constexpr char kOutputToRead2[] = "tests_output/second_file.json";
constexpr char kOutputToExpect1[] = "tests_expected_output/first_file.json";
constexpr char kOutputToExpect2[] = "tests_expected_output/second_file.json";
ReadInput(kInputFile);
EvaluateJsonnetCode(kMultipleFiles, true);
WriteOutput(kOutputFile, kMultipleFiles);
const std::string produced_output_1 = ReadOutput(kOutputToRead1);
const std::string produced_output_2 = ReadOutput(kOutputToRead2);
const std::string expected_output_1 = ReadOutput(kOutputToExpect1);
const std::string expected_output_2 = ReadOutput(kOutputToExpect2);
ASSERT_STREQ(produced_output_1.c_str(), expected_output_1.c_str());
ASSERT_STREQ(produced_output_2.c_str(), expected_output_2.c_str());
}
// One file evaluating to yaml stream format
TEST_F(JsonnetTest, Yaml_stream) {
constexpr char kInputFile[] = "yaml_stream_example.jsonnet";
constexpr char kOutputFile[] = "yaml_stream_example.yaml";
constexpr char kOutputToRead[] = "tests_output/yaml_stream_example.yaml";
constexpr char kOutputToExpect[] =
"tests_expected_output/yaml_stream_example.yaml";
ReadInput(kInputFile);
EvaluateJsonnetCode(kYamlStream, true);
WriteOutput(kOutputFile, kYamlStream);
const std::string produced_output = ReadOutput(kOutputToRead);
const std::string expected_output = ReadOutput(kOutputToExpect);
ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
}
// One file depended on some other files not accessible by the sandbox
TEST_F(JsonnetTest, Bad_evaluation) {
constexpr char kInputFile[] = "imports.jsonnet";
ReadInput(kInputFile);
EvaluateJsonnetCode(kBase, false);
}
} // namespace

View File

@ -0,0 +1,160 @@
// Copyright 2020 Google LLC
//
// 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 "jsonnet_tests.h" // NOLINT(build/include)
// Prepares what is needed to perform a test.
void JsonnetTestHelper::TestSetUp() {
// Get paths to where input and output is stored.
char buffer[256];
int error = readlink("/proc/self/exe", buffer, 256);
ASSERT_GE(error, 0);
std::pair<absl::string_view, absl::string_view> parts_of_path =
sandbox2::file::SplitPath(buffer);
absl::string_view binary_path = parts_of_path.first;
std::string input_path =
sandbox2::file::JoinPath(binary_path, "tests_input", "dummy_input");
std::string output_path =
sandbox2::file::JoinPath(binary_path, "tests_output", "dummy_input");
// Set up sandbox and api.
sandbox_ = absl::make_unique<JsonnetBaseSandbox>(input_path, output_path);
ASSERT_THAT(sandbox_->Init(), sapi::IsOk());
api_ = absl::make_unique<JsonnetApi>(sandbox_.get());
// Initialize library's main structure.
SAPI_ASSERT_OK_AND_ASSIGN(JsonnetVm * vm_ptr, api_->c_jsonnet_make());
vm_ = absl::make_unique<sapi::v::RemotePtr>(vm_ptr);
jsonnet_vm_was_used_ = false;
input_was_read_ = false;
}
// Cleans up after a test.
void JsonnetTestHelper::TestTearDown() {
if (jsonnet_vm_was_used_) {
SAPI_ASSERT_OK_AND_ASSIGN(char* result,
api_->c_jsonnet_realloc(vm_.get(), output_.get(), 0));
}
ASSERT_THAT(api_->c_jsonnet_destroy(vm_.get()), sapi::IsOk());
if (input_was_read_) {
ASSERT_THAT(api_->c_free_input(input_.get()), sapi::IsOk());
}
}
// Reads input from file.
void JsonnetTestHelper::ReadInput(const char* filename) {
std::string in_file_in_sandboxee(std::string("/input/") +
basename(const_cast<char*>(&filename[0])));
input_filename_in_sandboxee_ = std::move(in_file_in_sandboxee);
sapi::v::ConstCStr in_file_var(input_filename_in_sandboxee_.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(char* input_ptr,
api_->c_read_input(0, in_file_var.PtrBefore()));
input_ = absl::make_unique<sapi::v::RemotePtr>(input_ptr);
input_was_read_ = true;
}
// Evaluates jsonnet code.
void JsonnetTestHelper::Evaluate_jsonnet_code(Evaluation type,
bool expected_correct) {
sapi::v::ConstCStr in_file_var(input_filename_in_sandboxee_.c_str());
sapi::v::Int error;
char* output_ptr;
switch (type) {
case kBase: {
SAPI_ASSERT_OK_AND_ASSIGN(output_ptr, api_->c_jsonnet_evaluate_snippet(
vm_.get(), in_file_var.PtrBefore(),
input_.get(), error.PtrAfter()));
break;
}
case kMultipleFiles: {
SAPI_ASSERT_OK_AND_ASSIGN(output_ptr, api_->c_jsonnet_evaluate_snippet_multi(
vm_.get(), in_file_var.PtrBefore(),
input_.get(), error.PtrAfter()));
break;
}
case kYamlStream: {
SAPI_ASSERT_OK_AND_ASSIGN(output_ptr, api_->c_jsonnet_evaluate_snippet_stream(
vm_.get(), in_file_var.PtrBefore(),
input_.get(), error.PtrAfter()));
break;
}
}
if (expected_correct) {
ASSERT_THAT(error.GetValue(), testing::Eq(0));
} else {
ASSERT_THAT(error.GetValue(), testing::Eq(1));
}
output_ = absl::make_unique<sapi::v::RemotePtr>(output_ptr);
jsonnet_vm_was_used_ = true;
}
// Writes output to file.
void JsonnetTestHelper::WriteOutput(const char* filename_or_directory,
Evaluation type) {
bool success;
switch (type) {
case kBase: {
std::string out_file_in_sandboxee(
std::string("/output/") +
basename(const_cast<char*>(&filename_or_directory[0])));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(
success,
api_->c_write_output_file(output_.get(), out_file_var.PtrBefore()));
break;
}
case kMultipleFiles: {
std::string out_file_in_sandboxee(std::string("/output/"));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(success,
api_->c_write_multi_output_files(
output_.get(), out_file_var.PtrBefore(), false));
break;
}
case kYamlStream: {
std::string out_file_in_sandboxee(
std::string("/output/") +
basename(const_cast<char*>(&filename_or_directory[0])));
sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
SAPI_ASSERT_OK_AND_ASSIGN(
success,
api_->c_write_output_stream(output_.get(), out_file_var.PtrBefore()));
break;
}
}
ASSERT_THAT(success, testing::Eq(true));
}
// Reads the output written to a file by library function / expected output
std::string JsonnetTestHelper::ReadOutput(const char* filename) {
std::ifstream input_stream(filename);
std::string contents((std::istreambuf_iterator<char>(input_stream)),
std::istreambuf_iterator<char>());
return contents;
}