#!/bin/sh # shmake_sync_makefile.sh - Project synchronization for shmake # Target: bmake with .POSIX: compatibility sync_project() { printf "Syncing project files...\n" SOURCE_FILES_LIST=$(mktemp) SHARED_SOURCE_LIST=$(mktemp) SHARED_BINARY_LIST=$(mktemp) TEST_SOURCE_LIST=$(mktemp) TEST_BINARY_LIST=$(mktemp) DEBUG_SOURCE_LIST=$(mktemp) DEBUG_BINARY_LIST=$(mktemp) RELEASE_SOURCE_LIST=$(mktemp) RELEASE_BINARY_LIST=$(mktemp) INCLUDE_DIRS_LIST=$(mktemp) LINKED_LIBS_LIST=$(mktemp) trap 'rm -f "$SOURCE_FILES_LIST" "$SHARED_SOURCE_LIST" "$SHARED_BINARY_LIST" "$TEST_SOURCE_LIST" "$TEST_BINARY_LIST" "$DEBUG_SOURCE_LIST" "$DEBUG_BINARY_LIST" "$RELEASE_SOURCE_LIST" "$RELEASE_BINARY_LIST" "$INCLUDE_DIRS_LIST" "$LINKED_LIBS_LIST"' EXIT printf "Scanning source_code/ for source files...\n" [ -d "source_code" ] && find source_code -type f -name '*.c' ! -path '*/internal/*' 2>/dev/null | while read -r f; do printf '%s\n' "$f" >> "$SOURCE_FILES_LIST" done printf "Scanning external_code/shared_libraries/ for source files...\n" [ -d "external_code/shared_libraries/source_code" ] && find external_code/shared_libraries/source_code -type f -name '*.c' ! -path '*/internal/*' 2>/dev/null | while read -r f; do case "$f" in *example*) printf "Skipping %s (example code)\n" "$f" >&2 ;; *) printf '%s\n' "$f" >> "$SHARED_SOURCE_LIST" ;; esac done printf "Scanning external_code/shared_libraries/binary/ for binary libraries...\n" [ -d "external_code/shared_libraries/binary" ] && find external_code/shared_libraries/binary -type f \( -name '*.a' -o -name '*.so' -o -name '*.lib' -o -name '*.dylib' \) 2>/dev/null | while read -r f; do printf '%s\n' "$f" >> "$SHARED_BINARY_LIST" done printf "Scanning external_code/test_only/ for source files...\n" [ -d "external_code/test_only/source_code" ] && find external_code/test_only/source_code -type f -name '*.c' ! -path '*/internal/*' 2>/dev/null | while read -r f; do case "$f" in *example*) printf "Skipping %s (example code)\n" "$f" >&2 ;; *) printf '%s\n' "$f" >> "$TEST_SOURCE_LIST" ;; esac done printf "Scanning external_code/test_only/binary/ for test libraries...\n" [ -d "external_code/test_only/binary" ] && find external_code/test_only/binary -type f \( -name '*.a' -o -name '*.so' -o -name '*.lib' -o -name '*.dylib' \) 2>/dev/null | while read -r f; do printf '%s\n' "$f" >> "$TEST_BINARY_LIST" done printf "Scanning external_code/debug_only/ for source files...\n" [ -d "external_code/debug_only/source_code" ] && find external_code/debug_only/source_code -type f -name '*.c' ! -path '*/internal/*' 2>/dev/null | while read -r f; do case "$f" in *example*) printf "Skipping %s (example code)\n" "$f" >&2 ;; *) printf '%s\n' "$f" >> "$DEBUG_SOURCE_LIST" ;; esac done printf "Scanning external_code/debug_only/binary/ for debug libraries...\n" [ -d "external_code/debug_only/binary" ] && find external_code/debug_only/binary -type f \( -name '*.a' -o -name '*.so' -o -name '*.lib' -o -name '*.dylib' \) 2>/dev/null | while read -r f; do printf '%s\n' "$f" >> "$DEBUG_BINARY_LIST" done printf "Scanning external_code/release_only/ for source files...\n" [ -d "external_code/release_only/source_code" ] && find external_code/release_only/source_code -type f -name '*.c' ! -path '*/internal/*' 2>/dev/null | while read -r f; do case "$f" in *example*) printf "Skipping %s (example code)\n" "$f" >&2 ;; *) printf '%s\n' "$f" >> "$RELEASE_SOURCE_LIST" ;; esac done printf "Scanning external_code/release_only/binary/ for release libraries...\n" [ -d "external_code/release_only/binary" ] && find external_code/release_only/binary -type f \( -name '*.a' -o -name '*.so' -o -name '*.lib' -o -name '*.dylib' \) 2>/dev/null | while read -r f; do printf '%s\n' "$f" >> "$RELEASE_BINARY_LIST" done printf "Auto-detecting include directories across entire project...\n" find . -type d ! -path "*/internal/*" ! -path "./.git*" ! -path "./build_output*" ! -path "./intermediate_code*" ! -path "*/examples*" ! -path "*/example*" ! -path "./shmake_config*" 2>/dev/null | while read -r dir; do clean_dir=$(printf "%s" "$dir" | sed 's|^\./||') [ -z "$clean_dir" ] && continue [ "$clean_dir" = "." ] && continue find "$clean_dir" -maxdepth 1 -type f -name '*.h' 2>/dev/null | grep -q . && printf '%s\n' "$clean_dir" >> "$INCLUDE_DIRS_LIST" done printf "Reading linked libraries from shmake_config/linked.conf...\n" [ -f "shmake_config/linked.conf" ] && grep '^LINKED_LIBS' "shmake_config/linked.conf" | sed 's/^LINKED_LIBS[[:space:]]*=[[:space:]]*//' | while read -r line; do printf '%s\n' "$line" >> "$LINKED_LIBS_LIST" done printf "Sorting and deduplicating file lists...\n" for list in "$SOURCE_FILES_LIST" "$SHARED_SOURCE_LIST" "$SHARED_BINARY_LIST" \ "$TEST_SOURCE_LIST" "$TEST_BINARY_LIST" "$DEBUG_SOURCE_LIST" \ "$DEBUG_BINARY_LIST" "$RELEASE_SOURCE_LIST" "$RELEASE_BINARY_LIST" \ "$INCLUDE_DIRS_LIST" "$LINKED_LIBS_LIST"; do sort -u "$list" -o "$list" done printf "Reading project configuration...\n" if [ -f "shmake_config/project.conf" ]; then C_VERSION_RAW=$(grep -E '^C_VERSION[[:space:]]*=' "shmake_config/project.conf" | sed 's/^.*=[[:space:]]*//' | tr -d '"'\' | tr '[:upper:]' '[:lower:]') fi C_VERSION_LOWER=${C_VERSION_RAW:-c11} [ ! -f "shmake_config/linked.conf" ] && touch "shmake_config/linked.conf" write_objects_and_rules() { _list_file="$1" _prefix="$2" _conf_file="$3" printf '%s_OBJECTS = \\\n' "$_prefix" >> "$_conf_file" while IFS= read -r file; do [ -z "$file" ] && continue obj="intermediate_code/${file%%.c}.o" printf ' %s \\\n' "$obj" >> "$_conf_file" done < "$_list_file" printf '\n' >> "$_conf_file" while IFS= read -r file; do [ -z "$file" ] && continue obj="intermediate_code/${file%%.c}.o" dir_path=$(dirname "$obj") printf '%s: %s\n' "$obj" "$file" >> "$_conf_file" printf '\t@mkdir -p %s\n' "$dir_path" >> "$_conf_file" printf '\t$(CC) $(CFLAGS) -c %s -o %s\n\n' "$file" "$obj" >> "$_conf_file" done < "$_list_file" } printf "Generating makefile.conf...\n" cat > "shmake_config/makefile.conf" << 'CONF_HEADER' # --- shmake Base Configuration (Always Included) --- CONF_HEADER printf 'CFLAGS += -std=%s\n' "$C_VERSION_LOWER" >> "shmake_config/makefile.conf" printf 'INCLUDE_DIRS = \\\n' >> "shmake_config/makefile.conf" while IFS= read -r dir; do [ -n "$dir" ] && printf ' -I%s \\\n' "$dir" >> "shmake_config/makefile.conf" done < "$INCLUDE_DIRS_LIST" printf '\nCFLAGS += $(INCLUDE_DIRS)\n\n' >> "shmake_config/makefile.conf" printf 'SHARED_LIBS = \\\n' >> "shmake_config/makefile.conf" while IFS= read -r file; do [ -n "$file" ] && printf ' %s \\\n' "$file" >> "shmake_config/makefile.conf" done < "$SHARED_BINARY_LIST" printf '\n' >> "shmake_config/makefile.conf" write_objects_and_rules "$SOURCE_FILES_LIST" "PROJECT" "shmake_config/makefile.conf" write_objects_and_rules "$SHARED_SOURCE_LIST" "SHARED" "shmake_config/makefile.conf" printf "Generating makefile_test.conf...\n" cat > "shmake_config/makefile_test.conf" << 'CONF_HEADER' # --- shmake Test Configuration --- CONF_HEADER printf 'TEST_LIBS = \\\n' >> "shmake_config/makefile_test.conf" while IFS= read -r file; do [ -n "$file" ] && printf ' %s \\\n' "$file" >> "shmake_config/makefile_test.conf" done < "$TEST_BINARY_LIST" printf '\n' >> "shmake_config/makefile_test.conf" write_objects_and_rules "$TEST_SOURCE_LIST" "TEST" "shmake_config/makefile_test.conf" printf "Generating makefile_debug.conf...\n" cat > "shmake_config/makefile_debug.conf" << 'CONF_HEADER' # --- shmake Debug Configuration --- CONF_HEADER printf 'DEBUG_LIBS = \\\n' >> "shmake_config/makefile_debug.conf" while IFS= read -r file; do [ -n "$file" ] && printf ' %s \\\n' "$file" >> "shmake_config/makefile_debug.conf" done < "$DEBUG_BINARY_LIST" printf '\n' >> "shmake_config/makefile_debug.conf" printf 'CFLAGS += -g -O0 -DDEBUG\n\n' >> "shmake_config/makefile_debug.conf" write_objects_and_rules "$DEBUG_SOURCE_LIST" "DEBUG" "shmake_config/makefile_debug.conf" printf "Generating makefile_release.conf...\n" cat > "shmake_config/makefile_release.conf" << 'CONF_HEADER' # --- shmake Release Configuration --- CONF_HEADER printf 'RELEASE_LIBS = \\\n' >> "shmake_config/makefile_release.conf" while IFS= read -r file; do [ -n "$file" ] && printf ' %s \\\n' "$file" >> "shmake_config/makefile_release.conf" done < "$RELEASE_BINARY_LIST" printf '\n' >> "shmake_config/makefile_release.conf" printf 'CFLAGS += -O2 -DNDEBUG\n\n' >> "shmake_config/makefile_release.conf" write_objects_and_rules "$RELEASE_SOURCE_LIST" "RELEASE" "shmake_config/makefile_release.conf" printf "Generating makefile_clean.conf...\n" cat > "shmake_config/makefile_clean.conf" << 'CONF_HEADER' # --- shmake Clean Configuration --- CLEAN_TARGETS = build_output EXTRA_CLEAN = intermediate_code CONF_HEADER printf "Updating Root Makefile...\n" cat > "Makefile" << 'MAKEFILE_TEMPLATE' # --- shmake Root Makefile --- # Portable: Works on bmake in .POSIX: mode .POSIX: PROJECT_NAME ?= a.out .include "shmake_config/project.conf" .include "shmake_config/makefile.conf" .include "shmake_config/linked.conf" .if !defined(BUILD_MODE) BUILD_MODE = release .endif .if "${BUILD_MODE}" == "test" .include "shmake_config/makefile_test.conf" ALL_MODE_OBJECTS = ${PROJECT_OBJECTS} ${SHARED_OBJECTS} ${TEST_OBJECTS} ALL_MODE_LIBS = ${SHARED_LIBS} ${LINKED_LIBS} ${TEST_LIBS} .endif .if "${BUILD_MODE}" == "debug" .include "shmake_config/makefile_debug.conf" ALL_MODE_OBJECTS = ${PROJECT_OBJECTS} ${SHARED_OBJECTS} ${DEBUG_OBJECTS} ALL_MODE_LIBS = ${SHARED_LIBS} ${LINKED_LIBS} ${DEBUG_LIBS} .endif .if "${BUILD_MODE}" == "release" .include "shmake_config/makefile_release.conf" ALL_MODE_OBJECTS = ${PROJECT_OBJECTS} ${SHARED_OBJECTS} ${RELEASE_OBJECTS} ALL_MODE_LIBS = ${SHARED_LIBS} ${LINKED_LIBS} ${RELEASE_LIBS} .endif ALL_OBJECTS = ${ALL_MODE_OBJECTS} ALL_LIBS = ${ALL_MODE_LIBS} OUTPUT_DIR = build_output OBJ_DIR = intermediate_code CC ?= gcc .PHONY: all debug test release clean distclean all: @if [ "${BUILD_MODE}" = "debug" ]; then \ ${MAKE} debug; \ elif [ "${BUILD_MODE}" = "test" ]; then \ ${MAKE} test; \ else \ ${MAKE} release; \ fi debug: ${OUTPUT_DIR}/${PROJECT_NAME}-debug test: ${OUTPUT_DIR}/${PROJECT_NAME}-test release: ${OUTPUT_DIR}/${PROJECT_NAME} ${OUTPUT_DIR}/${PROJECT_NAME}-debug: ${ALL_OBJECTS} @mkdir -p ${OUTPUT_DIR} ${CC} ${CFLAGS} -o $@ ${ALL_OBJECTS} ${ALL_LIBS} ${OUTPUT_DIR}/${PROJECT_NAME}-test: ${ALL_OBJECTS} @mkdir -p ${OUTPUT_DIR} ${CC} ${CFLAGS} -o $@ ${ALL_OBJECTS} ${ALL_LIBS} ${OUTPUT_DIR}/${PROJECT_NAME}: ${ALL_OBJECTS} @mkdir -p ${OUTPUT_DIR} ${CC} ${CFLAGS} -o $@ ${ALL_OBJECTS} ${ALL_LIBS} .include "shmake_config/makefile_clean.conf" clean: rm -rf ${CLEAN_TARGETS} ${EXTRA_CLEAN} distclean: clean rm -rf ${OBJ_DIR} ${OUTPUT_DIR} MAKEFILE_TEMPLATE printf "\nSync complete!\n" }