Skip to content

Commit c308dac

Browse files
Copilotlarp0
andcommitted
Fix crunchy crate Windows cross-compilation error by patching path separators
Co-authored-by: larp0 <[email protected]>
1 parent c1144c8 commit c308dac

File tree

5 files changed

+360
-0
lines changed

5 files changed

+360
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ remote-wallet = ["solana-remote-wallet"]
5050

5151
[patch.crates-io]
5252
curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", tag = "3.2.0" }
53+
crunchy = { path = "vendor/crunchy" }

vendor/crunchy/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "crunchy"
3+
version = "0.2.3"
4+
authors = ["Eira Fransham <[email protected]>"]
5+
license = "MIT"
6+
description = "Crunchy unroller: deterministically unroll constant loops (fixed for cross-compilation)"
7+
repository = "https://github.com/eira-fransham/crunchy"
8+
documentation = "https://docs.rs/crunchy"
9+
keywords = ["unroll", "loop", "const", "for", "deterministic"]
10+
categories = ["development-tools", "no-std"]
11+
edition = "2018"
12+
exclude = ["CHANGELOG.md"]
13+
14+
[features]
15+
default = ["limit_128"]
16+
17+
std = []
18+
19+
limit_64 = []
20+
limit_128 = []
21+
limit_256 = []
22+
limit_512 = []
23+
limit_1024 = []
24+
limit_2048 = []
25+
26+
[build-dependencies]

vendor/crunchy/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Fix for crunchy crate cross-compilation issue
2+
3+
This directory contains a patched version of the `crunchy` crate that fixes a Windows cross-compilation issue.
4+
5+
## Problem
6+
7+
The original `crunchy` v0.2.3 crate has a bug when cross-compiling from Unix systems to Windows targets.
8+
The issue is in `src/lib.rs` lines 36-40:
9+
10+
```rust
11+
#[cfg(target_os = "windows")]
12+
include!(concat!(env!("OUT_DIR"), "\\lib.rs"));
13+
14+
#[cfg(not(target_os = "windows"))]
15+
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
16+
```
17+
18+
When cross-compiling from Linux to Windows, `target_os = "windows"` is true, but the build script runs on Linux,
19+
so `OUT_DIR` contains a Linux path like `/home/.../target/x86_64-pc-windows-gnu/debug/build/crunchy-xxx/out`.
20+
Appending `\\lib.rs` creates an invalid path: `/home/.../out\lib.rs`.
21+
22+
## Solution
23+
24+
The fix uses forward slashes consistently since the build process always runs on the host system:
25+
26+
```rust
27+
// Always use forward slash since this runs on the host system during build
28+
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
29+
```
30+
31+
## Error Fixed
32+
33+
This resolves the error:
34+
```
35+
error: couldn't read `/github/workspace/./target/x86_64-pc-windows-gnu/release/build/crunchy-034bbf831537023a/out\lib.rs`: No such file or directory (os error 2)
36+
```
37+
38+
## Source
39+
40+
Original crate: https://github.com/eira-fransham/crunchy
41+
Version: 0.2.3
42+
License: MIT

