switched to cbuild.sh
This commit is contained in:
parent
dbdb2ee926
commit
f9f294232d
3 changed files with 300 additions and 29 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
obj/*
|
obj/*
|
||||||
nsnake
|
nsnake
|
||||||
|
.makefile
|
||||||
|
|
29
Makefile
29
Makefile
|
@ -1,29 +0,0 @@
|
||||||
CC = gcc
|
|
||||||
CFLAGS = -Wall -Wextra -pedantic-errors -std=c99 -Wno-format -lncurses
|
|
||||||
OUTFILE = nsnake
|
|
||||||
|
|
||||||
SRCDIR = src
|
|
||||||
OBJDIR = obj
|
|
||||||
INCDIR = src/include
|
|
||||||
|
|
||||||
SRC = $(wildcard $(SRCDIR)/*.c)
|
|
||||||
INC = $(wildcard $(INCDIR)/*.h)
|
|
||||||
_OBJ = $(SRC:.c=.o)
|
|
||||||
OBJ = $(subst $(SRCDIR),$(OBJDIR),$(_OBJ))
|
|
||||||
|
|
||||||
$(OUTFILE): $(OBJ) config.h
|
|
||||||
$(CC) $(CFLAGS) -o $(OUTFILE) $(OBJ)
|
|
||||||
|
|
||||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(INCDIR)/%.h config.h
|
|
||||||
@mkdir -p $(@D)
|
|
||||||
$(CC) $(CFLAGS) -o $@ -c $<
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
-rm $(OUTFILE) $(OBJ)
|
|
||||||
|
|
||||||
.PHONY: install
|
|
||||||
install: $(OUTFILE)
|
|
||||||
cp $(OUTFILE) /usr/local/bin
|
|
||||||
chown root /usr/local/bin/$(OUTFILE)
|
|
||||||
chmod 755 /usr/local/bin/$(OUTFILE)
|
|
299
cbuild.sh
Executable file
299
cbuild.sh
Executable file
|
@ -0,0 +1,299 @@
|
||||||
|
#!/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="nsnake"
|
||||||
|
SRCDIR="src"
|
||||||
|
OBJDIR="obj"
|
||||||
|
MAKEFILE=".makefile"
|
||||||
|
|
||||||
|
PKG_CONFIG_LIBS="ncurses"
|
||||||
|
|
||||||
|
HEADER_DIR="include"
|
||||||
|
# -------------------
|
||||||
|
|
||||||
|
PROG_COMMAND="./cbuild.sh __progress__"
|
||||||
|
SRC="$(find ${SRCDIR} -name '*\.c' 2> /dev/null)"
|
||||||
|
OBJ="$(find ${SRCDIR} -name '*\.c' 2> /dev/null |
|
||||||
|
sed "s/${SRCDIR}\//${OBJDIR}\//" |
|
||||||
|
sed "s/\.c$/.o/" |
|
||||||
|
tr '\n' ' ')"
|
||||||
|
|
||||||
|
srcnum="$(find ${SRCDIR} -name '*\.c' -exec printf %c {} + 2> /dev/null | wc -c)"
|
||||||
|
|
||||||
|
CFLAGS="${CFLAGS} $(pkg-config --cflags "${PKG_CONFIG_LIBS}")"
|
||||||
|
LDLIBS="${CFLAGS} $(pkg-config --libs "${PKG_CONFIG_LIBS}")"
|
||||||
|
|
||||||
|
clear_formatting="\033[0m"
|
||||||
|
bold="\033[1m"
|
||||||
|
info_color="\033[34m" # default: "\033[34m" - blue
|
||||||
|
err_color="\033[31m" # default: "\033[31m" - red
|
||||||
|
build_progress_color="\033[35m" # default: "\033[35m" - magenta
|
||||||
|
build_info_color="\033[32m" # default: "\033[32m" - green
|
||||||
|
link_info_color="\033[36m" # default: "\033[36m" - cyan
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
less <<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)
|
||||||
|
|
||||||
|
PKG_CONFIG_LIBS uses flags generated by pkg-config for the given library
|
||||||
|
name.
|
||||||
|
|
||||||
|
HEADER_DIR the directory in which header files will be placed by the 'new'
|
||||||
|
command. Note that this is relative to SRCDIR. Setting this to
|
||||||
|
'.' means headers will be placed in the same directory as
|
||||||
|
source files. Setting this to 'include' means headers will be
|
||||||
|
placed in SRCDIR/include. (defualt: include)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
./cbuild.sh [COMMAND]
|
||||||
|
|
||||||
|
COMMAND:
|
||||||
|
--help display this help message
|
||||||
|
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.
|
||||||
|
init equivalent to 'new module main' (see below).
|
||||||
|
dryrun print all commands that will be executed during the build process
|
||||||
|
to stdout.
|
||||||
|
|
||||||
|
new header [NAME] [...]
|
||||||
|
generate a header file with name NAME in HEADER_DIR. Multiple
|
||||||
|
NAMEs may be provided. Including '.h' in NAME is optional.
|
||||||
|
|
||||||
|
new module [NAME] [...]
|
||||||
|
generate a new source file NAME.c in SRCDIR and a new header
|
||||||
|
NAME.h in HEADER_DIR. Multiple NAMEs may be provided.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
export_vars() {
|
||||||
|
export CC
|
||||||
|
export CFLAGS
|
||||||
|
export LDFLAGS
|
||||||
|
export LDLIBS
|
||||||
|
export TARGET
|
||||||
|
export OBJ
|
||||||
|
export PROG_COMMAND
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
printf "${info_color}${bold}[*]${clear_formatting}\
|
||||||
|
${info_color} %s${clear_formatting} %s${info_color} %s${clear_formatting}\n" \
|
||||||
|
"${1}" "${2}" "${3}"
|
||||||
|
}
|
||||||
|
|
||||||
|
err() {
|
||||||
|
printf "${err_color}${bold}[!]${clear_formatting}\
|
||||||
|
${err_color} %s${clear_formatting} %s${err_color} %s${clear_formatting}\n" \
|
||||||
|
"${1}" "${2}" "${3}"
|
||||||
|
}
|
||||||
|
|
||||||
|
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 " "${srcname%$(basename ${srcname})}${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" "${TARGET}" "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" "./${TARGET} ${args}"
|
||||||
|
"./${TARGET}" $@ ||
|
||||||
|
err "Run FAILURE" "./${TARGET}" "returned $?"
|
||||||
|
}
|
||||||
|
|
||||||
|
dry_run() {
|
||||||
|
gen_makefile
|
||||||
|
export_vars
|
||||||
|
make -f "${MAKEFILE}" -e "${TARGET}" -n
|
||||||
|
}
|
||||||
|
|
||||||
|
new() {
|
||||||
|
case $1 in
|
||||||
|
header)
|
||||||
|
shift 1
|
||||||
|
for arg in $@; do
|
||||||
|
if [ "${arg}" != "__MODULE__" ]; then
|
||||||
|
path="${SRCDIR}/${HEADER_DIR}/${arg%.h}.h"
|
||||||
|
clean_path="$(echo ${path} | sed 's/\.\///g')"
|
||||||
|
macro="$(basename ${clean_path} .h | tr '[a-z]' '[A-Z]')"
|
||||||
|
[ ! -d "$(dirname ${clean_path})" ] &&
|
||||||
|
mkdir -p "$(dirname ${clean_path})"
|
||||||
|
if [ ! -f "${clean_path}" ]; then
|
||||||
|
cat > "${clean_path}" <<EOF
|
||||||
|
#ifndef ${macro}_H
|
||||||
|
#define ${macro}_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
[ "${2}" != "__MODULE__" ] &&
|
||||||
|
err "Header" "${clean_path}" "already exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "${2}" != "__MODULE__" ] &&
|
||||||
|
info "Created header" "${clean_path}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
module)
|
||||||
|
shift 1
|
||||||
|
for arg in $@; do
|
||||||
|
./cbuild.sh new header "${arg}" '__MODULE__'
|
||||||
|
path="${SRCDIR}/${arg}.c"
|
||||||
|
if [ "${HEADER_DIR}" = "." ]; then
|
||||||
|
include="./$(basename ${arg}).h"
|
||||||
|
else
|
||||||
|
include="$(dirname ${path#${SRCDIR}/} |
|
||||||
|
sed -e 's/\/[A-z]*/\/../g' \
|
||||||
|
-e 's/[A-z]*\//..\//' \
|
||||||
|
-e 's/^[A-z]*$/../')/${HEADER_DIR}/${arg}.h"
|
||||||
|
fi
|
||||||
|
[ ! -d "$(dirname ${path})" ] &&
|
||||||
|
mkdir -p "$(dirname ${path})"
|
||||||
|
if [ ! -f "${path}" ]; then
|
||||||
|
cat > "${path}" <<EOF
|
||||||
|
#include "${include}"
|
||||||
|
|
||||||
|
EOF
|
||||||
|
[ "${arg}" != "main" ] && echo >> "${path}" ||
|
||||||
|
cat >> "${path}" <<EOF
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
err "Module" "${path%.c}" "already exists"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Created module" "${path%.c}"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
__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 "${build_progress_color}${bold}[%3d/%d]${clear_formatting} " \
|
||||||
|
"${targetno}" "${ntargets}"
|
||||||
|
printf "${build_info_color}Building${clear_formatting} "
|
||||||
|
elif [ "${1}" = "link" ]; then
|
||||||
|
printf "${link_info_color}${bold}[%3d/%d]${clear_formatting} " "${targetno}" "${ntargets}"
|
||||||
|
printf "${link_info_color}${bold}Linking${clear_formatting} "
|
||||||
|
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;;
|
||||||
|
new) shift 1 && new $@;;
|
||||||
|
init) new module main;;
|
||||||
|
-h|--help|help) usage;;
|
||||||
|
__progress__) __progress__ "${2}" "${3}";;
|
||||||
|
*) echo "Invalid command. Try --help";;
|
||||||
|
esac
|
Loading…
Reference in a new issue