/ build.rs
build.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 use std::{ 20 ffi::OsStr, 21 fs::{self, File}, 22 io::Read, 23 path::Path, 24 }; 25 26 use walkdir::WalkDir; 27 28 // The following license text that should be present at the beginning of every source file. 29 const EXPECTED_LICENSE_TEXT: &[u8] = include_bytes!(".license_header"); 30 31 // The following directories will be excluded from the license scan. 32 const DIRS_TO_SKIP: [&str; 5] = [".cargo", ".circleci", ".git", ".github", "target"]; 33 34 #[derive(Clone, Copy, PartialEq, Eq)] 35 enum ImportOfInterest { 36 Locktick, 37 ParkingLot, 38 Tokio, 39 } 40 41 fn check_locktick_imports<P: AsRef<Path>>(path: P) { 42 let mut iter = WalkDir::new(path).into_iter(); 43 while let Some(entry) = iter.next() { 44 let entry = entry.unwrap(); 45 let entry_type = entry.file_type(); 46 47 // Skip the specified directories. 48 if entry_type.is_dir() && DIRS_TO_SKIP.contains(&entry.file_name().to_str().unwrap_or("")) { 49 iter.skip_current_dir(); 50 51 continue; 52 } 53 54 let path = entry.path(); 55 56 // Ignore non-rs 57 if path.extension() != Some(OsStr::new("rs")) { 58 continue; 59 } 60 61 // Read the entire file. 62 let file = fs::read_to_string(path).unwrap(); 63 64 // Prepare a filtered line iterator. 65 let lines = file 66 .lines() 67 .filter(|l| !l.is_empty()) // Ignore empty lines. 68 .skip_while(|l| !l.starts_with("use")) // Skip the license etc. 69 .take_while(|l| { // Process the section containing import statements. 70 l.starts_with("use") 71 || l.starts_with("#[cfg") 72 || l.starts_with("//") 73 || *l == "};" 74 || l.starts_with(|c: char| c.is_ascii_whitespace()) 75 }); 76 77 // The currently processed import of interest. 78 let mut import_of_interest: Option<ImportOfInterest> = None; 79 // This value not being zero at the end of the imports suggests a missing locktick import. 80 let mut lock_balance: i8 = 0; 81 82 // Process the filtered lines. 83 for line in lines { 84 // Check if this is a lock-related import. 85 if import_of_interest.is_none() { 86 if line.starts_with("use locktick::") { 87 import_of_interest = Some(ImportOfInterest::Locktick); 88 } else if line.starts_with("use parking_lot::") { 89 import_of_interest = Some(ImportOfInterest::ParkingLot); 90 } else if line.starts_with("use tokio::") { 91 import_of_interest = Some(ImportOfInterest::Tokio); 92 } 93 } 94 95 // Skip irrelevant imports. 96 let Some(ioi) = import_of_interest else { 97 continue; 98 }; 99 100 // Modify the lock balance based on the type of the relevant import. 101 if [ImportOfInterest::ParkingLot, ImportOfInterest::Tokio].contains(&ioi) { 102 if line.contains("Mutex") { 103 lock_balance += 1; 104 } 105 if line.contains("RwLock") { 106 lock_balance += 1; 107 } 108 } else if ioi == ImportOfInterest::Locktick { 109 // Use `matches` instead of just `contains` here, as more than a single 110 // lock type entry is possible in a locktick import. 111 for _hit in line.matches("Mutex") { 112 lock_balance -= 1; 113 } 114 for _hit in line.matches("RwLock") { 115 lock_balance -= 1; 116 } 117 // A correction in case of the `use tokio::Mutex as TMutex` convention. 118 if line.contains("TMutex") { 119 lock_balance += 1; 120 } 121 } 122 123 // Register the end of an import statement. 124 if line.ends_with(";") { 125 import_of_interest = None; 126 } 127 } 128 129 // If the file has a lock import "imbalance", print it out and increment the counter. 130 assert!( 131 lock_balance == 0, 132 "The locks in \"{}\" don't seem to have `locktick` counterparts!", 133 entry.path().display() 134 ); 135 } 136 } 137 138 fn check_file_licenses<P: AsRef<Path>>(path: P) { 139 let path = path.as_ref(); 140 141 let mut iter = WalkDir::new(path).into_iter(); 142 while let Some(entry) = iter.next() { 143 let entry = entry.unwrap(); 144 let entry_type = entry.file_type(); 145 146 // Skip the specified directories. 147 if entry_type.is_dir() && DIRS_TO_SKIP.contains(&entry.file_name().to_str().unwrap_or("")) { 148 iter.skip_current_dir(); 149 150 continue; 151 } 152 153 // Check all files with the ".rs" extension. 154 if entry_type.is_file() && entry.file_name().to_str().unwrap_or("").ends_with(".rs") { 155 let file = File::open(entry.path()).unwrap(); 156 let mut contents = Vec::with_capacity(EXPECTED_LICENSE_TEXT.len()); 157 file.take(EXPECTED_LICENSE_TEXT.len() as u64).read_to_end(&mut contents).unwrap(); 158 159 assert!( 160 contents == EXPECTED_LICENSE_TEXT, 161 "The license in \"{}\" is either missing or it doesn't match the expected string!", 162 entry.path().display() 163 ); 164 } 165 } 166 } 167 168 // The build script; it currently only checks the licenses. 169 fn main() { 170 // Check licenses in the current folder. 171 check_file_licenses("."); 172 // Ensure that lock imports have locktick counterparts. 173 check_locktick_imports("."); 174 }