190 lines
4.9 KiB
Bash
Executable file
190 lines
4.9 KiB
Bash
Executable file
#!/bin/sh
|
||
|
||
# Written by Noah Swerhun
|
||
# https://noahsw.xyz
|
||
# Development: https://git.noahsw.xyz/cbuild.sh/log.html
|
||
|
||
# --- USER CONFIG ---
|
||
CC="gcc"
|
||
CFLAGS="-Wall -Wpedantic"
|
||
LDFLAGS=""
|
||
LDLIBS=""
|
||
TARGET="a.out"
|
||
SRCDIR="src"
|
||
OBJDIR="obj"
|
||
MAKEFILE=".makefile"
|
||
# -------------------
|
||
|
||
PROG_COMMAND="./cbuild.sh __progress__"
|
||
SRC="$(find ${SRCDIR} -name '*\.c')"
|
||
OBJ="$(find ${SRCDIR} -name '*\.c' |
|
||
sed "s/${SRCDIR}\//${OBJDIR}\//" |
|
||
sed "s/\.c$/.o/" |
|
||
tr '\n' ' ')"
|
||
|
||
srcnum="$(find ${SRCDIR} -name '*\.c' -exec printf %c {} + | wc -c)"
|
||
|
||
usage() {
|
||
cat <<EOF
|
||
cbuild.sh: A simple, customizable, automated, and portable build script for C
|
||
projects. This script works by automatically detecting .c source files in
|
||
SRCDIR, generating a makefile, compiling them into objects in OBJDIR, and
|
||
finally linking them to TARGET.
|
||
|
||
Customization:
|
||
Open cbuild.sh in your editor and navigate to the USER CONFIG section (at
|
||
the top of the file).
|
||
|
||
CC the compiler to use (default: gcc)
|
||
CFLAGS compilation flags (default: -Wall -Wpedantic)
|
||
LDFLAGS linker flags (default: none)
|
||
LDLIBS linker libraries (default: none)
|
||
TARGET final target to link (default: a.out)
|
||
SRCDIR the directory where the script will search for .c source files
|
||
(default: src)
|
||
OBJDIR the directory where compiled objects will be placed. (default: obj)
|
||
MAKEFILE the filename of the generated makefile (default: .makefile)
|
||
|
||
Usage:
|
||
./cbuild.sh [COMMAND]
|
||
|
||
COMMAND:
|
||
build generate the makefile, compile objects and link target.
|
||
clean remove makefile, objects, and target.
|
||
buildcn clean, and then build.
|
||
generate ONLY generate the makefile.
|
||
run [ARGS] build, then execute TARGET with arguments ARGS.
|
||
dryrun print all commands that will be executed during the build process
|
||
to stdout.
|
||
EOF
|
||
}
|
||
|
||
export_vars() {
|
||
export CC
|
||
export CFLAGS
|
||
export LDFLAGS
|
||
export LDLIBS
|
||
export TARGET
|
||
export OBJ
|
||
export PROG_COMMAND
|
||
}
|
||
|
||
info() {
|
||
printf "[34;1m[*][21m %s[0m\n" "$@"
|
||
}
|
||
|
||
err() {
|
||
printf "[31;1m[!][21m %s[0m\n" "$@"
|
||
}
|
||
|
||
gen_makefile() {
|
||
info "Generating makefile..."
|
||
[ -f "${MAKEFILE}" ] && chmod +w "${MAKEFILE}"
|
||
cat > "${MAKEFILE}" <<'EOF'
|
||
##########################
|
||
# GENERATED BY cbuild.sh #
|
||
# DO NOT MODIFY BY HAND #
|
||
##########################
|
||
.POSIX:
|
||
|
||
$(TARGET): $(OBJ)
|
||
@$(PROG_COMMAND) link $(TARGET)
|
||
@$(CC) $(LDFLAGS) -o $(TARGET) $(OBJ) $(LDLIBS)
|
||
|
||
EOF
|
||
i=1
|
||
echo "${SRC}" | while read srcname; do
|
||
objname="$(echo "${srcname}" |
|
||
sed "s/${SRCDIR}\//${OBJDIR}\//" |
|
||
sed "s/\.c$/.o/")"
|
||
|
||
includes="$(grep '#include ".*"' ${srcname} |
|
||
awk -F\" '{ print $2 }')"
|
||
printf "%s: %s " "${objname}" "${srcname}" >> "${MAKEFILE}"
|
||
for line in $includes; do
|
||
printf "%s " "${file%$(basename ${file})}${line}" >> "${MAKEFILE}"
|
||
done
|
||
printf "\n" >> "${MAKEFILE}"
|
||
|
||
|
||
printf "\t@%s object %s\n" \
|
||
'$(PROG_COMMAND)' "${objname}" >> "${MAKEFILE}"
|
||
|
||
printf "\t@%s %s -o %s -c %s\n" '$(CC)' '$(CFLAGS)' \
|
||
"${objname}" "${srcname}" >> "${MAKEFILE}"
|
||
|
||
i=$((${i}+1))
|
||
done
|
||
chmod -w "${MAKEFILE}"
|
||
}
|
||
|
||
build() {
|
||
gen_makefile
|
||
[ ! -d "${OBJDIR}" ] && mkdir "${OBJDIR}"
|
||
export_vars
|
||
make -f "${MAKEFILE}" -e "${TARGET}" -n | grep -c "^${CC}" > .cbuild_prog.tmp
|
||
if [ "$(cat .cbuild_prog.tmp)" = 0 ]; then
|
||
info "Target [0m${TARGET}[34m up to date"
|
||
rm .cbuild_prog.tmp
|
||
return 0;
|
||
fi
|
||
info "Beginning build..."
|
||
make -f "${MAKEFILE}" -e "${TARGET}"
|
||
ret=$?
|
||
[ "${ret}" = 0 ] &&
|
||
info "Build SUCCESSFUL" ||
|
||
err "Build FAILURE"
|
||
rm .cbuild_prog.tmp
|
||
return "${ret}"
|
||
}
|
||
|
||
clean() {
|
||
[ -d "${OBJDIR}" ] &&
|
||
rm -rf "${OBJDIR}"
|
||
[ -f "${MAKEFILE}" ] &&
|
||
chmod +w "${MAKEFILE}"
|
||
[ -f "${TARGET}" ] && [ -f "${MAKEFILE}" ] &&
|
||
rm "${TARGET}" "${MAKEFILE}"
|
||
}
|
||
|
||
run() {
|
||
build || exit $?
|
||
args="$@"
|
||
info "Running [0m./${TARGET} ${args}"
|
||
"./${TARGET}" $@ ||
|
||
err "Run FAILURE [0m./${TARGET}[31m returned $?"
|
||
}
|
||
|
||
dry_run() {
|
||
gen_makefile
|
||
export_vars
|
||
make -f "${MAKEFILE}" -e "${TARGET}" -n
|
||
}
|
||
|
||
__progress__() {
|
||
ntargets=$(cat .cbuild_prog.tmp)
|
||
export_vars
|
||
rtargets="$(make -f "${MAKEFILE}" -e "${TARGET}" -n | grep -c "^${CC}")"
|
||
targetno=$((${ntargets} - ${rtargets} + 1))
|
||
if [ "${1}" = "object" ]; then
|
||
printf '[35;1m[%3d/%d][0m ' "${targetno}" "${ntargets}"
|
||
printf '[32mBuilding[0m '
|
||
elif [ "${1}" = "link" ]; then
|
||
printf '[36;1m[%3d/%d][0m ' "${targetno}" "${ntargets}"
|
||
printf '[36;1mLinking[0m '
|
||
fi
|
||
|
||
echo "${2}"
|
||
}
|
||
|
||
case $1 in
|
||
build) build;;
|
||
clean) clean;;
|
||
buildcn) clean ; build;;
|
||
generate) gen_makefile && info "Done";;
|
||
run) shift 1 && run $@;;
|
||
dryrun) dry_run;;
|
||
__progress__) __progress__ "${2}" "${3}";;
|
||
*) usage;;
|
||
esac
|