Skip to content

Commit 4dbf4b7

Browse files
committedDec 23, 2022
Vendor tinyjson sources to make process_wrapper deterministic
Fixes bazelbuild#1530
1 parent 1357b85 commit 4dbf4b7

File tree

10 files changed

+757
-16
lines changed

10 files changed

+757
-16
lines changed
 

‎rust/repositories.bzl

-11
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,6 @@ def rules_rust_dependencies():
8080
url = "https://github.com/bazelbuild/apple_support/releases/download/1.3.0/apple_support.1.3.0.tar.gz",
8181
)
8282

83-
# process_wrapper needs a low-dependency way to process json.
84-
maybe(
85-
http_archive,
86-
name = "rules_rust_tinyjson",
87-
sha256 = "1a8304da9f9370f6a6f9020b7903b044aa9ce3470f300a1fba5bc77c78145a16",
88-
url = "https://crates.io/api/v1/crates/tinyjson/2.3.0/download",
89-
strip_prefix = "tinyjson-2.3.0",
90-
type = "tar.gz",
91-
build_file = "@rules_rust//util/process_wrapper:BUILD.tinyjson.bazel",
92-
)
93-
9483
_RUST_TOOLCHAIN_VERSIONS = [
9584
rust_common.default_version,
9685
"nightly/{}".format(DEFAULT_NIGHTLY_ISO_DATE),

‎util/process_wrapper/BUILD.bazel

+1-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@ load("//rust/private:rust.bzl", "rust_binary_without_process_wrapper")
55

66
rust_binary_without_process_wrapper(
77
name = "process_wrapper",
8-
srcs = glob(["*.rs"]),
8+
srcs = glob(["**/*.rs"]),
99
edition = "2018",
1010
visibility = ["//visibility:public"],
11-
deps = [
12-
"@rules_rust_tinyjson//:tinyjson",
13-
],
1411
)
1512

1613
rust_test(

‎util/process_wrapper/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod options;
1717
mod output;
1818
mod rustc;
1919
mod util;
20+
mod tinyjson;
2021

2122
use std::fs::{copy, OpenOptions};
2223
use std::io;

‎util/process_wrapper/rustc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

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

17-
use tinyjson::JsonValue;
17+
use crate::tinyjson::JsonValue;
1818

1919
use crate::output::LineOutput;
2020

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
the MIT License
2+
3+
Copyright (c) 2016 rhysd
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+
of the Software, and to permit persons to whom the Software is furnished to do so,
10+
subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
20+
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This directory contains code from [tinyjson](https://github.com/rhysd/tinyjson).
2+
3+
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use super::JsonValue;
2+
use std::collections::HashMap;
3+
use std::error;
4+
use std::fmt;
5+
6+
#[derive(Debug)]
7+
pub struct JsonGenerateError {
8+
msg: &'static str,
9+
}
10+
11+
impl JsonGenerateError {
12+
fn new(msg: &'static str) -> Self {
13+
JsonGenerateError { msg }
14+
}
15+
16+
pub fn message(&self) -> &str {
17+
&self.msg
18+
}
19+
}
20+
21+
impl fmt::Display for JsonGenerateError {
22+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23+
write!(f, "Generate error: {}", &self.msg)
24+
}
25+
}
26+
27+
impl error::Error for JsonGenerateError {}
28+
29+
pub type JsonGenerateResult = Result<String, JsonGenerateError>;
30+
31+
fn quote(s: &str) -> String {
32+
let mut to = '"'.to_string();
33+
for c in s.chars() {
34+
match c {
35+
'\\' => to.push_str("\\\\"),
36+
'\u{0008}' => to.push_str("\\b"),
37+
'\u{000c}' => to.push_str("\\f"),
38+
'\n' => to.push_str("\\n"),
39+
'\r' => to.push_str("\\r"),
40+
'\t' => to.push_str("\\t"),
41+
'"' => to.push_str("\\\""),
42+
c if c.is_control() => to.push_str(&format!("\\u{:04x}", c as u32)),
43+
c => to.push(c),
44+
}
45+
}
46+
to.push('"');
47+
to
48+
}
49+
50+
fn array(array: &[JsonValue]) -> JsonGenerateResult {
51+
let mut to = '['.to_string();
52+
for elem in array.iter() {
53+
let s = stringify(elem)?;
54+
to += &s;
55+
to.push(',');
56+
}
57+
if !array.is_empty() {
58+
to.pop(); // Remove trailing comma
59+
}
60+
to.push(']');
61+
Ok(to)
62+
}
63+
64+
fn object(m: &HashMap<String, JsonValue>) -> JsonGenerateResult {
65+
let mut to = '{'.to_string();
66+
for (k, v) in m {
67+
to += &quote(k);
68+
to.push(':');
69+
let s = stringify(v)?;
70+
to += &s;
71+
to.push(',');
72+
}
73+
if !m.is_empty() {
74+
to.pop(); // Remove trailing comma
75+
}
76+
to.push('}');
77+
Ok(to)
78+
}
79+
80+
fn number(f: f64) -> JsonGenerateResult {
81+
if f.is_infinite() {
82+
Err(JsonGenerateError::new("JSON cannot represent inf"))
83+
} else if f.is_nan() {
84+
Err(JsonGenerateError::new("JSON cannot represent NaN"))
85+
} else {
86+
Ok(f.to_string())
87+
}
88+
}
89+
90+
pub fn stringify(value: &JsonValue) -> JsonGenerateResult {
91+
match value {
92+
JsonValue::Number(n) => number(*n),
93+
JsonValue::Boolean(b) => Ok(b.to_string()),
94+
JsonValue::String(s) => Ok(quote(s)),
95+
JsonValue::Null => Ok("null".to_string()),
96+
JsonValue::Array(a) => array(a),
97+
JsonValue::Object(o) => object(o),
98+
}
99+
}
+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
use super::generator::{stringify, JsonGenerateResult};
2+
use std::collections::HashMap;
3+
use std::convert::TryInto;
4+
use std::error;
5+
use std::fmt;
6+
use std::ops::{Index, IndexMut};
7+
8+
const NULL: () = ();
9+
10+
#[derive(Debug, Clone, PartialEq)]
11+
pub enum JsonValue {
12+
Number(f64),
13+
Boolean(bool),
14+
String(String),
15+
Null,
16+
Array(Vec<JsonValue>),
17+
Object(HashMap<String, JsonValue>),
18+
}
19+
20+
pub trait InnerAsRef {
21+
fn json_value_as(v: &JsonValue) -> Option<&Self>;
22+
}
23+
24+
macro_rules! impl_inner_ref {
25+
($to:ty, $pat:pat => $val:expr) => {
26+
impl InnerAsRef for $to {
27+
fn json_value_as(v: &JsonValue) -> Option<&$to> {
28+
use JsonValue::*;
29+
match v {
30+
$pat => Some($val),
31+
_ => None,
32+
}
33+
}
34+
}
35+
};
36+
}
37+
38+
impl_inner_ref!(f64, Number(n) => n);
39+
impl_inner_ref!(bool, Boolean(b) => b);
40+
impl_inner_ref!(String, String(s) => s);
41+
impl_inner_ref!((), Null => &NULL);
42+
impl_inner_ref!(Vec<JsonValue>, Array(a) => a);
43+
impl_inner_ref!(HashMap<String, JsonValue>, Object(h) => h);
44+
45+
pub trait InnerAsRefMut {
46+
fn json_value_as_mut(v: &mut JsonValue) -> Option<&mut Self>;
47+
}
48+
49+
macro_rules! impl_inner_ref_mut {
50+
($to:ty, $pat:pat => $val:expr) => {
51+
impl InnerAsRefMut for $to {
52+
fn json_value_as_mut(v: &mut JsonValue) -> Option<&mut $to> {
53+
use JsonValue::*;
54+
match v {
55+
$pat => Some($val),
56+
_ => None,
57+
}
58+
}
59+
}
60+
};
61+
}
62+
63+
impl_inner_ref_mut!(f64, Number(n) => n);
64+
impl_inner_ref_mut!(bool, Boolean(b) => b);
65+
impl_inner_ref_mut!(String, String(s) => s);
66+
impl_inner_ref_mut!(Vec<JsonValue>, Array(a) => a);
67+
impl_inner_ref_mut!(HashMap<String, JsonValue>, Object(h) => h);
68+
69+
// Note: matches! is available from Rust 1.42
70+
macro_rules! is_xxx {
71+
($name:ident, $variant:pat) => {
72+
pub fn $name(&self) -> bool {
73+
match self {
74+
$variant => true,
75+
_ => false,
76+
}
77+
}
78+
};
79+
}
80+
81+
impl JsonValue {
82+
pub fn get<T: InnerAsRef>(&self) -> Option<&T> {
83+
T::json_value_as(self)
84+
}
85+
86+
pub fn get_mut<T: InnerAsRefMut>(&mut self) -> Option<&mut T> {
87+
T::json_value_as_mut(self)
88+
}
89+
90+
is_xxx!(is_bool, JsonValue::Boolean(_));
91+
is_xxx!(is_number, JsonValue::Number(_));
92+
is_xxx!(is_string, JsonValue::String(_));
93+
is_xxx!(is_null, JsonValue::Null);
94+
is_xxx!(is_array, JsonValue::Array(_));
95+
is_xxx!(is_object, JsonValue::Object(_));
96+
97+
pub fn stringify(&self) -> JsonGenerateResult {
98+
stringify(self)
99+
}
100+
}
101+
102+
impl<'a> Index<&'a str> for JsonValue {
103+
type Output = JsonValue;
104+
105+
fn index(&self, key: &'a str) -> &Self::Output {
106+
let obj = match self {
107+
JsonValue::Object(o) => o,
108+
_ => panic!(
109+
"Attempted to access to an object with key '{}' but actually it was {:?}",
110+
key, self
111+
),
112+
};
113+
114+
match obj.get(key) {
115+
Some(json) => json,
116+
None => panic!("Key '{}' was not found in {:?}", key, self),
117+
}
118+
}
119+
}
120+
121+
impl Index<usize> for JsonValue {
122+
type Output = JsonValue;
123+
124+
fn index(&self, index: usize) -> &'_ Self::Output {
125+
let array = match self {
126+
JsonValue::Array(a) => a,
127+
_ => panic!(
128+
"Attempted to access to an array with index {} but actually the value was {:?}",
129+
index, self,
130+
),
131+
};
132+
&array[index]
133+
}
134+
}
135+
136+
impl<'a> IndexMut<&'a str> for JsonValue {
137+
fn index_mut(&mut self, key: &'a str) -> &mut Self::Output {
138+
let obj = match self {
139+
JsonValue::Object(o) => o,
140+
_ => panic!(
141+
"Attempted to access to an object with key '{}' but actually it was {:?}",
142+
key, self
143+
),
144+
};
145+
146+
if let Some(json) = obj.get_mut(key) {
147+
json
148+
} else {
149+
panic!("Key '{}' was not found in object", key)
150+
}
151+
}
152+
}
153+
154+
impl IndexMut<usize> for JsonValue {
155+
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
156+
let array = match self {
157+
JsonValue::Array(a) => a,
158+
_ => panic!(
159+
"Attempted to access to an array with index {} but actually the value was {:?}",
160+
index, self,
161+
),
162+
};
163+
164+
&mut array[index]
165+
}
166+
}
167+
168+
#[derive(Debug)]
169+
pub struct UnexpectedValue {
170+
value: JsonValue,
171+
expected: &'static str,
172+
}
173+
174+
impl fmt::Display for UnexpectedValue {
175+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176+
write!(
177+
f,
178+
"Unexpected JSON value: {:?}. Expected {} value",
179+
self.value, self.expected
180+
)
181+
}
182+
}
183+
184+
impl error::Error for UnexpectedValue {}
185+
186+
macro_rules! impl_try_into {
187+
($ty:ty, $pat:pat => $val:expr) => {
188+
impl TryInto<$ty> for JsonValue {
189+
type Error = UnexpectedValue;
190+
191+
fn try_into(self) -> Result<$ty, UnexpectedValue> {
192+
match self {
193+
$pat => Ok($val),
194+
v => Err(UnexpectedValue {
195+
value: v,
196+
expected: stringify!($ty),
197+
}),
198+
}
199+
}
200+
}
201+
};
202+
}
203+
204+
impl_try_into!(f64, JsonValue::Number(n) => n);
205+
impl_try_into!(bool, JsonValue::Boolean(b) => b);
206+
impl_try_into!(String, JsonValue::String(s) => s);
207+
impl_try_into!((), JsonValue::Null => ());
208+
impl_try_into!(Vec<JsonValue>, JsonValue::Array(a) => a);
209+
impl_try_into!(HashMap<String, JsonValue>, JsonValue::Object(o) => o);

‎util/process_wrapper/tinyjson/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![allow(dead_code)]
2+
3+
mod generator;
4+
mod json_value;
5+
mod parser;
6+
7+
pub use json_value::JsonValue;

‎util/process_wrapper/tinyjson/parser.rs

+416
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.