Skip to content

Commit f9dcaca

Browse files
fix: Improve compatibility_check_once
- Export the symbol even when `export-globals` is enabled - Make `check_compatibility` (behind the `Once`) non-pub
1 parent a7eacd5 commit f9dcaca

File tree

1 file changed

+154
-151
lines changed

1 file changed

+154
-151
lines changed

rubicon/src/lib.rs

Lines changed: 154 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,10 @@ macro_rules! compatibility_check {
452452
$($feature)*
453453
];
454454
}
455+
456+
pub fn compatibility_check_once() {
457+
// no-op when exporting
458+
}
455459
};
456460
}
457461

@@ -539,199 +543,198 @@ macro_rules! compatibility_check {
539543
len
540544
}
541545

542-
// this one is _actually_ meant to exist once per shared object
543-
static COMPATIBILITY_CHECK_ONCE: std::sync::Once = std::sync::Once::new();
544-
545546
pub fn compatibility_check_once() {
546-
COMPATIBILITY_CHECK_ONCE.call_once(|| {
547-
check_compatibility();
548-
});
549-
}
550-
551-
pub fn check_compatibility() {
552-
let imported: &[(&str, &str)] = &[
553-
("rustc-version", $crate::RUBICON_RUSTC_VERSION),
554-
("target-triple", $crate::RUBICON_TARGET_TRIPLE),
555-
$($feature)*
556-
];
557-
let exported = unsafe { COMPATIBILITY_INFO };
558-
559-
let missing: Vec<_> = imported.iter().filter(|&item| !exported.contains(item)).collect();
560-
let extra: Vec<_> = exported.iter().filter(|&item| !imported.contains(item)).collect();
561-
562-
if missing.is_empty() && extra.is_empty() {
563-
// all good
564-
return;
565-
}
566-
567-
let so_name = get_shared_object_name().unwrap_or("unknown_so".to_string());
568-
// get only the last bit of the path
569-
let so_name = so_name.rsplit('/').next().unwrap_or("unknown_so");
570-
571-
let exe_name = std::env::current_exe().map(|p| p.file_name().unwrap().to_string_lossy().to_string()).unwrap_or_else(|_| "unknown_exe".to_string());
547+
fn check_compatibility() {
548+
let imported: &[(&str, &str)] = &[
549+
("rustc-version", $crate::RUBICON_RUSTC_VERSION),
550+
("target-triple", $crate::RUBICON_TARGET_TRIPLE),
551+
$($feature)*
552+
];
553+
let exported = unsafe { COMPATIBILITY_INFO };
554+
555+
let missing: Vec<_> = imported.iter().filter(|&item| !exported.contains(item)).collect();
556+
let extra: Vec<_> = exported.iter().filter(|&item| !imported.contains(item)).collect();
557+
558+
if missing.is_empty() && extra.is_empty() {
559+
// all good
560+
return;
561+
}
572562

573-
let mut error_message = String::new();
574-
error_message.push_str("\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n");
575-
error_message.push_str(&format!(" 💀 Feature mismatch for crate \x1b[31m{}\x1b[0m\n\n", env!("CARGO_PKG_NAME")));
563+
let so_name = get_shared_object_name().unwrap_or("unknown_so".to_string());
564+
// get only the last bit of the path
565+
let so_name = so_name.rsplit('/').next().unwrap_or("unknown_so");
576566

577-
error_message.push_str(&format!("{} has an incompatible configuration for {}.\n\n", blue(so_name), red(env!("CARGO_PKG_NAME"))));
567+
let exe_name = std::env::current_exe().map(|p| p.file_name().unwrap().to_string_lossy().to_string()).unwrap_or_else(|_| "unknown_exe".to_string());
578568

579-
// Compute max lengths for alignment
580-
let max_exported_len = exported.iter().map(|(k, v)| format!("{}={}", k, v).len()).max().unwrap_or(0);
581-
let max_ref_len = imported.iter().map(|(k, v)| format!("{}={}", k, v).len()).max().unwrap_or(0);
582-
let column_width = max_exported_len.max(max_ref_len);
569+
let mut error_message = String::new();
570+
error_message.push_str("\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n");
571+
error_message.push_str(&format!(" 💀 Feature mismatch for crate \x1b[31m{}\x1b[0m\n\n", env!("CARGO_PKG_NAME")));
583572

584-
// Gather all unique keys
585-
let mut all_keys: Vec<&str> = Vec::new();
586-
for (key, _) in exported.iter() {
587-
if !all_keys.contains(key) {
588-
all_keys.push(key);
589-
}
590-
}
591-
for (key, _) in imported.iter() {
592-
if !all_keys.contains(key) {
593-
all_keys.push(key);
594-
}
595-
}
573+
error_message.push_str(&format!("{} has an incompatible configuration for {}.\n\n", blue(so_name), red(env!("CARGO_PKG_NAME"))));
596574

597-
struct Grid {
598-
rows: Vec<Vec<String>>,
599-
column_widths: Vec<usize>,
600-
}
575+
// Compute max lengths for alignment
576+
let max_exported_len = exported.iter().map(|(k, v)| format!("{}={}", k, v).len()).max().unwrap_or(0);
577+
let max_ref_len = imported.iter().map(|(k, v)| format!("{}={}", k, v).len()).max().unwrap_or(0);
578+
let column_width = max_exported_len.max(max_ref_len);
601579

602-
impl Grid {
603-
fn new() -> Self {
604-
Grid {
605-
rows: Vec::new(),
606-
column_widths: Vec::new(),
580+
// Gather all unique keys
581+
let mut all_keys: Vec<&str> = Vec::new();
582+
for (key, _) in exported.iter() {
583+
if !all_keys.contains(key) {
584+
all_keys.push(key);
607585
}
608586
}
609-
610-
fn add_row(&mut self, row: Vec<String>) {
611-
if self.column_widths.len() < row.len() {
612-
self.column_widths.resize(row.len(), 0);
613-
}
614-
for (i, cell) in row.iter().enumerate() {
615-
self.column_widths[i] = self.column_widths[i].max(visible_len(cell));
587+
for (key, _) in imported.iter() {
588+
if !all_keys.contains(key) {
589+
all_keys.push(key);
616590
}
617-
self.rows.push(row);
618591
}
619592

620-
fn write_to(&self, out: &mut String) {
621-
let total_width: usize = self.column_widths.iter().sum::<usize>() + self.column_widths.len() * 3 - 1;
622-
623-
// Top border
624-
out.push_str(&format!("┌{}┐\n", "─".repeat(total_width)));
593+
struct Grid {
594+
rows: Vec<Vec<String>>,
595+
column_widths: Vec<usize>,
596+
}
625597

626-
for (i, row) in self.rows.iter().enumerate() {
627-
if i == 1 {
628-
// Separator after header
629-
out.push_str(&format!("╞{}╡\n", "═".repeat(total_width)));
598+
impl Grid {
599+
fn new() -> Self {
600+
Grid {
601+
rows: Vec::new(),
602+
column_widths: Vec::new(),
630603
}
604+
}
631605

632-
for (j, cell) in row.iter().enumerate() {
633-
out.push_str("│ ");
634-
out.push_str(cell);
635-
out.push_str(&" ".repeat(self.column_widths[j] - visible_len(cell)));
636-
out.push_str(" ");
606+
fn add_row(&mut self, row: Vec<String>) {
607+
if self.column_widths.len() < row.len() {
608+
self.column_widths.resize(row.len(), 0);
609+
}
610+
for (i, cell) in row.iter().enumerate() {
611+
self.column_widths[i] = self.column_widths[i].max(visible_len(cell));
637612
}
638-
out.push_str("│\n");
613+
self.rows.push(row);
639614
}
640615

641-
// Bottom border
642-
out.push_str(&format!("└{}┘\n", "─".repeat(total_width)));
643-
}
644-
}
645-
646-
let mut grid = Grid::new();
616+
fn write_to(&self, out: &mut String) {
617+
let total_width: usize = self.column_widths.iter().sum::<usize>() + self.column_widths.len() * 3 - 1;
647618

648-
// Add header
649-
grid.add_row(vec!["Key".to_string(), format!("Binary {}", blue(&exe_name)), format!("Module {}", blue(so_name))]);
619+
// Top border
620+
out.push_str(&format!("┌{}┐\n", "─".repeat(total_width)));
650621

651-
for key in all_keys.iter() {
652-
let exported_value = exported.iter().find(|&(k, _)| k == key).map(|(_, v)| v);
653-
let imported_value = imported.iter().find(|&(k, _)| k == key).map(|(_, v)| v);
654-
655-
let key_column = colored(AnsiColor::GREY, key).to_string();
656-
let binary_column = format_column(exported_value.as_deref().copied(), imported_value.as_deref().copied(), AnsiColor::GREEN);
657-
let module_column = format_column(imported_value.as_deref().copied(), exported_value.as_deref().copied(), AnsiColor::RED);
622+
for (i, row) in self.rows.iter().enumerate() {
623+
if i == 1 {
624+
// Separator after header
625+
out.push_str(&format!("╞{}╡\n", "═".repeat(total_width)));
626+
}
658627

659-
fn format_column(primary: Option<&str>, secondary: Option<&str>, highlight_color: AnsiColor) -> String {
660-
match primary {
661-
Some(value) => {
662-
if secondary.map_or(false, |v| v == value) {
663-
colored(AnsiColor::GREY, value).to_string()
664-
} else {
665-
colored(highlight_color, value).to_string()
628+
for (j, cell) in row.iter().enumerate() {
629+
out.push_str("│ ");
630+
out.push_str(cell);
631+
out.push_str(&" ".repeat(self.column_widths[j] - visible_len(cell)));
632+
out.push_str(" ");
666633
}
667-
},
668-
None => colored(AnsiColor::RED, "∅").to_string(),
634+
out.push_str("│\n");
635+
}
636+
637+
// Bottom border
638+
out.push_str(&format!("└{}┘\n", "─".repeat(total_width)));
669639
}
670640
}
671641

672-
grid.add_row(vec![key_column, binary_column, module_column]);
673-
}
642+
let mut grid = Grid::new();
643+
644+
// Add header
645+
grid.add_row(vec!["Key".to_string(), format!("Binary {}", blue(&exe_name)), format!("Module {}", blue(so_name))]);
646+
647+
for key in all_keys.iter() {
648+
let exported_value = exported.iter().find(|&(k, _)| k == key).map(|(_, v)| v);
649+
let imported_value = imported.iter().find(|&(k, _)| k == key).map(|(_, v)| v);
650+
651+
let key_column = colored(AnsiColor::GREY, key).to_string();
652+
let binary_column = format_column(exported_value.as_deref().copied(), imported_value.as_deref().copied(), AnsiColor::GREEN);
653+
let module_column = format_column(imported_value.as_deref().copied(), exported_value.as_deref().copied(), AnsiColor::RED);
654+
655+
fn format_column(primary: Option<&str>, secondary: Option<&str>, highlight_color: AnsiColor) -> String {
656+
match primary {
657+
Some(value) => {
658+
if secondary.map_or(false, |v| v == value) {
659+
colored(AnsiColor::GREY, value).to_string()
660+
} else {
661+
colored(highlight_color, value).to_string()
662+
}
663+
},
664+
None => colored(AnsiColor::RED, "∅").to_string(),
665+
}
666+
}
674667

675-
grid.write_to(&mut error_message);
668+
grid.add_row(vec![key_column, binary_column, module_column]);
669+
}
676670

677-
struct MessageBox {
678-
lines: Vec<String>,
679-
max_width: usize,
680-
}
671+
grid.write_to(&mut error_message);
681672

682-
impl MessageBox {
683-
fn new() -> Self {
684-
MessageBox {
685-
lines: Vec::new(),
686-
max_width: 0,
687-
}
673+
struct MessageBox {
674+
lines: Vec<String>,
675+
max_width: usize,
688676
}
689677

690-
fn add_line(&mut self, line: String) {
691-
self.max_width = self.max_width.max(visible_len(&line));
692-
self.lines.push(line);
693-
}
678+
impl MessageBox {
679+
fn new() -> Self {
680+
MessageBox {
681+
lines: Vec::new(),
682+
max_width: 0,
683+
}
684+
}
694685

695-
fn add_empty_line(&mut self) {
696-
self.lines.push(String::new());
697-
}
686+
fn add_line(&mut self, line: String) {
687+
self.max_width = self.max_width.max(visible_len(&line));
688+
self.lines.push(line);
689+
}
690+
691+
fn add_empty_line(&mut self) {
692+
self.lines.push(String::new());
693+
}
698694

699-
fn write_to(&self, out: &mut String) {
700-
let box_width = self.max_width + 4;
695+
fn write_to(&self, out: &mut String) {
696+
let box_width = self.max_width + 4;
701697

702-
out.push_str("\n");
703-
out.push_str(&format!("┌{}┐\n", "─".repeat(box_width - 2)));
698+
out.push_str("\n");
699+
out.push_str(&format!("┌{}┐\n", "─".repeat(box_width - 2)));
704700

705-
for line in &self.lines {
706-
if line.is_empty() {
707-
out.push_str(&format!("│{}│\n", " ".repeat(box_width - 2)));
708-
} else {
709-
let visible_line_len = visible_len(line);
710-
let padding = " ".repeat(box_width - 4 - visible_line_len);
711-
out.push_str(&format!("│ {}{} │\n", line, padding));
701+
for line in &self.lines {
702+
if line.is_empty() {
703+
out.push_str(&format!("│{}│\n", " ".repeat(box_width - 2)));
704+
} else {
705+
let visible_line_len = visible_len(line);
706+
let padding = " ".repeat(box_width - 4 - visible_line_len);
707+
out.push_str(&format!("│ {}{} │\n", line, padding));
708+
}
712709
}
713-
}
714710

715-
out.push_str(&format!("└{}┘", "─".repeat(box_width - 2)));
711+
out.push_str(&format!("└{}┘", "─".repeat(box_width - 2)));
712+
}
716713
}
717-
}
718714

719-
error_message.push_str("\nDifferent feature sets may result in different struct layouts, which\n");
720-
error_message.push_str("would lead to memory corruption. Instead, we're going to panic now.\n\n");
715+
error_message.push_str("\nDifferent feature sets may result in different struct layouts, which\n");
716+
error_message.push_str("would lead to memory corruption. Instead, we're going to panic now.\n\n");
721717

722-
error_message.push_str("More info: \x1b[4m\x1b[34mhttps://crates.io/crates/rubicon\x1b[0m\n");
718+
error_message.push_str("More info: \x1b[4m\x1b[34mhttps://crates.io/crates/rubicon\x1b[0m\n");
723719

724-
let mut message_box = MessageBox::new();
725-
message_box.add_line(format!("To fix this issue, {} needs to enable", blue(so_name)));
726-
message_box.add_line(format!("the same cargo features as {} for crate {}.", blue(&exe_name), red(env!("CARGO_PKG_NAME"))));
727-
message_box.add_empty_line();
728-
message_box.add_line("\x1b[34mHINT:\x1b[0m".to_string());
729-
message_box.add_line(format!("Run `cargo tree -i {} -e features` from both.", red(env!("CARGO_PKG_NAME"))));
720+
let mut message_box = MessageBox::new();
721+
message_box.add_line(format!("To fix this issue, {} needs to enable", blue(so_name)));
722+
message_box.add_line(format!("the same cargo features as {} for crate {}.", blue(&exe_name), red(env!("CARGO_PKG_NAME"))));
723+
message_box.add_empty_line();
724+
message_box.add_line("\x1b[34mHINT:\x1b[0m".to_string());
725+
message_box.add_line(format!("Run `cargo tree -i {} -e features` from both.", red(env!("CARGO_PKG_NAME"))));
730726

731-
message_box.write_to(&mut error_message);
732-
error_message.push_str("\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n");
727+
message_box.write_to(&mut error_message);
728+
error_message.push_str("\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n");
729+
730+
panic!("{}", error_message);
731+
}
733732

734-
panic!("{}", error_message);
733+
// this one is _actually_ meant to exist once per shared object
734+
static COMPATIBILITY_CHECK_ONCE: std::sync::Once = std::sync::Once::new();
735+
COMPATIBILITY_CHECK_ONCE.call_once(|| {
736+
check_compatibility();
737+
});
735738
}
736739
};
737740
}

0 commit comments

Comments
 (0)