Last active
February 1, 2024 20:38
-
-
Save rlcamp/daa32007c6c9d501c2c2ecee5c28e384 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# richard campbell | |
# isc license | |
# | |
# assumptions/caveats: | |
# - this script is run by bash 3.2+, produces a Makefile suitable for gnu make 3.81+ | |
# - targets to build have a main function in a .c file of the same name | |
# - each module depended upon is in the same directory | |
# - each module depended upon consists of one .c and one .h file | |
# - if you need to link in external libraries, you still need to manually provide those to make via LDLIBS | |
# todo: | |
# - some things could probably be bash arrays rather than multiline strings | |
# - this thing crawls the entire tree twice, once to build the header dependencies and once to build the link-time dependencies. probably an easy way to do them both at once | |
set -euo pipefail | |
# if a Makefile already exists that was not autogenerated by this code, then just write to stdout | |
if ([ ! -f Makefile ] || grep '# autogenerated' Makefile >/dev/null) && [ -t 1 ]; then exec 1>Makefile ; fi | |
# this function could be implemented using cc -MM but it would still require all of the postprocessing | |
function get_headers_recursive() { | |
for module in $(sed -n 's/\#include \"\([A-Za-z0-9_]*\)\.h\"/\1/p' ${1}.[c,h] | grep -v '^'$1'$' | sort -u); do | |
printf "${module}\n" | |
get_headers_recursive $module | |
done | |
} | |
printf '# autogenerated\n' | |
printf '# if you ever see a segfault or other unexpected nondeterminism in this (or any other) code,\n' | |
printf '# the immediate muscle memory response should be to recompile with the following, and then\n' | |
printf '# rerun the failing use case:\n' | |
printf '# make clean && make CFLAGS="-Og -g -fno-inline -fsanitize=address,undefined"\n\n' | |
printf '# overrideable vars used in implicit make rules\n' | |
printf '# only default to -march=native if not on an arm mac\n' | |
printf 'ifeq (,$(findstring arm64,$(shell uname -m)))\n' | |
printf 'TARGET_ARCH ?= -march=native\n' | |
printf 'endif\n' | |
printf '\n' | |
printf 'CFLAGS ?= %s\n' "${CFLAGS:--Os}" | |
printf '# older versions of gcc need -fcx-limited-range, in others its effect is implied by -ffinite-math-only\n' | |
printf 'ifeq (0,$(shell cc -fcx-limited-range -x c -o /dev/null -c - < /dev/null 2>/dev/null; echo $$?))\n' | |
printf '\tCFLAGS += -fcx-limited-range\n' | |
printf 'endif\n' | |
printf '\n' | |
printf 'CPPFLAGS += -Wall -Wextra -Wshadow -Wmissing-prototypes\n' | |
printf 'LDFLAGS += ${CFLAGS}\n' | |
if printenv LDLIBS >/dev/null; then printf 'LDLIBS ?= %s\n' "$LDLIBS"; fi | |
printf '\n' | |
TARGETS=$(grep -l 'int main(' *.c | sed 's/\.c//g') | |
TARGETS_STRING=$(for target in ${TARGETS}; do printf "${target} " ; done | sed 's/ $//') | |
printf '# list of targets to build, generated from .c files containing a main() function:\n\n' | |
printf 'TARGETS=%s\n\n' "${TARGETS_STRING}" | |
printf 'all : ${TARGETS}\n\n' | |
ALL_POSSIBLE_HEADER_FILES='' | |
printf '# for each target, the list of objects to link, generated by recursively crawling include statements with a corresponding .c file:\n\n' | |
for target in ${TARGETS}; do | |
HEADER_FILES=$(get_headers_recursive $target | sort -u) | |
ALL_POSSIBLE_HEADER_FILES=$((printf "${ALL_POSSIBLE_HEADER_FILES}\n${target}\n${HEADER_FILES}") | sort -u) | |
printf "${target} : ${target}.o$(for object in $HEADER_FILES; do if [ -f ${object}.c ]; then printf ' %s.o' $object; fi; done)\n"; | |
done | |
printf '\n' | |
printf '# for each object, the list of headers it depends on, generated by recursively crawling include statements:\n\n' | |
for object in ${ALL_POSSIBLE_HEADER_FILES}; do | |
if [ ! -f ${object}.c ]; then continue; fi | |
HEADER_FILES=$(get_headers_recursive $object | sort -u) | |
printf "${object}.o :$(for header in $HEADER_FILES ${object}; do if [ -f ${header}.h ]; then printf ' %s.h' $header; fi; done)\n" | |
done | |
printf '\n' | |
printf '*.o : Makefile\n' | |
printf '\n' | |
printf 'clean :\n\t$(RM) -rf *.o *.dSYM ${TARGETS}\n' | |
printf '.PHONY: clean all\n' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# autogenerated | |
# if you ever see a segfault or other unexpected nondeterminism in this (or any other) code, | |
# the immediate muscle memory response should be to recompile with the following, and then | |
# rerun the failing use case: | |
# make clean && make CFLAGS="-Og -g -fno-inline -fsanitize=address,undefined" | |
# overrideable vars used in implicit make rules | |
# only default to -march=native if not on an arm mac | |
ifeq (,$(findstring arm64,$(shell uname -m))) | |
TARGET_ARCH ?= -march=native | |
endif | |
CFLAGS ?= -Os | |
# older versions of gcc need -fcx-limited-range, in others its effect is implied by -ffinite-math-only | |
ifeq (0,$(shell cc -fcx-limited-range -x c -o /dev/null -c - < /dev/null 2>/dev/null; echo $$?)) | |
CFLAGS += -fcx-limited-range | |
endif | |
CPPFLAGS += -Wall -Wextra -Wshadow -Wmissing-prototypes | |
LDFLAGS += ${CFLAGS} | |
# list of targets to build, generated from .c files containing a main() function: | |
TARGETS=qrpn | |
all : ${TARGETS} | |
# for each target, the list of objects to link, generated by recursively crawling include statements with a corresponding .c file: | |
qrpn : qrpn.o | |
# for each object, the list of headers it depends on, generated by recursively crawling include statements: | |
qrpn.o : qrpn.h | |
*.o : Makefile | |
clean : | |
$(RM) -rf *.o *.dSYM ${TARGETS} | |
.PHONY: clean all |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
note that this scales as O(N^🤭) in the number of files, there are probably much better solutions