Skip to main content

provenant/parsers/
autotools.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Parser for Autotools configure scripts.
5//!
6//! Extracts basic package metadata from Autotools configure files by using
7//! the parent directory name as the package name.
8//!
9//! # Supported Formats
10//! - configure (Autotools configure script)
11//! - configure.ac (Autoconf input file)
12//!
13//! # Key Features
14//! - Lightweight detection based on parent directory name
15//! - `configure.ac` is path-based; `configure` requires autoconf-generated markers
16//!
17//! # Implementation Notes
18//! - configure.in is NOT supported (deprecated legacy format)
19//! - Returns minimal PackageData with only package_type and name fields
20
21use crate::models::PackageData;
22use crate::models::{DatasourceId, PackageType};
23use packageurl::PackageUrl;
24use std::fs;
25use std::path::Path;
26
27use super::PackageParser;
28
29/// Parser for Autotools configure scripts.
30///
31/// Extracts the parent directory name as the package name.
32pub struct AutotoolsConfigureParser;
33
34const AUTOCONF_CONFIGURE_MARKERS: &[&str] = &[
35    "generated by gnu autoconf",
36    "generated automatically using autoconf",
37    "please tell bug-autoconf@gnu.org",
38];
39
40fn looks_like_autoconf_generated_configure(path: &Path) -> bool {
41    fs::read(path)
42        .ok()
43        .map(|content| {
44            String::from_utf8_lossy(&content)
45                .lines()
46                .take(250)
47                .map(|line| line.trim().to_ascii_lowercase())
48                .any(|line| {
49                    AUTOCONF_CONFIGURE_MARKERS
50                        .iter()
51                        .any(|marker| line.contains(marker))
52                })
53        })
54        .unwrap_or(false)
55}
56
57impl PackageParser for AutotoolsConfigureParser {
58    const PACKAGE_TYPE: PackageType = PackageType::Autotools;
59
60    fn is_match(path: &Path) -> bool {
61        match path.file_name().and_then(|name| name.to_str()) {
62            Some("configure.ac") => true,
63            Some("configure") => looks_like_autoconf_generated_configure(path),
64            _ => false,
65        }
66    }
67
68    fn extract_packages(path: &Path) -> Vec<PackageData> {
69        let name = path
70            .parent()
71            .and_then(|p| p.file_name())
72            .and_then(|n| n.to_str())
73            .map(|s| s.to_string())
74            .or_else(|| {
75                path.file_name()
76                    .is_some_and(|name| name == "configure" || name == "configure.ac")
77                    .then_some("input".to_string())
78            });
79
80        let purl = name.as_deref().and_then(|name| {
81            PackageUrl::new(Self::PACKAGE_TYPE.as_str(), name)
82                .ok()
83                .map(|purl| purl.to_string())
84        });
85
86        vec![PackageData {
87            package_type: Some(Self::PACKAGE_TYPE),
88            name,
89            datasource_id: Some(DatasourceId::AutotoolsConfigure),
90            purl,
91            ..Default::default()
92        }]
93    }
94}
95
96crate::register_parser!(
97    "Autotools configure script",
98    &["**/configure", "**/configure.ac"],
99    "autotools",
100    "C",
101    Some("https://www.gnu.org/software/autoconf/"),
102);