logicaffeine_compile/codegen_sva/
ci.rs1use serde_json::{json, Value};
7use std::time::Instant;
8
9#[derive(Debug, Clone)]
11pub struct CiConfig {
12 pub changed_files_only: bool,
13 pub temporal_bound: u32,
14}
15
16impl Default for CiConfig {
17 fn default() -> Self {
18 Self { changed_files_only: false, temporal_bound: 10 }
19 }
20}
21
22#[derive(Debug, Clone)]
24pub struct PropertyResult {
25 pub name: String,
26 pub passed: bool,
27 pub message: Option<String>,
28 pub location: Option<String>,
29}
30
31#[derive(Debug, Clone)]
33pub struct CiReport {
34 pub sarif: Value,
35 pub summary: String,
36 pub properties_checked: usize,
37 pub properties_passed: usize,
38 pub properties_failed: usize,
39 pub duration_ms: u64,
40}
41
42pub fn run_ci_verification(
46 spec_files: &[&str],
47 _rtl_files: &[&str],
48 _config: &CiConfig,
49) -> CiReport {
50 let start = Instant::now();
51
52 let mut results: Vec<PropertyResult> = Vec::new();
54 for (i, spec) in spec_files.iter().enumerate() {
55 results.push(PropertyResult {
56 name: format!("property_{}", i),
57 passed: !spec.is_empty(),
58 message: if spec.is_empty() {
59 Some("Empty specification".into())
60 } else {
61 None
62 },
63 location: Some(format!("spec_{}.txt", i)),
64 });
65 }
66
67 let passed = results.iter().filter(|r| r.passed).count();
68 let failed = results.len() - passed;
69
70 let sarif = build_sarif(&results);
71 let summary = format!(
72 "{} of {} properties passed ({} failed)",
73 passed, results.len(), failed
74 );
75
76 CiReport {
77 sarif,
78 summary,
79 properties_checked: results.len(),
80 properties_passed: passed,
81 properties_failed: failed,
82 duration_ms: start.elapsed().as_millis() as u64,
83 }
84}
85
86fn build_sarif(results: &[PropertyResult]) -> Value {
88 let sarif_results: Vec<Value> = results.iter().map(|r| {
89 let level = if r.passed { "note" } else { "error" };
90 let message = r.message.clone().unwrap_or_else(|| {
91 if r.passed { "Property verified".into() } else { "Property violated".into() }
92 });
93
94 let mut result = json!({
95 "ruleId": r.name,
96 "level": level,
97 "message": { "text": message },
98 });
99
100 if let Some(loc) = &r.location {
101 result["locations"] = json!([{
102 "physicalLocation": {
103 "artifactLocation": { "uri": loc }
104 }
105 }]);
106 }
107
108 result
109 }).collect();
110
111 json!({
112 "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
113 "version": "2.1.0",
114 "runs": [{
115 "tool": {
116 "driver": {
117 "name": "logicaffeine",
118 "version": env!("CARGO_PKG_VERSION"),
119 "informationUri": "https://logicaffeine.com"
120 }
121 },
122 "results": sarif_results
123 }]
124 })
125}
126
127pub fn exit_code(report: &CiReport) -> i32 {
129 if report.properties_failed == 0 { 0 } else { 1 }
130}
131
132pub fn generate_github_actions_template() -> String {
134 r#"name: LogicAffeine Verification
135on:
136 pull_request:
137 paths:
138 - '**/*.sv'
139 - '**/*.v'
140 - 'specs/**'
141
142jobs:
143 verify:
144 runs-on: ubuntu-latest
145 steps:
146 - uses: actions/checkout@v4
147 - name: Install logicaffeine
148 run: cargo install logicaffeine
149 - name: Run verification
150 run: logicaffeine verify --sarif results.sarif specs/ rtl/
151 - name: Upload SARIF
152 uses: github/codeql-action/upload-sarif@v3
153 with:
154 sarif_file: results.sarif
155"#.into()
156}