Scafold

It's been a very long time since I've written my first post about the way I use make. Now that I'm reading it again, I realize I don't even use Makefiles anymore. Since few years now, I use Tox, too much Python development I guess.

I started (again) learning C programming, I had to write a simple Makefile for being able to easily and quickly build, install, remove my executables. The following is a Bash script I've created to bootstrap small C projects:

#!/bin/bash

GIT=`which git`
MSG="initial commit from $USER on $(date)"
CURRENT_DIRECTORY=`pwd`
PROJECT_NAME=$1
EXECUTABLE_NAME="${2:-kli}"

if [[ -z "${PROJECT_NAME}" ]]; then
  echo "Please add the name of the project"
  exit
fi

if [[ -d "${CURRENT_DIRECTORY}/${PROJECT_NAME}" ]]; then
  echo "The folder exists. Choose another name"
  exit
fi

# project tree
mkdir -p "${CURRENT_DIRECTORY}/${PROJECT_NAME}"/{build,src,docs,include,lib,tests,data}

# clang format config ???
clang-format --style=llvm -dump-config > "${CURRENT_DIRECTORY}/${PROJECT_NAME}"/.clang-format

# Makefile header
cat <<EOF >> "${CURRENT_DIRECTORY}/${PROJECT_NAME}"/Makefile
SHELL := /bin/sh
EXECUTABLE := $EXECUTABLE_NAME

EOF

# Makefile body
cat <<"EOF" >> "${CURRENT_DIRECTORY}/${PROJECT_NAME}"/Makefile

# https://matt.sh/howto-c

# where all bin will be moved for easy trying
BIN_DIR := $(shell echo $${HOME})/bin

# set build and source directories
BUILD_DIR := build
SRC_DIRS := src
LIBS := -lm -lpthread

# all source files are .c files under source directory
SRCS := $(shell find $(SRC_DIRS) -name *.c)

# all objects files are obtained from all the .c files in src directory
# and placed under build directory with the o extension
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)

# deps are .d files, obtained from all the .o files in the build directory
DEPS := $(OBJS:.o=.d)

# directories to include are all the directories under src directory
INC_DIRS := $(shell find $(SRC_DIRS) -type d)

# add -I in front of each subdirectory under src directory
INC_FLAGS := $(addprefix -I,$(INC_DIRS))

#
CFLAGS ?= $(INC_FLAGS) -g -Wall -Wfatal-errors -Wpedantic -Wextra -Werror -Wshadow -std=c11 -MMD -MP -march=native

install: $(BUILD_DIR)/$(EXECUTABLE) ## install prog
	@echo "installing..."
	$(CP) $(BUILD_DIR)/$(EXECUTABLE) $(BIN_DIR)

$(BUILD_DIR)/$(EXECUTABLE): $(OBJS) ## build executable
	@echo "building executable file..."
	$(CC) $(OBJS) -o $@ $(LDFLAGS) $(LIBS)  # CC and LDFLAGS are predefined see make -p -f /dev/null

$(BUILD_DIR)/%.o: %.c clean ## build object files
	@echo "building object files..."
	@$(MKDIR_P) $(dir $@)
    # $< evaluates to the first "prerequisite", $@ evaluates to the "target"
	$(CC) $(CFLAGS) -c $< -o $@

format:
	@echo "reformatting..."
	find . -iname *.h -o -iname *.c | xargs clang-format -style=file -i -fallback-style=none

clean: ## delete build files
	@echo "deleting old build..."
	@$(RM) -r $(BUILD_DIR)
	@$(RM) -r $(BIN_DIR)/$(EXECUTABLE)

# acts like include except that there is no error (not even a warning) if DEPS does not exists, sinclude is another name
-include $(DEPS)

# Meh :\
MKDIR_P ?= mkdir -p
CP ?= cp -r

.PHONY: clean install help format
EOF


# main source file
cat <<EOF > "${CURRENT_DIRECTORY}/${PROJECT_NAME}"/src/main.c
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>

int main (int argc, char **argv) {
    printf("Hello World.\n");
    exit(EXIT_SUCCESS);
}

EOF

# git it
cd ${PROJECT_NAME}
${GIT} init -q .
${GIT} add --all .
${GIT} commit -m "$MSG"