Skip to main content

makefile_lossless/
lib.rs

1#![allow(clippy::tabs_in_doc_comments)] // Makefile uses tabs
2#![deny(missing_docs)]
3
4//! A lossless parser for Makefiles
5//!
6//! Example:
7//!
8//! ```rust
9//! use std::io::Read;
10//! let contents = r#"PYTHON = python3
11//!
12//! .PHONY: all
13//!
14//! all: build
15//!
16//! build:
17//! 	$(PYTHON) setup.py build
18//! "#;
19//! let makefile: makefile_lossless::Makefile = contents.parse().unwrap();
20//!
21//! assert_eq!(makefile.rules().count(), 3);
22//! ```
23
24mod ast;
25mod incremental;
26mod lex;
27mod lossless;
28mod parse;
29mod pattern;
30mod text;
31
32pub use ast::makefile::MakefileItem;
33pub use ast::rule::RuleItem;
34pub use incremental::{apply_edit_to_text, TextEdit};
35pub use lossless::{
36    ArchiveMember, ArchiveMembers, Conditional, Error, ErrorInfo, Identifier, Include, Lang,
37    Makefile, ParseError, PositionedParseError, Rule, VariableDefinition, VariableReference,
38};
39pub use parse::Parse;
40pub use rowan::TextRange;
41pub use text::{is_in_prerequisites, variable_at_offset, word_at_offset};
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44/// The variant of makefile being parsed
45pub enum MakefileVariant {
46    /// GNU Make (most common, supports ifeq/ifneq/ifdef/ifndef conditionals, pattern rules, etc.)
47    GNUMake,
48    /// BSD Make (FreeBSD, NetBSD, OpenBSD - uses .if/.ifdef/.ifndef directives)
49    BSDMake,
50    /// Microsoft nmake (Windows - uses !IF/!IFDEF/!IFNDEF directives)
51    NMake,
52    /// POSIX-compliant make (basic portable subset, no extensions)
53    POSIXMake,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
57#[allow(non_camel_case_types)]
58#[repr(u16)]
59#[allow(missing_docs)]
60pub enum SyntaxKind {
61    IDENTIFIER = 0,
62    INDENT,
63    TEXT,
64    WHITESPACE,
65    NEWLINE,
66    DOLLAR,
67    LPAREN,
68    RPAREN,
69    LBRACE,
70    RBRACE,
71    QUOTE,
72    BACKSLASH,
73    COMMA,
74    OPERATOR,
75
76    COMMENT,
77    ERROR,
78
79    // composite nodes
80    ROOT,          // The entire file
81    RULE,          // A single rule
82    RECIPE,        // A command/recipe line
83    VARIABLE,      // A variable definition
84    EXPR,          // An expression (e.g., targets before colon, or old-style prerequisites)
85    TARGETS,       // Container for targets before the colon
86    PREREQUISITES, // Container for prerequisites after the colon
87    PREREQUISITE,  // A single prerequisite item
88
89    // Directives
90    CONDITIONAL,       // The entire conditional block (ifdef...endif)
91    CONDITIONAL_IF,    // The initial conditional (ifdef/ifndef/ifeq/ifneq)
92    CONDITIONAL_ELSE,  // An else or else-conditional clause
93    CONDITIONAL_ENDIF, // The endif keyword
94    INCLUDE,
95
96    // Archive members
97    ARCHIVE_MEMBERS, // Container for just the members inside parentheses
98    ARCHIVE_MEMBER,  // Individual member like "bar.o" or "baz.o"
99
100    // Blank lines
101    BLANK_LINE, // A blank line between top-level items
102}
103
104/// Convert our `SyntaxKind` into the rowan `SyntaxKind`.
105impl From<SyntaxKind> for rowan::SyntaxKind {
106    fn from(kind: SyntaxKind) -> Self {
107        Self(kind as u16)
108    }
109}