1use makefile_lossless::{Makefile, Rule};
4
5pub fn dh_invoke_add_with(line: &str, with_argument: &str) -> String {
7 if line.contains(with_argument) {
8 return line.to_owned();
9 }
10 if !line.contains(" --with") {
11 return format!("{} --with={}", line, with_argument);
12 }
13
14 lazy_regex::regex_replace!(
15 r"([ \t])--with([ =])([^ \t]+)",
16 line,
17 |_, head, _with, tail| format!("{}--with={},{}", head, with_argument, tail)
18 )
19 .to_string()
20}
21
22pub fn dh_invoke_get_with(line: &str) -> Vec<String> {
24 let mut ret = Vec::new();
25 for cap in lazy_regex::regex!("[ \t]--with[ =]([^ \t]+)").captures_iter(line) {
26 if let Some(m) = cap.get(1) {
27 ret.extend(m.as_str().split(',').map(|s| s.to_owned()));
28 }
29 }
30 ret
31}
32
33pub fn dh_invoke_drop_with(line: &str, with_argument: &str) -> String {
55 if !line.contains(with_argument) {
56 return line.to_owned();
57 }
58
59 let mut result = line.to_owned();
60 let escaped = regex::escape(with_argument);
61
62 if let Ok(re) = regex::Regex::new(&format!(r"[ \t]--with[ =]{}( .+|)$", escaped)) {
64 result = re.replace(&result, "$1").to_string();
65 }
66
67 if let Ok(re) = regex::Regex::new(&format!(r"([ \t])--with([ =]){},", escaped)) {
69 result = re.replace(&result, "${1}--with${2}").to_string();
70 }
71
72 if let Ok(re) = regex::Regex::new(&format!(r"([ \t])--with([ =])(.+),{}([ ,])", escaped)) {
74 result = re.replace(&result, "${1}--with${2}${3}${4}").to_string();
75 }
76
77 if let Ok(re) = regex::Regex::new(&format!(r"([ \t])--with([ =])(.+),{}$", escaped)) {
79 result = re.replace(&result, "${1}--with${2}${3}").to_string();
80 }
81
82 result
83}
84
85pub fn dh_invoke_drop_argument(line: &str, argument: &str) -> String {
103 if !line.contains(argument) {
104 return line.to_owned();
105 }
106
107 let mut result = line.to_owned();
108 let escaped = regex::escape(argument);
109
110 if let Ok(re) = regex::Regex::new(&format!(r"[ \t]+{}$", escaped)) {
112 result = re.replace(&result, "").to_string();
113 }
114
115 if let Ok(re) = regex::Regex::new(&format!(r"([ \t]){}[ \t]", escaped)) {
117 result = re.replace(&result, "$1").to_string();
118 }
119
120 result
121}
122
123pub fn dh_invoke_replace_argument(line: &str, old: &str, new: &str) -> String {
142 if !line.contains(old) {
143 return line.to_owned();
144 }
145
146 let mut result = line.to_owned();
147 let escaped = regex::escape(old);
148
149 if let Ok(re) = regex::Regex::new(&format!(r"([ \t]){}$", escaped)) {
151 result = re.replace(&result, format!("$1{}", new)).to_string();
152 }
153
154 if let Ok(re) = regex::Regex::new(&format!(r"([ \t]){}([ \t])", escaped)) {
156 result = re.replace(&result, format!("$1{}$2", new)).to_string();
157 }
158
159 result
160}
161
162pub fn check_cdbs(path: &std::path::Path) -> bool {
177 let Ok(contents) = std::fs::read(path) else {
178 return false;
179 };
180
181 for line in contents.split(|&b| b == b'\n') {
182 let trimmed = line.strip_prefix(b"-").unwrap_or(line);
183 if trimmed.starts_with(b"include /usr/share/cdbs/") {
184 return true;
185 }
186 }
187 false
188}
189
190pub fn discard_pointless_override(makefile: &mut Makefile, rule: &Rule) -> bool {
209 let Some(target) = rule.targets().find(|t| t.starts_with("override_")) else {
211 return false;
212 };
213
214 let command = &target["override_".len()..];
216
217 let effective_recipes: Vec<String> = rule
219 .recipes()
220 .filter(|line| !line.trim().is_empty())
221 .collect();
222
223 if effective_recipes.len() != 1 || effective_recipes[0].trim() != command {
225 return false;
226 }
227
228 if rule.prerequisites().next().is_some() {
230 return false;
231 }
232
233 let _ = makefile.remove_phony_target(&target);
235 rule.clone().remove().is_ok()
236}
237
238pub fn discard_pointless_overrides(makefile: &mut Makefile) -> usize {
246 let mut removed = 0;
247
248 let rules: Vec<Rule> = makefile.rules().collect();
250
251 for rule in rules {
252 if discard_pointless_override(makefile, &rule) {
253 removed += 1;
254 }
255 }
256
257 removed
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn test_dh_invoke_add_with() {
266 assert_eq!(dh_invoke_add_with("dh", "blah"), "dh --with=blah");
267 assert_eq!(
268 dh_invoke_add_with("dh --with=foo", "blah"),
269 "dh --with=blah,foo"
270 );
271 assert_eq!(
272 dh_invoke_add_with("dh --with=foo --other", "blah"),
273 "dh --with=blah,foo --other"
274 );
275 }
276
277 #[test]
278 fn test_dh_invoke_get_with() {
279 assert_eq!(dh_invoke_get_with("dh --with=blah --foo"), vec!["blah"]);
280 assert_eq!(dh_invoke_get_with("dh --with=blah"), vec!["blah"]);
281 assert_eq!(
282 dh_invoke_get_with("dh --with=blah,blie"),
283 vec!["blah", "blie"]
284 );
285 assert_eq!(dh_invoke_get_with("dh $@ --with python3"), vec!["python3"]);
288 assert_eq!(
290 dh_invoke_get_with("dh $@ --with foo,bar,baz"),
291 vec!["foo", "bar", "baz"]
292 );
293 }
294
295 #[test]
296 fn test_dh_invoke_drop_with() {
297 assert_eq!(dh_invoke_drop_with("dh --with=blah", "blah"), "dh");
298 assert_eq!(
299 dh_invoke_drop_with("dh --with=blah,foo", "blah"),
300 "dh --with=foo"
301 );
302 assert_eq!(
303 dh_invoke_drop_with("dh --with=blah,foo --other", "blah"),
304 "dh --with=foo --other"
305 );
306 assert_eq!(dh_invoke_drop_with("dh --with=blah", "blah"), "dh");
307 assert_eq!(
308 dh_invoke_drop_with("dh --with=foo,blah", "blah"),
309 "dh --with=foo"
310 );
311 assert_eq!(
312 dh_invoke_drop_with(
313 "dh $@ --verbose --with autoreconf,systemd,cme-upgrade",
314 "systemd"
315 ),
316 "dh $@ --verbose --with autoreconf,cme-upgrade"
317 );
318 assert_eq!(
319 dh_invoke_drop_with(
320 "dh $@ --with gir,python3,sphinxdoc,systemd --without autoreconf --buildsystem=cmake",
321 "systemd"
322 ),
323 "dh $@ --with gir,python3,sphinxdoc --without autoreconf --buildsystem=cmake"
324 );
325 assert_eq!(
326 dh_invoke_drop_with("dh $@ --with systemd", "systemd"),
327 "dh $@"
328 );
329 }
330
331 #[test]
332 fn test_dh_invoke_drop_argument() {
333 assert_eq!(
334 dh_invoke_drop_argument("dh $@ --foo --bar", "--foo"),
335 "dh $@ --bar"
336 );
337 assert_eq!(
338 dh_invoke_drop_argument("dh $@ --foo --bar", "--bar"),
339 "dh $@ --foo"
340 );
341 assert_eq!(dh_invoke_drop_argument("dh $@ --foo", "--foo"), "dh $@");
342 }
343
344 #[test]
345 fn test_dh_invoke_replace_argument() {
346 assert_eq!(
347 dh_invoke_replace_argument("dh $@ --foo", "--foo", "--bar"),
348 "dh $@ --bar"
349 );
350 assert_eq!(
351 dh_invoke_replace_argument("dh $@ --foo --baz", "--foo", "--bar"),
352 "dh $@ --bar --baz"
353 );
354 }
355
356 #[test]
357 fn test_discard_pointless_override() {
358 let makefile_text = r#"
360override_dh_auto_build:
361 dh_auto_build
362"#;
363 let mut makefile = makefile_text.parse::<Makefile>().unwrap();
364 let rules: Vec<Rule> = makefile.rules().collect();
365 assert_eq!(rules.len(), 1);
366
367 let removed = discard_pointless_override(&mut makefile, &rules[0]);
368 assert!(removed, "Should have removed the pointless override");
369
370 let remaining_rules: Vec<Rule> = makefile.rules().collect();
371 assert_eq!(remaining_rules.len(), 0, "Rule should be removed");
372 }
373
374 #[test]
375 fn test_discard_pointless_override_with_args() {
376 let makefile_text = r#"
378override_dh_auto_build:
379 dh_auto_build --foo
380"#;
381 let mut makefile = makefile_text.parse::<Makefile>().unwrap();
382 let rules: Vec<Rule> = makefile.rules().collect();
383 assert_eq!(rules.len(), 1);
384
385 let removed = discard_pointless_override(&mut makefile, &rules[0]);
386 assert!(!removed, "Should NOT remove override with arguments");
387
388 let remaining_rules: Vec<Rule> = makefile.rules().collect();
389 assert_eq!(remaining_rules.len(), 1, "Rule should remain");
390 }
391
392 #[test]
393 fn test_discard_pointless_override_with_comment() {
394 let makefile_text = r#"
397override_dh_auto_build:
398 # This is a comment
399 dh_auto_build
400"#;
401 let mut makefile = makefile_text.parse::<Makefile>().unwrap();
402 let rules: Vec<Rule> = makefile.rules().collect();
403 assert_eq!(rules.len(), 1);
404
405 let removed = discard_pointless_override(&mut makefile, &rules[0]);
407 assert!(
408 removed,
409 "Should remove - recipes() doesn't include comments"
410 );
411 }
412
413 #[test]
414 fn test_discard_pointless_override_not_override() {
415 let makefile_text = r#"
417build:
418 dh_auto_build
419"#;
420 let mut makefile = makefile_text.parse::<Makefile>().unwrap();
421 let rules: Vec<Rule> = makefile.rules().collect();
422 assert_eq!(rules.len(), 1);
423
424 let removed = discard_pointless_override(&mut makefile, &rules[0]);
425 assert!(!removed, "Should NOT remove non-override rules");
426 }
427
428 #[test]
429 fn test_discard_pointless_overrides() {
430 let makefile_text = r#"
432override_dh_auto_build:
433 dh_auto_build
434
435override_dh_auto_test:
436 dh_auto_test
437
438override_dh_auto_install:
439 dh_auto_install --foo
440"#;
441 let mut makefile = makefile_text.parse::<Makefile>().unwrap();
442 let initial_rules = makefile.rules().count();
443 assert_eq!(initial_rules, 3);
444
445 let removed = discard_pointless_overrides(&mut makefile);
446 assert_eq!(removed, 2, "Should remove 2 pointless overrides");
447
448 let remaining_rules: Vec<Rule> = makefile.rules().collect();
449 assert_eq!(remaining_rules.len(), 1, "Should have 1 rule remaining");
450
451 let targets: Vec<String> = remaining_rules[0].targets().collect();
453 assert_eq!(targets, vec!["override_dh_auto_install"]);
454 }
455}