Skip to content

Commit cd192ab

Browse files
committed
fix(emit): skip comments above the first checksum entry
An annotated checksums list names each file in a comment above its hash. The rewrite skipped whitespace and then demanded a brace or a quote, so the comment line made it give up with "unrecognized source checksum entry form" and the bump failed on exactly the lists a reviewer finds easiest to read. Comment and blank lines are now skipped like any other leading whitespace, and the annotations pass through untouched.
1 parent c085e6d commit cd192ab

1 file changed

Lines changed: 131 additions & 4 deletions

File tree

‎src/eb_emit.rs‎

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,26 @@ fn rewrite_dep_list_selections(
490490
/// `old_version` replaced with `new_version`. The checksum value is replaced
491491
/// with `new_checksum` when given; otherwise it is left as-is and the second
492492
/// return value is `true` (the value is now stale).
493+
/// Advance past whitespace and whole-line `#` comments to the first real entry.
494+
///
495+
/// An annotated checksums list names each file above its hash, so the first
496+
/// thing inside the brackets is often a comment rather than an entry.
497+
fn skip_to_first_entry(bytes: &[u8], from: usize) -> usize {
498+
let mut i = from;
499+
loop {
500+
while i < bytes.len() && (bytes[i] as char).is_whitespace() {
501+
i += 1;
502+
}
503+
if i < bytes.len() && bytes[i] == b'#' {
504+
while i < bytes.len() && bytes[i] != b'\n' {
505+
i += 1;
506+
}
507+
continue;
508+
}
509+
return i;
510+
}
511+
}
512+
493513
fn rewrite_source_checksum(
494514
src: &str,
495515
old_version: &str,
@@ -503,10 +523,7 @@ fn rewrite_source_checksum(
503523
let body = &src[list_open_end..list_close_start];
504524
let body_bytes = body.as_bytes();
505525

506-
let mut start = 0usize;
507-
while start < body_bytes.len() && (body_bytes[start] as char).is_whitespace() {
508-
start += 1;
509-
}
526+
let start = skip_to_first_entry(body_bytes, 0);
510527
if start >= body_bytes.len() {
511528
// Empty checksums list — nothing to rewrite.
512529
return Ok((src.to_string(), false));
@@ -1017,6 +1034,42 @@ checksums = [
10171034
dependencies = [
10181035
('hwloc', '2.10.0'),
10191036
]
1037+
";
1038+
1039+
/// A checksums list that names each file in a comment above its hash, the
1040+
/// shape upstream uses for annotated multi-entry lists.
1041+
const WITH_COMMENTED_CHECKSUMS: &str = "\
1042+
name = 'OpenMPI'
1043+
version = '5.0.3'
1044+
toolchain = {'name': 'NVHPC', 'version': '24.9-CUDA-12.6.0'}
1045+
homepage = 'https://www.open-mpi.org/'
1046+
sources = [SOURCELOWER_TAR_BZ2]
1047+
patches = [
1048+
'OpenMPI-5.0.3_fix_hle_make_errors.patch',
1049+
]
1050+
checksums = [
1051+
# openmpi-5.0.3.tar.bz2
1052+
{'openmpi-5.0.3.tar.bz2': '990582f206b3ab32e938aa31bbf07c639368e4405dca196fabe7f0f76eeda90b'},
1053+
# OpenMPI-5.0.3_fix_hle_make_errors.patch
1054+
{'OpenMPI-5.0.3_fix_hle_make_errors.patch': '881c907a9f5901d5d6af41cd33dffdcecba4a67a9e5123e602542aea57a80895'},
1055+
]
1056+
dependencies = [
1057+
('hwloc', '2.10.0'),
1058+
]
1059+
";
1060+
1061+
/// The same list with a bare-string first entry under its comment.
1062+
const WITH_COMMENTED_BARE_CHECKSUM: &str = "\
1063+
name = 'OpenMPI'
1064+
version = '5.0.3'
1065+
toolchain = {'name': 'NVHPC', 'version': '24.9-CUDA-12.6.0'}
1066+
homepage = 'https://www.open-mpi.org/'
1067+
sources = [SOURCELOWER_TAR_BZ2]
1068+
checksums = [
1069+
# openmpi-5.0.3.tar.bz2
1070+
1071+
'990582f206b3ab32e938aa31bbf07c639368e4405dca196fabe7f0f76eeda90b',
1072+
]
10201073
";
10211074

10221075
fn nvhpc(ver: &str) -> Toolchain {
@@ -1053,6 +1106,80 @@ dependencies = [
10531106
assert!(r.warnings[0].contains("patches"), "{:?}", r.warnings);
10541107
}
10551108

1109+
#[test]
1110+
fn a_comment_above_the_first_checksum_does_not_stop_the_rewrite() {
1111+
let params = EmitParams {
1112+
toolchain: nvhpc("25.11-CUDA-12.8.0"),
1113+
version: Some("5.0.7".into()),
1114+
dep_versions: HashMap::new(),
1115+
dep_toolchains: HashMap::new(),
1116+
source_checksum: Some(
1117+
"119f2009936a403334d0df3c0d74d5595a32d99497f9b1d41e90019fee2fc2dd".into(),
1118+
),
1119+
};
1120+
let r = emit_next_generation(WITH_COMMENTED_CHECKSUMS, &params).expect("emit");
1121+
assert!(r.text.contains(
1122+
"{'openmpi-5.0.7.tar.bz2': '119f2009936a403334d0df3c0d74d5595a32d99497f9b1d41e90019fee2fc2dd'}"
1123+
), "{}", r.text);
1124+
// The annotations survive: they are not the tool's to rewrite.
1125+
assert!(
1126+
r.text.contains(" # openmpi-5.0.3.tar.bz2\n"),
1127+
"{}",
1128+
r.text
1129+
);
1130+
assert!(
1131+
r.text
1132+
.contains(" # OpenMPI-5.0.3_fix_hle_make_errors.patch\n"),
1133+
"{}",
1134+
r.text
1135+
);
1136+
// The patch entry below the second comment is still left alone.
1137+
assert!(r.text.contains(
1138+
"{'OpenMPI-5.0.3_fix_hle_make_errors.patch': '881c907a9f5901d5d6af41cd33dffdcecba4a67a9e5123e602542aea57a80895'}"
1139+
), "{}", r.text);
1140+
}
1141+
1142+
#[test]
1143+
fn a_comment_and_a_blank_line_above_a_bare_checksum_are_skipped() {
1144+
let params = EmitParams {
1145+
toolchain: nvhpc("25.11-CUDA-12.8.0"),
1146+
version: Some("5.0.7".into()),
1147+
dep_versions: HashMap::new(),
1148+
dep_toolchains: HashMap::new(),
1149+
source_checksum: Some(
1150+
"119f2009936a403334d0df3c0d74d5595a32d99497f9b1d41e90019fee2fc2dd".into(),
1151+
),
1152+
};
1153+
let r = emit_next_generation(WITH_COMMENTED_BARE_CHECKSUM, &params).expect("emit");
1154+
assert!(
1155+
r.text
1156+
.contains("'119f2009936a403334d0df3c0d74d5595a32d99497f9b1d41e90019fee2fc2dd'"),
1157+
"{}",
1158+
r.text
1159+
);
1160+
assert!(
1161+
r.text.contains(" # openmpi-5.0.3.tar.bz2\n"),
1162+
"{}",
1163+
r.text
1164+
);
1165+
}
1166+
1167+
#[test]
1168+
fn skipping_stops_at_the_first_entry() {
1169+
// Asserting on what the index lands on, rather than on the index,
1170+
// keeps the test about the behaviour instead of about arithmetic.
1171+
fn landed_on(s: &str) -> &str {
1172+
&s[skip_to_first_entry(s.as_bytes(), 0)..]
1173+
}
1174+
assert_eq!(landed_on("{'a': 'b'}"), "{'a': 'b'}");
1175+
assert_eq!(landed_on(" \n {'a': 'b'}"), "{'a': 'b'}");
1176+
assert_eq!(landed_on("\n # note\n 'sha'"), "'sha'");
1177+
assert_eq!(landed_on("\n # one\n\n # two\n 'sha'"), "'sha'");
1178+
// A comment running to the end leaves nothing, which reads as empty.
1179+
assert_eq!(landed_on("\n # trailing"), "");
1180+
assert_eq!(landed_on(""), "");
1181+
}
1182+
10561183
#[test]
10571184
fn version_bump_without_source_checksum_renames_key_and_warns() {
10581185
let params = EmitParams {

0 commit comments

Comments
 (0)