424 lines
15 KiB
Bash
Executable file
424 lines
15 KiB
Bash
Executable file
#!/bin/bash
|
|
set -eo pipefail
|
|
|
|
# Set language to C.UTF-8 (for easier debugging)
|
|
export "LANG=C.UTF-8"
|
|
export "LC_MESSAGES=${LANG}"
|
|
|
|
# Variables
|
|
export "BUILDTOOL_ISSUETRACKER=https://git.staropensource.de/JeremyStarTM/kernel-optimized.git/issues"
|
|
## Cloning (update these if you fork this project)
|
|
export "BUILDTOOL_REPOSITORY=https://git.staropensource.de/JeremyStarTM/kernel-optimized.git"
|
|
export "BUILDTOOL_CLONEDIR=jstm-kernel-optimized"
|
|
## Base packages to install, packages will be added by questions
|
|
export "BUILDTOOL_PACKAGES=base-devel git rustup"
|
|
## Building command line stuff
|
|
export "BUILDTOOL_BUILDCMDLINE=nice -20 env MAKEFLAGS=-j$(nproc)"
|
|
## OOOOoooo colors! (not true color tho)
|
|
export "BUILDTOOL_COLOR_SPECIAL=\e[0;35m"
|
|
export "BUILDTOOL_COLOR_DIAG=\e[0;36m"
|
|
export "BUILDTOOL_COLOR_WARN=\e[0;33m"
|
|
export "BUILDTOOL_COLOR_ERROR=\e[0;31m"
|
|
export "BUILDTOOL_COLOR_RESET=\e[0m"
|
|
|
|
# Logging
|
|
## Empty
|
|
function empty() {
|
|
echo ""
|
|
}
|
|
## Special
|
|
function special() {
|
|
echo -e "${BUILDTOOL_COLOR_SPECIAL}${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
## Diagnostic (header)
|
|
function diagh() {
|
|
if [ -z "${BUILDTOOL_DEBUG}" ]; then return; fi
|
|
echo -e "${BUILDTOOL_COLOR_DIAG}[DIAG] ${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
## Diagnostic (extension)
|
|
function diage() {
|
|
if [ -z "${BUILDTOOL_DEBUG}" ]; then return; fi
|
|
echo -e "${BUILDTOOL_COLOR_DIAG} ${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
## Informational (header)
|
|
function infoh() {
|
|
echo -e "${BUILDTOOL_COLOR_RESET}[INFO] ${*}"
|
|
}
|
|
## Informational (extension)
|
|
function infoe() {
|
|
echo -e "${BUILDTOOL_COLOR_RESET} ${*}"
|
|
}
|
|
## Warning (header)
|
|
function warnh() {
|
|
echo -e "${BUILDTOOL_COLOR_WARN}[WARN] ${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
## Warning (extension)
|
|
function warne() {
|
|
echo -e "${BUILDTOOL_COLOR_WARN} ${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
## Error (header)
|
|
function errorh() {
|
|
echo -e "${BUILDTOOL_COLOR_ERROR}[ERR!] ${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
## Error (extension)
|
|
function errore() {
|
|
echo -e "${BUILDTOOL_COLOR_ERROR} ${*}${BUILDTOOL_COLOR_RESET}"
|
|
}
|
|
|
|
# Banner
|
|
special "${BUILDTOOL_COLOR_RESET}${BUILDTOOL_COLOR_SPECIAL} _ _ _ _ _ _ _ _ "
|
|
special " (_)___| |_ _ __ ___ | | _____ _ __ _ __ ___| | ___ _ __ | |_(_)_ __ ___ (_)_______ __| |"
|
|
special " | / __| __| '_ \` _ \\ _____| |/ / _ \\ '__| '_ \\ / _ \\ |_____ / _ \\| '_ \\| __| | '_ \` _ \\| |_ / _ \\/ _\` |"
|
|
special " | \\__ \\ |_| | | | | |_____| < __/ | | | | | __/ |_____| (_) | |_) | |_| | | | | | | |/ / __/ (_| |"
|
|
special " _/ |___/\\__|_| |_| |_| |_|\\_\\___|_| |_| |_|\\___|_| \\___/| .__/ \\__|_|_| |_| |_|_/___\\___|\\__,_|${BUILDTOOL_COLOR_RESET}"
|
|
special "|__/ |_|"
|
|
empty
|
|
|
|
# Print environment configuration
|
|
diagh "\$BUILDTOOL_DEBUG is set, debug mode is enabled"
|
|
diagh "\$BUILDTOOL_LOCALDIR is set, will build kernel package from current working directory"
|
|
|
|
# Checks
|
|
## Check for Arch Linux
|
|
(
|
|
# shellcheck disable=SC1091
|
|
source "/etc/os-release"
|
|
if [ "${NAME}" != "Arch Linux" ] || [ "${PRETTY_NAME}" != "Arch Linux" ] || [ "${ID}" != "arch" ]; then
|
|
if [ -z "${BUILDTOOL_COMPILE_NONARCH}" ]; then
|
|
errorh "Not running on Arch Linux"
|
|
errore "This kernel can only compile on Arch Linux"
|
|
errore "If you are sure that you want to compile this kernel on other distributions"
|
|
errore "set the \$BUILDTOOL_COMPILE_NONARCH environment variable to \"true\""
|
|
exit 1
|
|
else
|
|
warnh "\$BUILDTOOL_COMPILE_NONARCH is set, allowing compiling on a non-arch distribution"
|
|
fi
|
|
fi
|
|
)
|
|
## Check for root
|
|
if [ "${UID}" == "0" ]; then
|
|
errorh "Can't build kernel as root"
|
|
errore "Please create a new user for building the kernel"
|
|
exit 1
|
|
fi
|
|
## Check for sudo
|
|
if ! which sudo &> /dev/null; then
|
|
errorh "Could not find sudo in \$PATH"
|
|
errore "Please ensure that you have sudo installed"
|
|
exit 1
|
|
fi
|
|
|
|
# Questions
|
|
## Ask if mold should replace ld/lld
|
|
function ask_mold() {
|
|
read -rp "$(infoh "Do you want to use mold as your linker (much faster) [Y/n]? ")" BUILDTOOL_MOLD
|
|
case "${BUILDTOOL_MOLD}" in
|
|
"y"|"Y"|"")
|
|
export "BUILDTOOL_PACKAGES=${BUILDTOOL_PACKAGES} mold"
|
|
export "BUILDTOOL_BUILDCMDLINE=${BUILDTOOL_BUILDCMDLINE} LDFLAGS=-fuse-ld=mold RUSTFLAGS=\"-C link-arg=-fuse-ld=mold\""
|
|
;;
|
|
"n"|"N") ;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_mold
|
|
esac
|
|
}
|
|
## Ask if the governor should be adjusted
|
|
function ask_cpupower() {
|
|
read -rp "$(infoh "Do you want to adjust the cpu governor (improves build performance) [Y/n]? ")" BUILDTOOL_CPUPOWER
|
|
case "${BUILDTOOL_CPUPOWER}" in
|
|
"y"|"Y"|"")
|
|
export "BUILDTOOL_PACKAGES=${BUILDTOOL_PACKAGES} cpupower"
|
|
;;
|
|
"n"|"N") ;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_cpupower
|
|
esac
|
|
}
|
|
## Ask for building in tmpfs
|
|
function ask_tmpfs() {
|
|
if [ -n "${BUILDTOOL_LOCALDIR}" ]; then
|
|
warnh "Skipped ask_tmpfs, building in local directory instead (\$BUILDTOOL_LOCALDIR is set)"
|
|
export "BUILDTOOL_CLONEDIR=$(pwd)"
|
|
return
|
|
fi
|
|
read -rp "$(infoh "Do you want to build in a tmpfs (improves build performance, protects your disk from wearing out faster, ~32 GiB memory required) [Y/n]? ")" BUILDTOOL_TMPFS
|
|
case "${BUILDTOOL_TMPFS}" in
|
|
"y"|"Y"|"")
|
|
export "BUILDTOOL_CLONEDIR=/tmp/${BUILDTOOL_CLONEDIR}"
|
|
;;
|
|
"n"|"N")
|
|
export "BUILDTOOL_CLONEDIR=$(pwd)/${BUILDTOOL_CLONEDIR}"
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_tmpfs
|
|
esac
|
|
}
|
|
## Ask for kernel config modification using xconfig
|
|
function ask_xconfig() {
|
|
read -rp "$(infoh "Do you want to configure the kernel using xconfig before build [y/N]? ")" BUILDTOOL_XCONFIG
|
|
case "${BUILDTOOL_XCONFIG}" in
|
|
"y"|"Y")
|
|
export "BUILDTOOL_PKGBUILD_XCONFIG=_makexconfig=SET"
|
|
;;
|
|
"n"|"N"|"")
|
|
export "BUILDTOOL_PKGBUILD_XCONFIG=_makexconfig="
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_xconfig
|
|
esac
|
|
}
|
|
## Ask for kernel config modification using xconfig
|
|
function ask_nconfig() {
|
|
read -rp "$(infoh "Do you want to configure the kernel using nconfig before build [y/N]? ")" BUILDTOOL_NCONFIG
|
|
case "${BUILDTOOL_NCONFIG}" in
|
|
"y"|"Y")
|
|
export "BUILDTOOL_PKGBUILD_NCONFIG=_makenconfig=SET"
|
|
;;
|
|
"n"|"N"|"")
|
|
export "BUILDTOOL_PKGBUILD_NCONFIG=_makenconfig="
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_nconfig
|
|
esac
|
|
}
|
|
## Ask if the final kernel configuration should be copied
|
|
function ask_cpfinalconfig() {
|
|
read -rp "$(infoh "Do you want to copy the final kernel configuration before build [y/N]? ")" BUILDTOOL_CPFINALCONFIG
|
|
case "${BUILDTOOL_CPFINALCONFIG}" in
|
|
"y"|"Y")
|
|
export "BUILDTOOL_PKGBUILD_CPFINALCONFIG=_copyfinalconfig=SET"
|
|
;;
|
|
"n"|"N"|"")
|
|
export "BUILDTOOL_PKGBUILD_CPFINALCONFIG=_copyfinalconfig="
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_cpfinalconfig
|
|
esac
|
|
}
|
|
## Ask if only active modules should be compiled into the kernel
|
|
function ask_modprobeddb() {
|
|
read -rp "$(infoh "Do you want to only build active kernel modules (modprobed-db must be installed, read \"https://wiki.archlinux.org/index.php/Modprobed-db\" first) [y/N]? ")" BUILDTOOL_MODPROBEDDB
|
|
case "${BUILDTOOL_MODPROBEDDB}" in
|
|
"y"|"Y")
|
|
if ! which modprobed-db &> /dev/null; then
|
|
errorh "Could not find modprobed-db in \$PATH."
|
|
errore "Please ensure that you have modprobed-db installed."
|
|
ask_modprobeddb
|
|
return 1
|
|
fi
|
|
export "BUILDTOOL_PACKAGES=${BUILDTOOL_PACKAGES} modprobed-db"
|
|
export "BUILDTOOL_PKGBUILD_MODPROBEDDB=_localmodcfg=SET"
|
|
;;
|
|
"n"|"N"|"")
|
|
export "BUILDTOOL_PKGBUILD_MODPROBEDDB=_localmodcfg="
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y or N"
|
|
ask_modprobeddb
|
|
esac
|
|
}
|
|
## Ask for sub architecture
|
|
function ask_subarchitecture() {
|
|
infoh "Displaying sub architecture list"
|
|
infoe " 1. AMD Opteron/Athlon64/Hammer/K8 (MK8)"
|
|
infoe " 2. AMD Opteron/Athlon64/Hammer/K8 with SSE3 (MK8SSE3)"
|
|
infoe " 3. AMD 61xx/7x50/PhenomX3/X4/II/K10 (MK10)"
|
|
infoe " 4. AMD Barcelona (MBARCELONA)"
|
|
infoe " 5. AMD Bobcat (MBOBCAT)"
|
|
infoe " 6. AMD Jaguar (MJAGUAR)"
|
|
infoe " 7. AMD Bulldozer (MBULLDOZER)"
|
|
infoe " 8. AMD Piledriver (MPILEDRIVER)"
|
|
infoe " 9. AMD Steamroller (MSTEAMROLLER)"
|
|
infoe "10. AMD Excavator (MEXCAVATOR)"
|
|
infoe "11. AMD Zen (MZEN)"
|
|
infoe "12. AMD Zen 2 (MZEN2)"
|
|
infoe "13. AMD Zen 3 (MZEN3)"
|
|
infoe "14. AMD Zen 4 (MZEN4)"
|
|
infoe "15. Intel P4 / older Netburst based Xeon (MPSC)"
|
|
infoe "16. Intel Core 2 (MCORE2)"
|
|
infoe "17. Intel Atom (MATOM)"
|
|
infoe "18. Intel Nehalem (MNEHALEM)"
|
|
infoe "19. Intel Westmere (MWESTMERE)"
|
|
infoe "20. Intel Silvermont (MSILVERMONT)"
|
|
infoe "21. Intel Goldmont (MGOLDMONT)"
|
|
infoe "22. Intel Goldmont Plus (MGOLDMONTPLUS)"
|
|
infoe "23. Intel Sandy Bridge (MSANDYBRIDGE)"
|
|
infoe "24. Intel Ivy Bridge (MIVYBRIDGE)"
|
|
infoe "25. Intel Haswell (MHASWELL)"
|
|
infoe "26. Intel Broadwell (MBROADWELL)"
|
|
infoe "27. Intel Skylake (MSKYLAKE)"
|
|
infoe "28. Intel Skylake X (MSKYLAKEX)"
|
|
infoe "29. Intel Cannon Lake (MCANNONLAKE)"
|
|
infoe "30. Intel Ice Lake (MICELAKE)"
|
|
infoe "31. Intel Cascade Lake (MCASCADELAKE)"
|
|
infoe "32. Intel Cooper Lake (MCOOPERLAKE)"
|
|
infoe "33. Intel Tiger Lake (MTIGERLAKE)"
|
|
infoe "34. Intel Sapphire Rapids (MSAPPHIRERAPIDS)"
|
|
infoe "35. Intel Rocket Lake (MROCKETLAKE)"
|
|
infoe "36. Intel Alder Lake (MALDERLAKE)"
|
|
infoe "37. Intel Raptor Lake (MRAPTORLAKE)"
|
|
infoe "38. Intel Meteor Lake (MMETEORLAKE)"
|
|
infoe "39. Intel Emerald Rapids (MEMERALDRAPIDS)"
|
|
infoe "40. Generic-x86-64 (GENERIC_CPU)"
|
|
infoe "41. Generic-x86-64-v2 (GENERIC_CPU2)"
|
|
infoe "42. Generic-x86-64-v3 (GENERIC_CPU3)"
|
|
infoe "43. Generic-x86-64-v4 (GENERIC_CPU4)"
|
|
infoe "44. Intel-Native optimizations autodetected by the compiler (MNATIVE_INTEL)"
|
|
infoe "45. AMD-Native optimizations autodetected by the compiler (MNATIVE_AMD)"
|
|
read -rp ":: Which sub architecture do you want to build Linux for (see above, enter 40 if unsure) [40]? " BUILDTOOL_SUBARCHITECTURE
|
|
case "${BUILDTOOL_SUBARCHITECTURE}" in
|
|
"")
|
|
export "BUILDTOOL_PKGBUILD_SUBARCHITECTURE=_subarch=40"
|
|
;;
|
|
*[!0-9]*)
|
|
errorh "Invalid subarchitecture"
|
|
ask_subarchitecture
|
|
return 1
|
|
;;
|
|
*)
|
|
if [ ! "${BUILDTOOL_SUBARCHITECTURE}" -gt 0 ] || [ ! "${BUILDTOOL_SUBARCHITECTURE}" -lt 46 ]; then
|
|
errorh "Invalid subarchitecture"
|
|
ask_subarchitecture
|
|
else
|
|
export "BUILDTOOL_PKGBUILD_SUBARCHITECTURE=_subarch=${BUILDTOOL_SUBARCHITECTURE}"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
## Ask for kernel debug mode
|
|
function ask_debug() {
|
|
read -rp "$(infoh "Do you want to enable kernel debug mode [y/N/i(gnore)]? ")" BUILDTOOL_KERNELDEBUG
|
|
case "${BUILDTOOL_KERNELDEBUG}" in
|
|
"y"|"Y")
|
|
export "BUILDTOOL_PKGBUILD_DEBUG=_debug=y"
|
|
;;
|
|
"n"|"N"|"")
|
|
export "BUILDTOOL_PKGBUILD_DEBUG=_debug=n"
|
|
;;
|
|
"i"|"I")
|
|
export "BUILDTOOL_PKGBUILD_DEBUG=_debug="
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y, N or I"
|
|
ask_debug
|
|
esac
|
|
}
|
|
## Ask for clone directory conflict resolution (includes check)
|
|
function ask_clonedir_conflictresolution() {
|
|
if [ -n "${BUILDTOOL_LOCALDIR}" ]; then
|
|
warnh "Skipped conflict resolution (\$BUILDTOOL_LOCALDIR is set)"
|
|
export "BUILDTOOL_CLONEDIR_CONFLICT=r"
|
|
if [ -a "${BUILDTOOL_CLONEDIR}/src" ]; then
|
|
diagh "src/ directory present, appending \"--noextract\""
|
|
export "BUILDTOOL_MAKEPKG_REUSE= --noextract"
|
|
elif [ -a "${BUILDTOOL_CLONEDIR}/pkg" ]; then
|
|
diagh "pkg/ directory present, appending \"--force\""
|
|
export "BUILDTOOL_MAKEPKG_REUSE=${BUILDTOOL_MAKEPKG_REUSE} --force"
|
|
fi
|
|
return
|
|
fi
|
|
if [ ! -d "${BUILDTOOL_CLONEDIR}" ] && [ ! -L "${BUILDTOOL_CLONEDIR}" ]; then
|
|
return
|
|
elif [ -a "${BUILDTOOL_CLONEDIR}" ] && [ ! -d "${BUILDTOOL_CLONEDIR}" ] && [ ! -L "${BUILDTOOL_CLONEDIR}" ]; then
|
|
warnh "Something that isn't a directory or symlink exists at location \"${BUILDTOOL_CLONEDIR}\""
|
|
fi
|
|
read -rp "$(infoh "Warning: \"${BUILDTOOL_CLONEDIR}\" already exists. What should be done to resolve the conflict [A(bort)/r(ecompile)/f(resh)]? ")" BUILDTOOL_CLONEDIR_CONFLICT
|
|
case "${BUILDTOOL_CLONEDIR_CONFLICT}" in
|
|
"a"|"A"|"")
|
|
errorh "Conflict resolution failed"
|
|
exit 1
|
|
;;
|
|
"r"|"R")
|
|
if [ -a "${BUILDTOOL_CLONEDIR}/src" ]; then
|
|
diagh "src/ directory present, appending \"--noextract\""
|
|
export "BUILDTOOL_MAKEPKG_REUSE= --noextract"
|
|
elif [ -a "${BUILDTOOL_CLONEDIR}/pkg" ]; then
|
|
diagh "pkg/ directory present, appending \"--force\""
|
|
export "BUILDTOOL_MAKEPKG_REUSE=${BUILDTOOL_MAKEPKG_REUSE} --force"
|
|
fi
|
|
;;
|
|
"f"|"F")
|
|
infoh "Removing existing \$BUILDTOOL_CLONEDIR directory"
|
|
rm -rf "${BUILDTOOL_CLONEDIR}"
|
|
;;
|
|
*)
|
|
errorh "Invalid answer. Please answer with Y, N or I"
|
|
ask_debug
|
|
esac
|
|
}
|
|
ask_mold
|
|
ask_cpupower
|
|
ask_tmpfs
|
|
ask_xconfig
|
|
ask_nconfig
|
|
ask_cpfinalconfig
|
|
ask_modprobeddb
|
|
ask_subarchitecture
|
|
ask_debug
|
|
ask_clonedir_conflictresolution
|
|
|
|
# Pre-building
|
|
## Install dependencies
|
|
infoh "Installing dependencies"
|
|
# shellcheck disable=SC2086
|
|
if ! sudo pacman -Syu --asdeps --needed ${BUILDTOOL_PACKAGES}; then
|
|
errorh "Installing dependencies failed: pacman returned with non-zero exit code"
|
|
exit 1
|
|
fi
|
|
if ! rustup default nightly; then
|
|
errorh "Installing dependencies failed: rustup returned with non-zero exit code"
|
|
exit 1
|
|
fi
|
|
## Adjust cpu governor
|
|
case "${BUILDTOOL_CPUPOWER}" in
|
|
"y"|"Y"|"")
|
|
infoh "Adjusting cpu governor"
|
|
if ! sudo cpupower frequency-set -g performance; then
|
|
errorh "Adjusting cpu governor failed: cpupower returned with non-zero exit code"
|
|
return 1
|
|
fi
|
|
;;
|
|
"n"|"N") ;;
|
|
*)
|
|
errorh "Internal inconsistency detected: Value of \$BUILDTOOL_CPUPOWER is not a valid boolean answer"
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
# Clone repository
|
|
if [ "${BUILDTOOL_CLONEDIR_CONFLICT}" == "r" ] || [ "${BUILDTOOL_CLONEDIR_CONFLICT}" == "R" ]; then
|
|
warnh "Skipping cloning process"
|
|
else
|
|
infoh "Cloning repository"
|
|
git clone "${BUILDTOOL_REPOSITORY}" "${BUILDTOOL_CLONEDIR}"
|
|
fi
|
|
cd "${BUILDTOOL_CLONEDIR}"
|
|
|
|
# Print debug information
|
|
if [ -n "${BUILDTOOL_DEBUG}" ]; then
|
|
diagh "Printing debug information"
|
|
diage "Environment variables:"
|
|
env|grep "BUILDTOOL_"|sort
|
|
diagh "More debug information:"
|
|
diage "Build cmdline=${BUILDTOOL_BUILDCMDLINE} ${BUILDTOOL_PKGBUILD_XCONFIG} ${BUILDTOOL_PKGBUILD_NCONFIG} ${BUILDTOOL_PKGBUILD_CPFINALCONFIG} ${BUILDTOOL_PKGBUILD_MODPROBEDDB} ${BUILDTOOL_PKGBUILD_SUBARCHITECTURE} ${BUILDTOOL_PKGBUILD_DEBUG} makepkg --syncdeps -p PKGBUILD.buildtool"
|
|
diage "Cwd=$(pwd)"
|
|
fi
|
|
|
|
# Build package
|
|
infoh "Building package"
|
|
# shellcheck disable=SC2086
|
|
if ! ${BUILDTOOL_BUILDCMDLINE} "${BUILDTOOL_PKGBUILD_XCONFIG}" "${BUILDTOOL_PKGBUILD_NCONFIG}" "${BUILDTOOL_PKGBUILD_CPFINALCONFIG}" "${BUILDTOOL_PKGBUILD_MODPROBEDDB}" "${BUILDTOOL_PKGBUILD_SUBARCHITECTURE}" "${BUILDTOOL_PKGBUILD_DEBUG}" makepkg --syncdeps${BUILDTOOL_MAKEPKG_REUSE} -p PKGBUILD.buildtool; then
|
|
errorh "Failed package compilation: makepkg returned with non-zero exit code"
|
|
errore "Please report this at \"${BUILDTOOL_ISSUETRACKER}\""
|
|
exit 1
|
|
fi
|
|
|
|
# Finish message
|
|
infoh "Finished kernel compilation successfully"
|
|
infoe "You can now install your kernel and kernel headers."
|