Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vendor tinyjson sources to make process_wrapper deterministic #1729

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions rust/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,6 @@ def rules_rust_dependencies():
url = "https://github.com/bazelbuild/apple_support/releases/download/1.3.0/apple_support.1.3.0.tar.gz",
)

# process_wrapper needs a low-dependency way to process json.
maybe(
http_archive,
name = "rules_rust_tinyjson",
sha256 = "1a8304da9f9370f6a6f9020b7903b044aa9ce3470f300a1fba5bc77c78145a16",
url = "https://crates.io/api/v1/crates/tinyjson/2.3.0/download",
strip_prefix = "tinyjson-2.3.0",
type = "tar.gz",
build_file = "@rules_rust//util/process_wrapper:BUILD.tinyjson.bazel",
)

_RUST_TOOLCHAIN_VERSIONS = [
rust_common.default_version,
"nightly/{}".format(DEFAULT_NIGHTLY_ISO_DATE),
Expand Down
5 changes: 1 addition & 4 deletions util/process_wrapper/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ load("//rust/private:rust.bzl", "rust_binary_without_process_wrapper")

rust_binary_without_process_wrapper(
name = "process_wrapper",
srcs = glob(["*.rs"]),
srcs = glob(["**/*.rs"]),
edition = "2018",
visibility = ["//visibility:public"],
deps = [
"@rules_rust_tinyjson//:tinyjson",
],
)

rust_test(
Expand Down
1 change: 1 addition & 0 deletions util/process_wrapper/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod flags;
mod options;
mod output;
mod rustc;
mod tinyjson;
mod util;

use std::fs::{copy, OpenOptions};
Expand Down
2 changes: 1 addition & 1 deletion util/process_wrapper/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use std::convert::{TryFrom, TryInto};

use tinyjson::JsonValue;
use crate::tinyjson::JsonValue;

use crate::output::LineOutput;

Expand Down
20 changes: 20 additions & 0 deletions util/process_wrapper/tinyjson/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
the MIT License

Copyright (c) 2016 rhysd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3 changes: 3 additions & 0 deletions util/process_wrapper/tinyjson/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory contains code from [tinyjson](https://github.com/rhysd/tinyjson).

It's vendored in rules_rust as a workaround for an issue where, if tinyjson were built as an independent crate, the resulting library would contain absolute path strings pointing into the Bazel sandbox directory, breaking build reproducibility. (https://github.com/bazelbuild/rules_rust/issues/1530)
99 changes: 99 additions & 0 deletions util/process_wrapper/tinyjson/generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use super::JsonValue;
use std::collections::HashMap;
use std::error;
use std::fmt;

#[derive(Debug)]
pub struct JsonGenerateError {
msg: &'static str,
}

impl JsonGenerateError {
fn new(msg: &'static str) -> Self {
JsonGenerateError { msg }
}

pub fn message(&self) -> &str {
self.msg
}
}

impl fmt::Display for JsonGenerateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Generate error: {}", &self.msg)
}
}

impl error::Error for JsonGenerateError {}

pub type JsonGenerateResult = Result<String, JsonGenerateError>;

fn quote(s: &str) -> String {
let mut to = '"'.to_string();
for c in s.chars() {
match c {
'\\' => to.push_str("\\\\"),
'\u{0008}' => to.push_str("\\b"),
'\u{000c}' => to.push_str("\\f"),
'\n' => to.push_str("\\n"),
'\r' => to.push_str("\\r"),
'\t' => to.push_str("\\t"),
'"' => to.push_str("\\\""),
c if c.is_control() => to.push_str(&format!("\\u{:04x}", c as u32)),
c => to.push(c),
}
}
to.push('"');
to
}

fn array(array: &[JsonValue]) -> JsonGenerateResult {
let mut to = '['.to_string();
for elem in array.iter() {
let s = stringify(elem)?;
to += &s;
to.push(',');
}
if !array.is_empty() {
to.pop(); // Remove trailing comma
}
to.push(']');
Ok(to)
}

fn object(m: &HashMap<String, JsonValue>) -> JsonGenerateResult {
let mut to = '{'.to_string();
for (k, v) in m {
to += &quote(k);
to.push(':');
let s = stringify(v)?;
to += &s;
to.push(',');
}
if !m.is_empty() {
to.pop(); // Remove trailing comma
}
to.push('}');
Ok(to)
}

fn number(f: f64) -> JsonGenerateResult {
if f.is_infinite() {
Err(JsonGenerateError::new("JSON cannot represent inf"))
} else if f.is_nan() {
Err(JsonGenerateError::new("JSON cannot represent NaN"))
} else {
Ok(f.to_string())
}
}

pub fn stringify(value: &JsonValue) -> JsonGenerateResult {
match value {
JsonValue::Number(n) => number(*n),
JsonValue::Boolean(b) => Ok(b.to_string()),
JsonValue::String(s) => Ok(quote(s)),
JsonValue::Null => Ok("null".to_string()),
JsonValue::Array(a) => array(a),
JsonValue::Object(o) => object(o),
}
}
209 changes: 209 additions & 0 deletions util/process_wrapper/tinyjson/json_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
use super::generator::{stringify, JsonGenerateResult};
use std::collections::HashMap;
use std::convert::TryInto;
use std::error;
use std::fmt;
use std::ops::{Index, IndexMut};

const NULL: () = ();

#[derive(Debug, Clone, PartialEq)]
pub enum JsonValue {
Number(f64),
Boolean(bool),
String(String),
Null,
Array(Vec<JsonValue>),
Object(HashMap<String, JsonValue>),
}

pub trait InnerAsRef {
fn json_value_as(v: &JsonValue) -> Option<&Self>;
}

macro_rules! impl_inner_ref {
($to:ty, $pat:pat => $val:expr) => {
impl InnerAsRef for $to {
fn json_value_as(v: &JsonValue) -> Option<&$to> {
use JsonValue::*;
match v {
$pat => Some($val),
_ => None,
}
}
}
};
}

impl_inner_ref!(f64, Number(n) => n);
impl_inner_ref!(bool, Boolean(b) => b);
impl_inner_ref!(String, String(s) => s);
impl_inner_ref!((), Null => &NULL);
impl_inner_ref!(Vec<JsonValue>, Array(a) => a);
impl_inner_ref!(HashMap<String, JsonValue>, Object(h) => h);

pub trait InnerAsRefMut {
fn json_value_as_mut(v: &mut JsonValue) -> Option<&mut Self>;
}

macro_rules! impl_inner_ref_mut {
($to:ty, $pat:pat => $val:expr) => {
impl InnerAsRefMut for $to {
fn json_value_as_mut(v: &mut JsonValue) -> Option<&mut $to> {
use JsonValue::*;
match v {
$pat => Some($val),
_ => None,
}
}
}
};
}

impl_inner_ref_mut!(f64, Number(n) => n);
impl_inner_ref_mut!(bool, Boolean(b) => b);
impl_inner_ref_mut!(String, String(s) => s);
impl_inner_ref_mut!(Vec<JsonValue>, Array(a) => a);
impl_inner_ref_mut!(HashMap<String, JsonValue>, Object(h) => h);

// Note: matches! is available from Rust 1.42
macro_rules! is_xxx {
($name:ident, $variant:pat) => {
pub fn $name(&self) -> bool {
match self {
$variant => true,
_ => false,
}
}
};
}

impl JsonValue {
pub fn get<T: InnerAsRef>(&self) -> Option<&T> {
T::json_value_as(self)
}

pub fn get_mut<T: InnerAsRefMut>(&mut self) -> Option<&mut T> {
T::json_value_as_mut(self)
}

is_xxx!(is_bool, JsonValue::Boolean(_));
is_xxx!(is_number, JsonValue::Number(_));
is_xxx!(is_string, JsonValue::String(_));
is_xxx!(is_null, JsonValue::Null);
is_xxx!(is_array, JsonValue::Array(_));
is_xxx!(is_object, JsonValue::Object(_));

pub fn stringify(&self) -> JsonGenerateResult {
stringify(self)
}
}

impl<'a> Index<&'a str> for JsonValue {
type Output = JsonValue;

fn index(&self, key: &'a str) -> &Self::Output {
let obj = match self {
JsonValue::Object(o) => o,
_ => panic!(
"Attempted to access to an object with key '{}' but actually it was {:?}",
key, self
),
};

match obj.get(key) {
Some(json) => json,
None => panic!("Key '{}' was not found in {:?}", key, self),
}
}
}

impl Index<usize> for JsonValue {
type Output = JsonValue;

fn index(&self, index: usize) -> &'_ Self::Output {
let array = match self {
JsonValue::Array(a) => a,
_ => panic!(
"Attempted to access to an array with index {} but actually the value was {:?}",
index, self,
),
};
&array[index]
}
}

