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, Recipe, Rule, VariableDefinition,
38    VariableReference,
39};
40pub use parse::Parse;
41pub use rowan::TextRange;
42pub use text::{is_in_prerequisites, variable_at_offset, word_at_offset};
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45/// The variant of makefile being parsed
46pub enum MakefileVariant {
47    /// GNU Make (most common, supports ifeq/ifneq/ifdef/ifndef conditionals, pattern rules, etc.)
48    GNUMake,
49    /// BSD Make (FreeBSD, NetBSD, OpenBSD - uses .if/.ifdef/.ifndef directives)
50    BSDMake,
51    /// Microsoft nmake (Windows - uses !IF/!IFDEF/!IFNDEF directives)
52    NMake,
53    /// POSIX-compliant make (basic portable subset, no extensions)
54    POSIXMake,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[allow(non_camel_case_types)]
59#[repr(u16)]
60#[allow(missing_docs)]
61pub enum SyntaxKind {
62    IDENTIFIER = 0,
63    INDENT,
64    TEXT,
65    WHITESPACE,
66    NEWLINE,
67    DOLLAR,
68    LPAREN,
69    RPAREN,
70    LBRACE,
71    RBRACE,
72    QUOTE,
73    BACKSLASH,
74    COMMA,
75    OPERATOR,
76
77    COMMENT,
78    ERROR,
79
80    // composite nodes
81    ROOT,          // The entire file
82    RULE,          // A single rule
83    RECIPE,        // A command/recipe line
84    VARIABLE,      // A variable definition
85    EXPR,          // An expression (e.g., targets before colon, or old-style prerequisites)
86    TARGETS,       // Container for targets before the colon
87    PREREQUISITES, // Container for prerequisites after the colon
88    PREREQUISITE,  // A single prerequisite item
89
90    // Directives
91    CONDITIONAL,       // The entire conditional block (ifdef...endif)
92    CONDITIONAL_IF,    // The initial conditional (ifdef/ifndef/ifeq/ifneq)
93    CONDITIONAL_ELSE,  // An else or else-conditional clause
94    CONDITIONAL_ENDIF, // The endif keyword
95    INCLUDE,
96
97    // Archive members
98    ARCHIVE_MEMBERS, // Container for just the members inside parentheses
99    ARCHIVE_MEMBER,  // Individual member like "bar.o" or "baz.o"
100
101    // Blank lines
102    BLANK_LINE, // A blank line between top-level items
103}
104
105/// Convert our `SyntaxKind` into the rowan `SyntaxKind`.
106impl From<SyntaxKind> for rowan::SyntaxKind {
107    fn from(kind: SyntaxKind) -> Self {
108        Self(kind as u16)
109    }
110}