-
Notifications
You must be signed in to change notification settings - Fork 0
/
Makefile
400 lines (326 loc) · 15.2 KB
/
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
##
# Generate a custom ISO to install Fedora
#
# Requirements:
# - gpgv
# - wget or curl
# - sha256sum or shasum
# - openssl
# - xorriso
#
# Customizable parameters can be set via environment variables or the command line
# Other variables can be overridden via the the command line
#
# For help about how to use this makefile:
# make help
#
# For a human-readable description of what the current configuration is:
# make summary
##
##
# | | Workstation | Server |
# | ------- | ------------- | ------------- |
# | x86_64 | To be tested | To be tested |
# | aarch64 | X | To be tested |
# | ppc64le | Download only | Download only |
# | s390x | X | Download only |
#
# TL;DR: x86_64 -> Workstation
# aarch64 -> Server
# ppc64le, s390x !?
##
# ---------- Customizable parameters ----------
# wget | curl
ifeq "$(origin Downloader)" 'undefined'
Downloader := $(shell command -v wget > /dev/null && echo wget || echo curl)
endif
FedoraVersion ?= 40-1.14
# Workstation | Server
FedoraEdition ?= Workstation
# x86_64 | aarch64 | ppc64le | s390x
Architecture ?= x86_64
FullName ?= Your NAME
UserName ?= admin
ifeq "$(origin Password)" 'undefined'
Password := $(shell openssl passwd -6 -salt sugar admin)
endif
ifeq "$(origin RootPassword)" 'undefined'
RootPassword := $(Password)
endif
EmailAddress ?= [email protected]
WorldRegion ?= America
CountryCode ?= US
City ?= New_York
Languages ?= en_US.utf8
KeyboardLayouts ?= us
NtpPool ?= 2.fedora.pool.ntp.org
DefaultEntry ?= shutdown
ifeq "$(origin TimeZone)" 'undefined'
TimeZone := $(WorldRegion)/$(City)
endif
# In templates, only substitute variables listed here
ExportedVariables += FullName UserName Password RootPassword TimeZone Languages KeyboardLayouts NtpPool
# ---------------------------------------------
# ---------- Computed / Preset ----------
# Commands
GPG_VERIFY := gpgv
OPENSSL := openssl
XORRISO := xorriso -no_rc
ENVSUBST := envsubst
MAKE_FAT := mkfs.fat
MKDIR_FAT := mmd
CP_FAT := mcopy
# Folders
GeneratedFolder := generated
ExtractedFolder := extracted
CertificateFolder := $(GeneratedFolder)/certificates
KickstartFolder := $(GeneratedFolder)/kickstart
GrubFolder := $(GeneratedFolder)/grub
# OS specific
ifeq "$(shell uname)" "Darwin"
ECHO := echo
DownloadsFolder := $(shell osascript -e 'POSIX path of (path to downloads folder)')
SHA_SUM := shasum --algorithm 256
else
SHELL := bash
.SHELLFLAGS := -o errexit -o nounset -o pipefail -c
ECHO := echo -e
DownloadsFolder != xdg-user-dir DOWNLOAD
SHA_SUM := sha256sum
endif
# Download command
ifeq "$(Downloader)" 'wget'
DOWNLOAD := wget --directory $(DownloadsFolder) --no-clobber --quiet --show-progress
else ifeq "$(Downloader)" 'curl'
DOWNLOAD := curl --output-dir $(DownloadsFolder) --remote-name --location
else
DOWNLOAD := $(Downloader)
endif
# Architecture specific
ifeq "$(Architecture)" 'x86_64'
ARCHEFI := X64
ArchEFI := x64
FedoraChannel := fedora/linux
else ifeq "$(Architecture)" 'aarch64'
ARCHEFI := AA64
ArchEFI := aa64
FedoraChannel := fedora/linux
else
FedoraChannel := fedora-secondary
endif
# Edition specific
ifeq "$(FedoraEdition)" 'Workstation'
FedoraMethod := Live
else
# dvd | netinst
FedoraMethod := netinst
endif
FedoraMajor := $(shell cut -d- -f1 <<< "$(FedoraVersion)")
IsoLabel := Fedora_$(FedoraVersion)_$(Architecture)
FatLabel := BOOT_EFI
MakeFatOptions := -C -n $(FatLabel)
# URLs
FedoraKeyURL := https://fedoraproject.org
OfficialIsoURL := https://download.fedoraproject.org/pub/$(FedoraChannel)/releases/$(FedoraMajor)/$(FedoraEdition)/$(Architecture)/iso
# File names
FedoraKeyName := fedora.gpg
OfficialIsoName := Fedora-$(FedoraEdition)-$(FedoraMethod)-$(Architecture)-$(FedoraVersion).iso
OfficialCheckName := Fedora-$(FedoraEdition)-$(FedoraVersion)-$(Architecture)-CHECKSUM
# Input Files
KickstartTemplates := $(wildcard kickstart/*.cfg)
# Files created by this Makefile
FedoraKey := $(DownloadsFolder)/$(FedoraKeyName)
OfficialIso := $(DownloadsFolder)/$(OfficialIsoName)
OfficialChecksum := $(DownloadsFolder)/$(OfficialCheckName)
MachineOwnerKey := $(CertificateFolder)/MOK.priv
MachineOwnerDER := $(CertificateFolder)/MOK.der
MachineOwnerPEM := $(CertificateFolder)/MOK.pem
ExtractedBootloaders:= $(addprefix $(ExtractedFolder)/,BOOT$(ARCHEFI).EFI grub$(ArchEFI).efi mm$(ArchEFI).efi)
KickstartScripts := $(KickstartTemplates:kickstart/%=$(KickstartFolder)/%)
GrubConfig := $(GrubFolder)/grub.cfg
IsoImage := $(GeneratedFolder)/$(IsoLabel).iso
SignedIsoImage := $(GeneratedFolder)/$(IsoLabel)_signed.iso
EfiBootFiles := $(ExtractedBootloaders) $(GrubConfig)
EfiBoot := $(GeneratedFolder)/efiboot.img
# ---------------------------------------
# ---------- Make Configuration ----------
.DELETE_ON_ERROR: # Delete the target of a rule if its recipe execution fails
.SUFFIXES: # Disable atomatic suffix guessing
#.ONESHELL: # Perform a single shell invocation per recipe. Only makes sense if the shell is set to exit on error !
# ----------------------------------------
# ---------- Colors ----------
Red := \033[31m
Cyan := \033[36m
Bold := \033[1m
Italic := \033[3m
EOC := \033[0m
PP_command := $(Cyan)
PP_section := $(Bold)
PP_input := $(Bold)
PP_error := $(Red)
PP_variable := $(Italic)
# ----------------------------
# ---------- Functions ----------
check_command = command -v $(value $(1)) > /dev/null || \
$(ECHO) "$(PP_error)Missing dependency $(Bold)$(firstword $(value $(1)))$(EOC)," \
"consider installing it or overriding the $(Bold)$(Italic)$(1)$(EOC) variable"
# -------------------------------
#Exporting variables for envsubst
$(foreach varible,$(ExportedVariables),$(eval export $(variable)))
# Phony rules
##@ General
default: help summary ## When no target is specified, display the help and summary
summary: ## Sum up what the makefile will do, given the current configuration
@$(ECHO) "\nReady to generate a bootable ISO for Fedora $(PP_input)$(FedoraMajor)$(EOC) ($(FedoraVersion))\n"
@$(ECHO) "When ready:\n make $(PP_command)$(PP_variable)<step>$(EOC)\n"
@$(ECHO) "$(PP_section)$(PP_command)download$(EOC)\n will download:"
@printf " - %s\n" $(FedoraKeyName) $(OfficialCheckName) $(OfficialIsoName)
@$(ECHO) " to $(PP_input)$(DownloadsFolder)$(EOC)"
@$(ECHO) " using $(PP_input)$(Downloader)$(EOC)\n"
@$(ECHO) "$(PP_section)$(PP_command)certificates$(EOC)"
@$(ECHO) " will generate certificates for Machine Owner Key verification"
@$(ECHO) " to $(PP_input)$(CertificateFolder)$(EOC)"
@$(ECHO) " using $(PP_input)$(OPENSSL)$(EOC)\n"
@$(ECHO) "$(PP_section)$(PP_command)extract$(EOC)"
@$(ECHO) " will extract from the official ISO:"
@printf " - %s\n" $(ExtractedBootloaders:$(ExtractedFolder)/%=%)
@$(ECHO) " to $(PP_input)$(ExtractedFolder)$(EOC)"
@$(ECHO) " using $(PP_input)$(firstword $(XORRISO))$(EOC)\n"
@$(ECHO) "$(PP_section)$(PP_command)evaluate$(EOC)\n will fill the values:"
@printf " - % -20s: %.40s\n" $(foreach var,$(ExportedVariables),$(var) '$(subst $(quote),,$(value $(var)))')
@$(ECHO) " in :$(addprefix \n - ,$(KickstartTemplates))"
@$(ECHO) " to $(PP_input)$(KickstartFolder)$(EOC)"
@$(ECHO) " using $(PP_input)$(ENVSUBST)$(EOC)\n"
@$(ECHO) "$(PP_section)$(PP_command)grub/config$(EOC)"
@$(ECHO) " will generate a grub configuration with the following entries:"
@printf " - % -15s: $(PP_input)%.45s$(EOC)\n" $(foreach entry,$(filter kickstart/entry_%,$(KickstartTemplates)),\
`a=$(entry);b=$${a#*entry_};c=$${b%.*};echo $$c`\
"`head -1 $(entry) | cut -d'\"' -f2`")
@$(ECHO) "\n$(PP_section)$(PP_command)help$(EOC)\n to learn how to use this makefile\n"
help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nThis Makefile allows one to generate a custom ISO to install Fedora\n\nUsage:\n make $(PP_command)$(PP_variable)<target>$(EOC)\n"} /^[\/a-zA-Z_0-9-]+:.*?##/ { printf " $(PP_command)%-20s$(EOC) %s\n", $$1, $$2 } /^##@/ { printf "\n$(PP_section)%s$(EOC):\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
raw_help: ## Display the help without color
@$(MAKE) help --no-print-directory PP_command= PP_section= PP_variable= EOC=
.PHONY: default summary help
##@ Check for requirements
check/downloader: ## Check that the choosen downloader is installed
@$(call check_command,Downloader)
check/gpg_verifier: ## Check that the GPG verifier is installed
@$(call check_command,GPG_VERIFY)
check/shasum: ## Check that the sum checker is installed. It is used to verify files integrity
@$(call check_command,SHA256SUM)
check/openssl: ## Check that openssl is installed. It is used to generate certificates
@$(call check_command,OPENSSL)
check/xorriso: ## Check that xorriso is installed. It is needed to extract from and modify ISOs
@$(call check_command,XORRISO)
check/envsubst: ## Check that envsubst is installed. It is used to evaluate templates using the environment
@$(call check_command,ENVSUBST)
check/fat: ## Check that FAT manipulation tools are installed
@$(call check_command,MAKE_FAT)
@$(call check_command,MKDIR_FAT)
@$(call check_command,CP_FAT)
check/all: check/downloader check/gpg_verifier check/shasum check/openssl check/xorriso check/envsubst ## Check that all requirements are installed
.PHONY: check/all check/downloader check/gpg_verifier check/shasum check/openssl check/xorriso check/envsubst
##@ Individual steps
download: $(OfficialIso) ## Download the official ISO (and check its integrity)
certificates: $(MachineOwnerPEM) $(MachineOwnerDER) $(MachineOwnerKey) ## Generate a private public key pair used to sign the bootloader
extract: $(ExtractedBootloaders) ## Extract bootloaders from the official ISO
evaluate: $(KickstartScripts) ## Evaluate kickstart scripts templates with the current values.
grub/config: $(GrubConfig) ## Generate GRUB configuration: Create an entry for each kickstart script starting with 'entry_'.
boot/image: $(EfiBoot) ## Generate an image used to boot in EFI mode
.PHONY: download certificates extract evaluate grub/config boot/image
##@ ISO Generation
iso: $(IsoImage) ## Generate a bootable ISO image
.PHONY: iso
##@ Removing generated files
clean/downloads: ## Remove downloaded files
$(RM) $(OfficialIso) $(OfficialChecksum) $(FedoraKey)
clean/certificates: ## Remove certificates
$(RM) -r $(CertificateFolder)
clean/extracted: ## Remove files extracted from the official ISO
$(RM) -r $(ExtractedFolder)
clean/evaluated: ## Remove generated kickstart scripts
$(RM) -r $(KickstartFolder)
clean/iso: ## Only remove generated ISO images
$(RM) $(IsoImage)
clean: clean/extracted ## Clean all generated and extracted files
$(RM) -r $(GeneratedFolder)
clean/all: clean/downloads clean ## Remove all generated, extracted and downloaded files
.PHONY: clean/downloads clean/certificates clean/extracted clean/evaluated clean/iso clean clean/all
# Concrete rules
$(DownloadsFolder) $(GeneratedFolder) $(ExtractedFolder) $(CertificateFolder) $(KickstartFolder) $(GrubFolder):
mkdir -p $@
# --------------- Second Expansion ---------------
# When a rule is expanded, both the target and the prerequisites
# are immediately evaluated. Enabling a second expansion allows
# a prerequisite to use automatic variables like $@, $*, etc
.SECONDEXPANSION:
$(FedoraKey): | $$(@D) check/downloader
$(DOWNLOAD) $(FedoraKeyURL)/$(@F)
@touch $@
$(OfficialChecksum): $(FedoraKey) | $$(@D) check/downloader check/gpg_verifier
$(DOWNLOAD) $(OfficialIsoURL)/$(@F)
$(GPG_VERIFY) --keyring $< $@
@touch $@
$(OfficialIso): $(OfficialChecksum) | $$(@D) check/downloader check/shasum
$(DOWNLOAD) $(OfficialIsoURL)/$(@F)
( cd $(@D) && $(SHA_SUM) --ignore-missing --check $(<F) 2> /dev/null )
@touch $@
# Update the timestamp to the time downloaded, not the time it was created upstream
# Because of course Fedora can only generate the checksum AFTER generating the ISO
# Using the upstream time would ALWAYS want to re-download the ISO as its dependency would be newer
$(MachineOwnerKey) $(MachineOwnerDER) &: | $$(@D) check/openssl
$(OPENSSL) req -new -x509 -newkey rsa:2048 -nodes -days 3650 \
-subj '/C=$(CountryCode)/L=$(City)/CN=$(FullName)/emailAddress=$(EmailAddress)' \
-addext 'subjectKeyIdentifier=hash' \
-addext 'authorityKeyIdentifier=keyid:always,issuer' \
-addext "basicConstraints=critical,CA:FALSE" \
-addext "extendedKeyUsage=codeSigning" \
-addext "nsComment=OpenSSL Generated Certificate" \
-outform DER -keyout $(MachineOwnerKey) -out $(MachineOwnerDER)
$(MachineOwnerPEM): $(MachineOwnerDER) | $$(@D) check/openssl
$(OPENSSL) x509 -in $< -inform DER -outform PEM -out $@
# Could be done with a single xorriso invocation but it seems MacOS' Make doesn't handle the &: syntax ?
$(ExtractedBootloaders): $(OfficialIso) | $$(@D) check/xorriso
$(XORRISO) -osirrox on -indev $< -extract /EFI/BOOT/$(@F) $@
@touch $@
$(KickstartScripts): $(KickstartFolder)/%.cfg: kickstart/%.cfg | $$(@D) check/envsubst
$(ENVSUBST) '$(ExportedVariables:%=$$%)' < $< | sed 's|^%shard \(.*\)$$|%ksappend /run/install/repo/kickstart/\1.cfg|' > $@
$(GrubFolder)/entries.cfg: $(filter kickstart/entry_%,$(KickstartTemplates)) | $$(@D)
printf "default=$(DefaultEntry)\n" > $@
printf "search --no-floppy --set=root --label '$(IsoLabel)'\n\n" >> $@
for entry in $^ ; \
do \
title=$$(head -1 $$entry | cut -d'"' -f2) ; \
id=$${entry#*entry_} ; id=$${id%.*} ; \
printf "menuentry '%s' --class fedora --class gnu --class os --id '%s' {\n\tset gfxpayload=keep\n\tlinuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=$(IsoLabel) inst.repo=hd:LABEL=$(IsoLabel):/ inst.ks=hd:LABEL=$(IsoLabel):/%s quiet\n\tinitrdefi /images/pxeboot/initrd.img\n}\n\n" "$$title" "$$id" "$$entry" >> $@ ; \
done
$(GrubConfig): grub/prefix.cfg $(GrubFolder)/entries.cfg grub/suffix.cfg | $$(@D)
cat $^ > $@
$(EfiBoot): $(ExtractedBootloaders) $(GrubConfig) | $$(@D) check/fat
$(RM) $@
bytes=$$(du --bytes --total --summarize $^ | tail -1 | cut -f1) && \
kilos=$$(echo "((($$bytes + 1023) / 1024 + 8192 + 1023) / 1024) * 1024" | bc) && \
$(MAKE_FAT) $(MakeFatOptions) `(( $$kilos >= 36864 )) && echo -F 32` $@ $$kilos
$(MKDIR_FAT) -i $@ "::/EFI" "::/EFI/BOOT"
$(CP_FAT) -i $@ $^ "::/EFI/BOOT"
$(IsoImage): $(OfficialIso) $(GrubConfig) $(EfiBoot) $(KickstartScripts) | $$(@D) check/xorriso
$(RM) $@
$(XORRISO) \
-indev $< \
-outdev $@ \
-map $(GrubConfig) EFI/BOOT/grub.cfg \
-map $(KickstartFolder) kickstart \
-chmod_r a+r,a-w / -- \
-as mkisofs \
-iso-level 3 -full-iso9660-filenames \
-joliet -joliet-long -rational-rock \
-volid "$(IsoLabel)" --preparer "$(FullName)" \
-partition_offset 16 \
-append_partition 2 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' $(EfiBoot) \
-appended_part_as_gpt \
-eltorito-alt-boot \
-e '--interval:appended_partition_2:all::' \
-no-emul-boot
# Put at the end because it is not well parsed by editors providing syntax color
quote := '