impl<'a> IndexMut<&'a str> for JsonValue {
fn index_mut(&mut self, key: &'a str) -> &mut Self::Output {
let obj = match self {
JsonValue::Object(o) => o,
_ => panic!(
"Attempted to access to an object with key '{}' but actually it was {:?}",
key, self
),
};

if let Some(json) = obj.get_mut(key) {
json
} else {
panic!("Key '{}' was not found in object", key)
}
}
}

impl IndexMut<usize> for JsonValue {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let array = match self {
JsonValue::Array(a) => a,
_ => panic!(
"Attempted to access to an array with index {} but actually the value was {:?}",
index, self,
),
};

&mut array[index]
}
}

#[derive(Debug)]
pub struct UnexpectedValue {
value: JsonValue,
expected: &'static str,
}

impl fmt::Display for UnexpectedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Unexpected JSON value: {:?}. Expected {} value",
self.value, self.expected
)
}
}

impl error::Error for UnexpectedValue {}

macro_rules! impl_try_into {
($ty:ty, $pat:pat => $val:expr) => {
impl TryInto<$ty> for JsonValue {
type Error = UnexpectedValue;

fn try_into(self) -> Result<$ty, UnexpectedValue> {
match self {
$pat => Ok($val),
v => Err(UnexpectedValue {
value: v,
expected: stringify!($ty),
}),
}
}
}
};
}

impl_try_into!(f64, JsonValue::Number(n) => n);
impl_try_into!(bool, JsonValue::Boolean(b) => b);
impl_try_into!(String, JsonValue::String(s) => s);
impl_try_into!((), JsonValue::Null => ());
impl_try_into!(Vec<JsonValue>, JsonValue::Array(a) => a);
impl_try_into!(HashMap<String, JsonValue>, JsonValue::Object(o) => o);
7 changes: 7 additions & 0 deletions util/process_wrapper/tinyjson/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![allow(dead_code)]

mod generator;
mod json_value;
mod parser;

pub use json_value::JsonValue;
Loading