vendor/crunchy/build.rs

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
use std::env;
2+
use std::fs::File;
3+
use std::io::Write;
4+
use std::path::Path;
5+
6+
const LOWER_LIMIT: usize = 16;
7+
8+
fn main() {
9+
let limit = if cfg!(feature="limit_2048") {
10+
2048
11+
} else if cfg!(feature="limit_1024") {
12+
1024
13+
} else if cfg!(feature="limit_512") {
14+
512
15+
} else if cfg!(feature="limit_256") {
16+
256
17+
} else if cfg!(feature="limit_128") {
18+
128
19+
} else {
20+
64
21+
};
22+
23+
let out_dir = env::var("OUT_DIR").unwrap();
24+
let dest_path = Path::new(&out_dir).join("lib.rs");
25+
let mut f = File::create(&dest_path).unwrap();
26+
27+
let mut output = String::new();
28+
29+
output.push_str(r#"
30+
/// Unroll the given for loop
31+
///
32+
/// Example:
33+
///
34+
/// ```ignore
35+
/// unroll! {
36+
/// for i in 0..5 {
37+
/// println!("Iteration {}", i);
38+
/// }
39+
/// }
40+
/// ```
41+
///
42+
/// will expand into:
43+
///
44+
/// ```ignore
45+
/// { println!("Iteration {}", 0); }
46+
/// { println!("Iteration {}", 1); }
47+
/// { println!("Iteration {}", 2); }
48+
/// { println!("Iteration {}", 3); }
49+
/// { println!("Iteration {}", 4); }
50+
/// ```
51+
#[macro_export]
52+
macro_rules! unroll {
53+
(for $v:ident in 0..0 $c:block) => {};
54+
55+
(for $v:ident < $max:tt in ($start:tt..$end:tt).step_by($val:expr) {$($c:tt)*}) => {
56+
{
57+
let step = $val;
58+
let start = $start;
59+
let end = start + ($end - start) / step;
60+
unroll! {
61+
for val < $max in start..end {
62+
let $v: usize = ((val - start) * step) + start;
63+
64+
$($c)*
65+
}
66+
}
67+
}
68+
};
69+
70+
(for $v:ident in ($start:tt..$end:tt).step_by($val:expr) {$($c:tt)*}) => {
71+
unroll! {
72+
for $v < $end in ($start..$end).step_by($val) {$($c)*}
73+
}
74+
};
75+
76+
(for $v:ident in ($start:tt..$end:tt) {$($c:tt)*}) => {
77+
unroll!{
78+
for $v in $start..$end {$($c)*}
79+
}
80+
};
81+
82+
(for $v:ident in $start:tt..$end:tt {$($c:tt)*}) => {
83+
#[allow(non_upper_case_globals)]
84+
#[allow(unused_comparisons)]
85+
{
86+
unroll!(@$v, 0, $end, {
87+
if $v >= $start {$($c)*}
88+
}
89+
);
90+
}
91+
};
92+
93+
(for $v:ident < $max:tt in $start:tt..$end:tt $c:block) => {
94+
#[allow(non_upper_case_globals)]
95+
{
96+
let range = $start..$end;
97+
assert!(
98+
$max >= range.end,
99+
"`{}` out of range `{:?}`",
100+
stringify!($max),
101+
range,
102+
);
103+
unroll!(
104+
@$v,
105+
0,
106+
$max,
107+
{
108+
if $v >= range.start && $v < range.end {
109+
$c
110+
}
111+
}
112+
);
113+
}
114+
};
115+
116+
(for $v:ident in 0..$end:tt {$($statement:tt)*}) => {
117+
#[allow(non_upper_case_globals)]
118+
{ unroll!(@$v, 0, $end, {$($statement)*}); }
119+
};
120+
121+
"#);
122+
123+
for i in 0..limit + 1 {
124+
output.push_str(format!(" (@$v:ident, $a:expr, {}, $c:block) => {{\n", i).as_str());
125+
126+
if i <= LOWER_LIMIT {
127+
output.push_str(format!(" {{ const $v: usize = $a; $c }}\n").as_str());
128+
129+
for a in 1..i {
130+
output.push_str(format!(" {{ const $v: usize = $a + {}; $c }}\n", a).as_str());
131+
}
132+
} else {
133+
let half = i / 2;
134+
135+
if i % 2 == 0 {
136+
output.push_str(format!(" unroll!(@$v, $a, {0}, $c);\n", half).as_str());
137+
output.push_str(format!(" unroll!(@$v, $a + {0}, {0}, $c);\n", half).as_str());
138+
} else {
139+
if half > 1 {
140+
output.push_str(format!(" unroll!(@$v, $a, {}, $c);\n", i - 1).as_str())
141+
}
142+
143+
output.push_str(format!(" {{ const $v: usize = $a + {}; $c }}\n", i - 1).as_str());
144+
}
145+
}
146+
147+
output.push_str(" };\n\n");
148+
}
149+
150+
output.push_str("}\n\n");
151+
152+
output.push_str(format!(r#"
153+
#[cfg(all(test, feature = "std"))]
154+
mod tests {{
155+
#[test]
156+
fn invalid_range() {{
157+
let mut a: Vec<usize> = vec![];
158+
unroll! {{
159+
for i in (5..4) {{
160+
a.push(i);
161+
}}
162+
}}
163+
assert_eq!(a, vec![]);
164+
}}
165+
166+
#[test]
167+
fn start_at_one_with_step() {{
168+
let mut a: Vec<usize> = vec![];
169+
unroll! {{
170+
for i in (2..4).step_by(1) {{
171+
a.push(i);
172+
}}
173+
}}
174+
assert_eq!(a, vec![2, 3]);
175+
}}
176+
177+
#[test]
178+
fn start_at_one() {{
179+
let mut a: Vec<usize> = vec![];
180+
unroll! {{
181+
for i in 1..4 {{
182+
a.push(i);
183+
}}
184+
}}
185+
assert_eq!(a, vec![1, 2, 3]);
186+
}}
187+
188+
#[test]
189+
fn test_all() {{
190+
{{
191+
let a: Vec<usize> = vec![];
192+
unroll! {{
193+
for i in 0..0 {{
194+
a.push(i);
195+
}}
196+
}}
197+
assert_eq!(a, (0..0).collect::<Vec<usize>>());
198+
}}
199+
{{
200+
let mut a: Vec<usize> = vec![];
201+
unroll! {{
202+
for i in 0..1 {{
203+
a.push(i);
204+
}}
205+
}}
206+
assert_eq!(a, (0..1).collect::<Vec<usize>>());
207+
}}
208+
{{
209+
let mut a: Vec<usize> = vec![];
210+
unroll! {{
211+
for i in 0..{0} {{
212+
a.push(i);
213+
}}
214+
}}
215+
assert_eq!(a, (0..{0}).collect::<Vec<usize>>());
216+
}}
217+
{{
218+
let mut a: Vec<usize> = vec![];
219+
let start = {0} / 4;
220+
let end = start * 3;
221+
unroll! {{
222+
for i < {0} in start..end {{
223+
a.push(i);
224+
}}
225+
}}
226+
assert_eq!(a, (start..end).collect::<Vec<usize>>());
227+
}}
228+
{{
229+
let mut a: Vec<usize> = vec![];
230+
unroll! {{
231+
for i in (0..{0}).step_by(2) {{
232+
a.push(i);
233+
}}
234+
}}
235+
assert_eq!(a, (0..{0} / 2).map(|x| x * 2).collect::<Vec<usize>>());
236+
}}
237+
{{
238+
let mut a: Vec<usize> = vec![];
239+
let start = {0} / 4;
240+
let end = start * 3;
241+
unroll! {{
242+
for i < {0} in (start..end).step_by(2) {{
243+
a.push(i);
244+
}}
245+
}}
246+
assert_eq!(a, (start..end).filter(|x| x % 2 == 0).collect::<Vec<usize>>());
247+
}}
248+
}}
249+
}}
250+
"#, limit).as_str());
251+
252+
f.write_all(output.as_bytes()).unwrap();
253+
}

vendor/crunchy/src/lib.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! The crunchy unroller - deterministically unroll constant loops. For number "crunching".
2+
//!
3+
//! The Rust optimizer will unroll constant loops that don't use the loop variable, like this:
4+
//!
5+
//! ```ignore
6+
//! for _ in 0..100 {
7+
//! println!("Hello!");
8+
//! }
9+
//! ```
10+
//!
11+
//! However, using the loop variable will cause it to never unroll the loop. This is unfortunate because it means that you can't
12+
//! constant-fold the loop variable, and if you end up stomping on the registers it will have to do a load for each iteration.
13+
//! This crate ensures that your code is unrolled and const-folded. It only works on literals,
14+
//! unfortunately, but there's a work-around:
15+
//!
16+
//! ```ignore
17+
//! debug_assert_eq!(MY_CONSTANT, 100);
18+
//! unroll! {
19+
//! for i in 0..100 {
20+
//! println!("Iteration {}", i);
21+
//! }
22+
//! }
23+
//! ```
24+
//! This means that your tests will catch if you redefine the constant.
25+
//!
26+
//! To default maximum number of loops to unroll is `64`, but that can be easily increased using the cargo features:
27+
//!
28+
//! * `limit_128`
29+
//! * `limit_256`
30+
//! * `limit_512`
31+
//! * `limit_1024`
32+
//! * `limit_2048`
33+
34+
#![cfg_attr(not(feature = "std"), no_std)]
35+
36+
// Fix for cross-compilation: Always use forward slash since this runs on the host system during build
37+
// On Unix systems (including when cross-compiling from Unix to Windows), paths use forward slashes
38+
include!(concat!(env!("OUT_DIR"), "/lib.rs"));

0 commit comments

Comments
 (0)