Skip to main content

provenant/parsers/
autotools.rs

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