1# GNU Make Extensions
2
3Makext is a collection of useful extensions for Makefiles, aimed
4at simplifying and enhancing the functionality of Make-based
5projects. These extensions provide additional features and
6convenience functions to improve the overall usage of [GNU
7Make](https://www.gnu.org/software/make/) as a task runner.
8
9All the extensions are written in GNU Make so no other languages are used
10making this very embedable and has zero dependencies other than GNU Make.
11
12Other than `makext.mk` all the other files in the repository are either
13license or readme files or just testing data that is/are not needed to
14use it.
15
16> [!NOTE]
17> These extensions are abusing GNU Make in some sense since it was not
18> meant to really be a task runner. Keep that in mind. However, despite
19> that, I constantly find myself using it as such.
20
21## Extensions
22
23| Extension | Description |
24|-------------|-----------------------------------------------------|
25| **help** | Displays all targets with a comment in help format. |
26| **assure** | Check for the existence of programs on a machine. |
27| **environment** | Loads environmental variables from other files. |
28
29Additional features:
30
31- Automatic description inclusion in help message.
32- Automatic license inclusion in help message.
33
34Tested on:
35
36- GNU Linux Debian 12 with GNU Make 4.3
37- macOS Sonoma 14.4.1 with GNU Make 3.81
38- Windows 10/11 with GNU Make 4.4.1 for Windows32
39
40If you have an idea for a new feature [open a new
41issue](https://github.com/mitjafelicijan/makext/issues/new).
42
43## How to use
44
45> [!IMPORTANT]
46> If you intend to use this script on Windows machine
47> make sure you have all the [prerequisites](#windows-specific-instructions) installed.
48
49First you will need to download `makext.mk` file from the repository to
50the same directory where you have `Makefile`.
51
52```sh
53wget -O makext.mk https://github.com/mitjafelicijan/makext/raw/master/makext.mk
54```
55
56Now you can include it in your `Makefile`.
57
58```make
59include makext.mk
60
61help: .help
62```
63
64> [!IMPORTANT]
65> Make sure you create first target `help: .help` before any other
66> targets in your `Makefile`. GNU Make will execute first target if
67> no target provided as an argument when calling `make`.
68
69### Windows specific instructions
70
71On Windows OS'es you need the following:
72
73- Git Bash for Windows
74- make
75
76All of the above can be conveniently installed via [Scoop](https://scoop.sh/):
77
78```sh
79scoop install main/git main/make
80```
81
82## Help extension
83
84One of the extensions is `.help` which displays all the targets in the
85`Makefile` and their descriptions which are provided as comments next
86to the target definition.
87
88Lets check how and example `Makefile` would look like. It
89is recommended to use `.PHONY` for targets that are not
90actual files. In the example below I am not doing that
91though, but it is wise to follow that rule. Check [4.6 Phony
92Targets](https://www.gnu.org/software/make/manual/make.html#Phony-Targets)
93section in the GNU Make manual.
94
95```make
96include makext.mk
97
98help: .help
99
100build-app: clean-cache # Build the application
101 @echo "Building the application..."
102
103clean-cache: # Clean the cache
104 @echo "Cleaning the cache..."
105
106deploy-prod: # Deploy to production
107 @echo "Deploying to production..."
108
109run-tests:
110 @echo "Running tests..."
111```
112
113This will give us the following result when we execute command `make`
114without any arguments.
115
116```text
117$ make
118Targets:
119 build-app Build the application
120 clean-cache Clean the cache
121 deploy-prod Deploy to production
122```
123
124- Targets without defined comment next to the target will be ignored
125 from help list. In this case `run-tests` is missing from the list.
126- Targets that start with `.` will also be ignored.
127- Prerequisites in targets will be omitted from the result. See how
128 `clean-cache` is missing from `build-app` target.
129
130## Description & License information
131
132You can provide description for the project that will be displayed
133together with `help`. To do this provide this information in the
134`MEX_DESCRIPTION` variable.
135
136Same goes for license information. Provide this information by creating
137`MEX_LICENSE` variable.
138
139If these variables are not present this information will not be displayed
140in the help.
141
142Description and license information is also formatted to max 75 characters
143per row.
144
145> [!IMPORTANT]
146> Variables `MEX_DESCRIPTION` and `MEX_LICENSE` must be defined before you
147> include `makext.mk` to your `Makefile`. This is needed because the way
148> GNU Make is parsing Makefiles.
149
150```make
151MEX_DESCRIPTION="This provides some additional tools for this project."
152MEX_LICENSE="Released under the BSD two-clause license, see the LICENSE file for more information."
153
154include makext.mk
155
156help: .help
157
158build-app: clean-cache # Build the application
159 @echo "Building the application..."
160
161clean-cache: # Clean the cache
162 @echo "Cleaning the cache..."
163```
164
165The following example will produce the following result.
166
167```text
168$ make
169This provides some additional tools for this project.
170
171Targets:
172 build-app Build the application
173 clean-cache Clean the cache
174
175Released under the BSD two-clause license, see the LICENSE file for
176more information.
177```
178
179## Assure extension
180
181Often times project uses multiple programs and to ensure that these
182programs are already installed before recipes are executed `.assure` can
183be used. If programs are missing recipes can only partially be executed
184leaving project in a potentially broken state.
185
186```make
187MEX_ASSURE="python3 ls tree clang"
188
189include makext.mk
190
191build-app: .assure
192 @echo "Building the application..."
193```
194
195`.assure` prerequisite will loop over the list of programs defined in
196`MEX_ASSURE` variable and in case one is missing will exit `make` with
197error status code 1. This will stop executing the recipe and therefore
198not execute anything in target `build-app`.
199
200## Environment extension
201
202This extension helps loading of additional environmental files in your
203project. The files should have environmental variables defined in the
204usual way. Separate each file by a space and that is about it.
205
206If a file is missing this will break the execution of make and exit with
207status code 1.
208
209```env
210API_KEY=abc123
211SECRET_KEY=def456
212```
213
214By defining `MEX_ENVIRONMENT` variable you can provide additional files
215and they will be loaded automatically.
216
217```make
218MEX_ENVIRONMENT="local.env second.env"
219
220include makext.mk
221
222demo-envars:
223 @echo "Environment variables"
224 @echo " HOME: $(HOME)"
225 @echo " TERM: $(TERM)"
226 @echo " ENV: $(MEX_ENVIRONMENT)"
227 @echo " AUDIO_BUCKET: $(AUDIO_BUCKET)"
228 @echo " DB_HOST: $(DB_HOST)"
229```
230
231After that they can be used in your recipes like all the other variables
232you have. They will however override variables the shell already has
233defined.
234
235## Alternative tools
236
237- https://github.com/rocky/remake
238- https://github.com/casey/just
239- https://github.com/xonixx/makesure
240- https://github.com/ruby/rake
241- https://github.com/taskctl/taskctl
242- https://github.com/go-task/task
243- https://github.com/pydoit/doit
244
245## Acknowledgment
246
247- https://stackoverflow.com/a/59087509
248
249## License
250
251[makext](https://github.com/mitjafelicijan/makext) was written by [Mitja
252Felicijan](https://mitjafelicijan.com) and is released under the BSD
253two-clause license, see the LICENSE file for more information.