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    "configure script for ",
39];
40
41fn looks_like_autoconf_generated_configure(path: &Path) -> bool {
42    fs::read(path)
43        .ok()
44        .map(|content| {
45            String::from_utf8_lossy(&content)
46                .lines()
47                .take(250)
48                .map(|line| line.trim().to_ascii_lowercase())
49                .any(|line| {
50                    AUTOCONF_CONFIGURE_MARKERS
51                        .iter()
52                        .any(|marker| line.contains(marker))
53                })
54        })
55        .unwrap_or(false)
56}
57
58impl PackageParser for AutotoolsConfigureParser {
59    const PACKAGE_TYPE: PackageType = PackageType::Autotools;
60
61    fn is_match(path: &Path) -> bool {
62        match path.file_name().and_then(|name| name.to_str()) {
63            Some("configure.ac") => true,
64            Some("configure") => looks_like_autoconf_generated_configure(path),
65            _ => false,
66        }
67    }
68
69    fn extract_packages(path: &Path) -> Vec<PackageData> {
70        let name = path
71            .parent()
72            .and_then(|p| p.file_name())
73            .and_then(|n| n.to_str())
74            .map(|s| s.to_string())
75            .or_else(|| {
76                path.file_name()
77                    .is_some_and(|name| name == "configure" || name == "configure.ac")
78                    .then_some("input".to_string())
79            });
80
81        let purl = name.as_deref().and_then(|name| {
82            PackageUrl::new(Self::PACKAGE_TYPE.as_str(), name)
83                .ok()
84                .map(|purl| purl.to_string())
85        });
86
87        vec![PackageData {
88            package_type: Some(Self::PACKAGE_TYPE),
89            name,
90            datasource_id: Some(DatasourceId::AutotoolsConfigure),
91            purl,
92            ..Default::default()
93        }]
94    }
95
96    fn metadata() -> Vec<super::metadata::ParserMetadata> {
97        vec![super::metadata::ParserMetadata {
98            description: "Autotools configure script",
99            file_patterns: &["**/configure", "**/configure.ac"],
100            package_type: "autotools",
101            primary_language: "C",
102            documentation_url: Some("https://www.gnu.org/software/autoconf/"),
103        }]
104    }
105}