@isopodlabs/make
v0.5.2
Published
A package for reading and running makefiles with TypeScript.
Downloads
59
Maintainers
Readme
@isopodlabs/make
A robust TypeScript parser and executor for GNU Makefiles.
View on npm • View on GitHub
@isopodlabs/make is a small TypeScript-native library for parsing and executing Makefiles programmatically in Node.js.
- Parses GNU Make–style syntax.
- Executes recipes cross‑platform using your system shell.
- Useful for IDE integrations, build tooling, or testing Makefiles in Node.js.
☕ Support My Work
If you use this package, consider buying me a cup of tea to support future updates!
Installation
npm install @isopodlabs/makeQuick start
import { Makefile } from '@isopodlabs/make';
import { readFile } from 'fs/promises';
async function main() {
// Load and parse a Makefile
const mf = await Makefile.parse(await readFile('Makefile', 'utf8'));
// Run default goal or provide explicit goals
const changed = await mf.run(['all'], {
jobs: 4, // run up to 4 jobs in parallel
mode: 'normal', // 'normal' | 'dry-run' | 'question' | 'touch'
output: s => process.stdout.write(s), // capture stdout/stderr from recipes
});
console.log('did work:', changed);
}
Minimal Makefile example:
.PHONY: all build clean
all: build
build: src/app.c
@echo compiling $<
@echo output -> $@
clean:
-@rm -f buildFeatures
- Parser and executor:
- Makefile parsing and execution.
- Directives:
- Conditionals:
ifeq,ifneq,ifdef,ifndef, withelse/endif. include,-include, andsincludewith search over include dirs.export/unexportand.EXPORT_ALL_VARIABLES.undefinevpath
- Conditionals:
- Targets and rules:
- Static, pattern, and double-colon rules; grouped targets; order-only prerequisites.
- Legacy suffix rules are recognized and converted.
- Variables:
- Recursive (=), simple (:=), conditional (?=), append (+=), shell assignment (!=).
- Target- and pattern-specific variables.
- Automatic variables:
$@,$<,$^(deduped),$+(with duplicates),$|,$?,$*. .EXTRA_PREREQSsupported (added prerequisites not reflected in automatic vars).
- Functions:
- String and list:
subst,patsubst,strip,findstring,filter,filter-out,sort,word,words,wordlist,firstword,lastword,join. - Filename:
dir,notdir,suffix,basename,addsuffix,addprefix,wildcard,realpath,abspath. - Conditionals/logic:
if,or,and,intcmp. - Variables/meta:
value,origin,flavor,call,foreach,let,file,error,warning,info. - Also provides
shellandeval.
- String and list:
- Search paths:
VPATHandvpathpattern-based file resolution.
- Special targets (recognized at runtime):
.PHONY,.SUFFIXES,.DEFAULT,.PRECIOUS,.INTERMEDIATE,.NOTINTERMEDIATE,.SECONDARY,.SECONDEXPANSION,.DELETE_ON_ERROR,.IGNORE,.LOW_RESOLUTION_TIME,.SILENT,.EXPORT_ALL_VARIABLES,.NOTPARALLEL,.ONESHELL,.POSIX.
- Recipe flags:
-ignore errors,@silent,+force execution..ONESHELLor per-call option to run a whole recipe in a single shell.
- Execution modes:
normal,dry-run(print commands),question(returns true if any rebuild is needed),touch(create missing files and update timestamps).
- Parallelism:
jobsoption to control concurrency..NOTPARALLELserializes prerequisites for affected targets..WAITis a pseudo-prerequisite that splits a prerequisite list into serial segments:A: p1 p2 .WAIT p3 p4runs p1/p2 (in parallel), then p3/p4 (in parallel).
API
- Classes and types:
Makefile
This class encapsulates a makefile. It provides direct access to these builtin variables:
CURDIRRECIPEPREFIXVARIABLESFEATURESINCLUDE_DIRSVPATHSUFFIXESDEFAULT_GOAL
Construction
newMakefile(options?:CreateOptions)Creates an empty makefile with only the provided variables (plus
SHELL,MAKESHELL,MAKE_VERSION,MAKE_HOST) and rules.Makefile.parse(text: string, options?:CreateOptions)As above, but also parses the text into the makefile.
Makefile.load(filePath: string, options?:CreateOptions)Creates a makefile from a file. If options.variables is undefined, the environment variables will be used. Also sets
CURDIRandMAKEFILE_LIST.
Methods
get(name: string)Lookup a variable.
setVariable(name: string, op: string, value: string, origin: VariableOrigin)Set a variable.
setFunction(name: string, fn:Function)Override (or add) a function.
addRule(rule:RuleEntry)Add a rule.
parse(text: string, file?: string)Parse additional text into the makefile (
fileis used to improve error messages).run(goals?: string[], options?:RunOptions)Make goals using provided options.
runDirect(goals: string[] = [], options:RunOptionsDirect)Make goals using low-level options.
Create options
See CreateOptions
variables:Record<string,VariableValue>initial variables.functions:Record<string,Function>functions to override or augment the standard make functionsrules:RuleEntry[]initial ruleset; also used to generate.SUFFIXES.includeDirs:string[]search paths for include.envOverrideswhether environment variables take precedence.warnUndefwarn when an undefined variable is accessed.
import { Makefile, environmentVariables } from '@isopodlabs/make';
// Parse from text
const mf = await Makefile.parse(text, {
variables: environmentVariables(),
includeDirs: ['.vscode', 'config/includes'], // search paths for include
});
// Or load directly from a path (sets CURDIR/MAKEFILE_LIST appropriately)
const mf2 = await Makefile.load('path/to/Makefile');Run options
See RunOptions:
modeone ofnormal,dry-run,question,touchjobsnumber of simultaneous jobs (default 1)outputto capture stdout/stderrignoreErrors,silent,noSilent,oneshellkeepGoing,checkSymlink,printDirectory:always,assumeOld,assumeNew: override timestamp checks- Special targets like
.SILENT,.ONESHELL,.IGNORE,.NOTPARALLELinfluence behavior per target (and globally if declared with no prerequisites).
const changed = await mf.run(['target'], {
jobs: 2,
mode: 'question', // returns true if any rebuild would occur
output: s => process.stdout.write(s),
});See RunOptionsDirect for lower-level control over execution.
Rules
See RuleEntry:
targetswhitespace-separated list of targetsprerequisiteswhitespace-separated list of prerequisitesrecipeoptional array of strings containing the recipedoubleColontrue if it's a doubleColon rulegroupedtrue if the rule is a grouped rulebuiltintrue if the rule is a builtin rulefile,lineNolocation of definition
CLI
This is an optional sub-module, which:
- Provides a gnumake-compatible command line interface.
- Is automatically invoked if run directly from command line.
- Optionally supplies builtin rules and variables.
- Can be run programmatically, but note that the first two arguments should be the node executable and the path to the make/cli module.
import { cli } from '@isopodlabs/make/cli';
await cli(process.argv);Using the cli module's builtinRules and builtinVariables:
import { Makefile, environmentVariables } from '@isopodlabs/make';
import { builtinRules, builtinVariables } from '@isopodlabs/make/cli';
const mf = await Makefile.load('path/to/Makefile', {
variables: {...builtinVariables(), ...environmentVariables()},
rules: builtinRules(),
});
mf.run(['all'], {jobs: 6})Limitations
- No archive member support (
lib.a(member.o),$%), and no jobserver. - All rules and variables must be passed to the Makefile constructor (or via Makefile.parse or Makefile.load). The typical rules and variables can be obtained from the CLI component. In particular, variables such as MAKE and MAKEFLAGS are only available if manually provided or when run from the CLI.
- Special targets with lifecycle semantics are recognized but not fully implemented:
.PRECIOUS,.INTERMEDIATE,.NOTINTERMEDIATE,.SECONDARY,.LOW_RESOLUTION_TIME. - Requires Node.js, however only index.ts (and the optional cli) rely on any external modules (specifically, path, fs, os, and child_process), so parsing and running makefiles is possible without Node.js.
Contributing
Contributions, bug reports, and feature requests are welcome!
Open an issue or pull request on GitHub.
License
MIT © Adrian Stephens
