Skip to main content

provenant/parsers/
autotools.rs

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