#!/usr/bin/env bash # configure - Configuration script for ft_ping # Generates build.mk with build configuration set -e # CONSTANTS SCRIPT_VERSION="1.0.0" PROJECT_NAME="ft_ping" BUILD_MK="build.mk" LIBICMP_DIR="libicmp" LIBICMP_MAKEFILE="Makefile" # Default values DEFAULT_PREFIX="/usr/local" DEFAULT_CC="clang" DEFAULT_LIBICMP_MODE="auto" DEFAULT_LIBRARY_TYPE="shared" DEFAULT_ENABLE_TESTS="no" DEFAULT_ENABLE_DEBUG="no" DEFAULT_ENABLE_SANITIZERS="no" DEFAULT_ENABLE_LTO="no" DEFAULT_ENABLE_STRICT="no" DEFAULT_SUDO_TESTS="yes" DEFAULT_INSTALL_LIBICMP="no" # Current configuration PREFIX="$DEFAULT_PREFIX" CC="$DEFAULT_CC" LIBICMP_MODE="$DEFAULT_LIBICMP_MODE" LIBRARY_TYPE="$DEFAULT_LIBRARY_TYPE" LIBRARY_PREFERENCE="prefer" # prefer, only ENABLE_TESTS="$DEFAULT_ENABLE_TESTS" ENABLE_DEBUG="$DEFAULT_ENABLE_DEBUG" ENABLE_SANITIZERS="$DEFAULT_ENABLE_SANITIZERS" ENABLE_LTO="$DEFAULT_ENABLE_LTO" ENABLE_STRICT="$DEFAULT_ENABLE_STRICT" SUDO_TESTS="$DEFAULT_SUDO_TESTS" INSTALL_LIBICMP="$DEFAULT_INSTALL_LIBICMP" VERBOSE="no" QUIET="no" # User-provided flags USER_CFLAGS="" USER_LDFLAGS="" USER_CPPFLAGS="" # Derived paths BINDIR="" LIBDIR="" INCLUDEDIR="" LIBICMP_PREFIX="" # HELPER FUNCTIONS info() { if [ "$QUIET" != "yes" ]; then echo -e "[INFO] $*" fi } success() { if [ "$QUIET" != "yes" ]; then echo -e "[OK] $*" fi } warn() { echo -e "[WARN] $*" >&2 } error() { echo -e "[ERROR] $*" >&2 } fatal() { error "$@" exit 1 } verbose() { if [ "$VERBOSE" = "yes" ]; then echo -e "[VERBOSE] $*" fi } # HELP show_help() { cat << EOF Usage: ./configure [OPTIONS] Configuration script for $PROJECT_NAME v$SCRIPT_VERSION libicmp Options: --with-system-libicmp Use system-installed libicmp (fail if not found) --with-local-libicmp Build local libicmp (ignore system version) --auto-libicmp Auto-detect libicmp (default) Library Type Options: --prefer-static Prefer static library (.a) with fallback (default if local) --prefer-shared Prefer shared library (.so) with fallback (default if system) --static-only Require static library (fail if unavailable) --shared-only Require shared library (fail if unavailable) Build Options: --enable-debug Enable debug symbols (-g -O0) --disable-debug Disable debug (default, -O2) --enable-sanitizers Enable AddressSanitizer and UndefinedBehaviorSanitizer --enable-lto Enable Link Time Optimization --strict Enable strict warnings (-Wpedantic -Wconversion etc.) Installation Paths: --prefix=PREFIX Installation prefix (default: $DEFAULT_PREFIX) --bindir=DIR Binary directory (default: PREFIX/bin) --libdir=DIR Library directory (default: PREFIX/lib) --includedir=DIR Include directory (default: PREFIX/include) --libicmp-prefix=PREFIX libicmp installation prefix (default: same as --prefix) Test Options: --enable-tests Build tests (requires criterion) --disable-tests Don't build tests (default) --enable-sudo-tests Tests require sudo (default) --disable-sudo-tests Tests without sudo (requires capabilities) libicmp Installation: --install-libicmp Install libicmp to system (if built locally) --no-install-libicmp Don't install libicmp (default) Compiler Options: CC=compiler Specify compiler (default: $DEFAULT_CC) CFLAGS="flags" Additional compilation flags CPPFLAGS="flags" Additional preprocessor flags LDFLAGS="flags" Additional linker flags Other Options: --help, -h Show this help message --version, -v Show version --verbose Verbose output during configuration --quiet, -q Quiet mode (minimal output) Examples: ./configure ./configure --with-local-libicmp --static-only ./configure --enable-debug --enable-sanitizers --enable-tests ./configure --prefix=/opt/ft_ping --install-libicmp ./configure CC=gcc --strict EOF exit 0 } show_version() { echo "$PROJECT_NAME configure script version $SCRIPT_VERSION" exit 0 } # ARGUMENT PARSING parse_arguments() { while [ $# -gt 0 ]; do case "$1" in --help|-h) show_help ;; --version|-v) show_version ;; --verbose) VERBOSE="yes" ;; --quiet|-q) QUIET="yes" ;; # libicmp mode --with-system-libicmp) LIBICMP_MODE="system" ;; --with-local-libicmp) LIBICMP_MODE="local" ;; --auto-libicmp) LIBICMP_MODE="auto" ;; # Library type --prefer-static) LIBRARY_TYPE="static" LIBRARY_PREFERENCE="prefer" ;; --prefer-shared) LIBRARY_TYPE="shared" LIBRARY_PREFERENCE="prefer" ;; --static-only) LIBRARY_TYPE="static" LIBRARY_PREFERENCE="only" ;; --shared-only) LIBRARY_TYPE="shared" LIBRARY_PREFERENCE="only" ;; # Build options --enable-debug) ENABLE_DEBUG="yes" ;; --disable-debug) ENABLE_DEBUG="no" ;; --enable-sanitizers) ENABLE_SANITIZERS="yes" ;; --enable-lto) ENABLE_LTO="yes" ;; --strict) ENABLE_STRICT="yes" ;; # Installation paths --prefix=*) PREFIX="${1#*=}" ;; --bindir=*) BINDIR="${1#*=}" ;; --libdir=*) LIBDIR="${1#*=}" ;; --includedir=*) INCLUDEDIR="${1#*=}" ;; --libicmp-prefix=*) LIBICMP_PREFIX="${1#*=}" ;; # Tests --enable-tests) ENABLE_TESTS="yes" ;; --disable-tests) ENABLE_TESTS="no" ;; --enable-sudo-tests) SUDO_TESTS="yes" ;; --disable-sudo-tests) SUDO_TESTS="no" ;; # libicmp installation --install-libicmp) INSTALL_LIBICMP="yes" ;; --no-install-libicmp) INSTALL_LIBICMP="no" ;; # Compiler variables CC=*) CC="${1#*=}" ;; CFLAGS=*) USER_CFLAGS="${1#*=}" ;; CPPFLAGS=*) USER_CPPFLAGS="${1#*=}" ;; LDFLAGS=*) USER_LDFLAGS="${1#*=}" ;; *) fatal "Unknown option: $1\nTry './configure --help' for more information." ;; esac shift done } # VALIDATION validate_configuration() { verbose "Validating configuration..." # Check for conflicting options if [ "$LIBICMP_MODE" = "system" ] && [ "$INSTALL_LIBICMP" = "yes" ]; then warn "Option --install-libicmp ignored when using system libicmp" INSTALL_LIBICMP="no" fi # Set derived paths [ -z "$BINDIR" ] && BINDIR="$PREFIX/bin" [ -z "$LIBDIR" ] && LIBDIR="$PREFIX/lib" [ -z "$INCLUDEDIR" ] && INCLUDEDIR="$PREFIX/include" [ -z "$LIBICMP_PREFIX" ] && LIBICMP_PREFIX="$PREFIX" verbose "PREFIX=$PREFIX" verbose "BINDIR=$BINDIR" verbose "LIBDIR=$LIBDIR" verbose "INCLUDEDIR=$INCLUDEDIR" } # DETECTION check_command() { local cmd="$1" if command -v "$cmd" >/dev/null 2>&1; then return 0 else return 1 fi } check_compiler() { info "Checking for compiler: $CC" if ! check_command "$CC"; then fatal "Compiler '$CC' not found" fi # Get compiler version local cc_version cc_version=$($CC --version 2>&1 | head -n1) success "Found $cc_version" verbose "Compiler: $CC" verbose "Version: $cc_version" } detect_system_libicmp() { local system_libdir="${PREFIX}/lib" local system_includedir="${PREFIX}/include" verbose "Searching for system libicmp in $system_libdir" SYSTEM_LIBICMP_STATIC="${system_libdir}/libicmp.a" SYSTEM_LIBICMP_SHARED="${system_libdir}/libicmp.so" SYSTEM_ICMP_H="${system_includedir}/icmp.h" HAS_STATIC="no" HAS_SHARED="no" HAS_HEADERS="no" if [ -f "$SYSTEM_LIBICMP_STATIC" ]; then HAS_STATIC="yes" verbose "Found static library: $SYSTEM_LIBICMP_STATIC" fi if [ -f "$SYSTEM_LIBICMP_SHARED" ]; then HAS_SHARED="yes" verbose "Found shared library: $SYSTEM_LIBICMP_SHARED" fi if [ -f "$SYSTEM_ICMP_H" ]; then HAS_HEADERS="yes" verbose "Found headers: $SYSTEM_ICMP_H" fi if [ "$HAS_HEADERS" = "yes" ] && { [ "$HAS_STATIC" = "yes" ] || [ "$HAS_SHARED" = "yes" ]; }; then return 0 # System libicmp found else return 1 # System libicmp not found fi } decide_libicmp_source() { info "Determining libicmp source..." case "$LIBICMP_MODE" in system) verbose "Mode: force system libicmp" if detect_system_libicmp; then success "Using system-installed libicmp from $PREFIX/lib" USE_SYSTEM_LIBICMP="yes" BUILD_LOCAL_LIBICMP="no" else fatal "System libicmp not found but --with-system-libicmp specified" fi ;; local) verbose "Mode: force local build" success "Building local libicmp from $LIBICMP_DIR/" USE_SYSTEM_LIBICMP="no" BUILD_LOCAL_LIBICMP="yes" ;; auto) verbose "Mode: auto-detect" if detect_system_libicmp; then success "Using system-installed libicmp from $PREFIX/lib" USE_SYSTEM_LIBICMP="yes" BUILD_LOCAL_LIBICMP="no" else info "System libicmp not found, will build local version" USE_SYSTEM_LIBICMP="no" BUILD_LOCAL_LIBICMP="yes" fi ;; esac } determine_library_type() { info "Determining library type..." if [ "$USE_SYSTEM_LIBICMP" = "yes" ]; then # System library - check what's available case "$LIBRARY_TYPE" in static) if [ "$HAS_STATIC" = "yes" ]; then FINAL_LIBRARY_TYPE="static" LIBICMP_PATH="$SYSTEM_LIBICMP_STATIC" success "Using static system library" elif [ "$LIBRARY_PREFERENCE" = "only" ]; then fatal "Static library required but only shared library found" elif [ "$HAS_SHARED" = "yes" ]; then warn "Static library not found, falling back to shared" FINAL_LIBRARY_TYPE="shared" LIBICMP_PATH="$SYSTEM_LIBICMP_SHARED" else fatal "No suitable library found" fi ;; shared) if [ "$HAS_SHARED" = "yes" ]; then FINAL_LIBRARY_TYPE="shared" LIBICMP_PATH="$SYSTEM_LIBICMP_SHARED" success "Using shared system library" elif [ "$LIBRARY_PREFERENCE" = "only" ]; then fatal "Shared library required but only static library found" elif [ "$HAS_STATIC" = "yes" ]; then warn "Shared library not found, falling back to static" FINAL_LIBRARY_TYPE="static" LIBICMP_PATH="$SYSTEM_LIBICMP_STATIC" else fatal "No suitable library found" fi ;; esac else # Local build - we control what to build FINAL_LIBRARY_TYPE="$LIBRARY_TYPE" if [ "$FINAL_LIBRARY_TYPE" = "static" ]; then LIBICMP_BUILD_STATIC="yes" LIBICMP_BUILD_SHARED="no" LIBICMP_PATH="$LIBICMP_DIR/libicmp.a" success "Will build static library" else LIBICMP_BUILD_STATIC="no" LIBICMP_BUILD_SHARED="yes" LIBICMP_PATH="$LIBICMP_DIR/libicmp.so" success "Will build shared library" fi fi verbose "Final library type: $FINAL_LIBRARY_TYPE" verbose "Library path: $LIBICMP_PATH" } check_test_dependencies() { if [ "$ENABLE_TESTS" = "yes" ]; then info "Checking test dependencies..." # Check for criterion library if ! $CC -lcriterion -o /dev/null -xc - >/dev/null 2>&1 <<< "int main(){return 0;}"; then warn "Criterion library not found, tests may fail to build" warn "Install criterion: https://github.com/Snaipe/Criterion" else success "Found criterion library" fi fi } # BUILD.MK GENERATION generate_build_mk() { info "Generating $BUILD_MK..." local timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Prepare compiler flags local cflags="-Wall -Wextra -Werror -pipe" local cppflags="-std=c99 -I includes" local ldflags="" local debug_flags="" local sanitizer_flags="" local lto_flags="" local strict_flags="" # Debug flags if [ "$ENABLE_DEBUG" = "yes" ]; then debug_flags="-g -O0 -DDEBUG" else debug_flags="-O2 -DNDEBUG" fi # Sanitizer flags if [ "$ENABLE_SANITIZERS" = "yes" ]; then sanitizer_flags="-fsanitize=address,undefined -fno-omit-frame-pointer" fi # LTO flags if [ "$ENABLE_LTO" = "yes" ]; then lto_flags="-flto" fi # Strict flags if [ "$ENABLE_STRICT" = "yes" ]; then strict_flags="-Wpedantic -Wconversion -Wshadow -Wcast-align -Wstrict-prototypes" fi # libicmp-specific configuration if [ "$USE_SYSTEM_LIBICMP" = "yes" ]; then LIBICMP_DEP="" LIBICMP_BUILD_FLAGS="" ldflags="-licmp" else LIBICMP_DEP="$LIBICMP_PATH" LIBICMP_BUILD_FLAGS="BUILD_STATIC=$LIBICMP_BUILD_STATIC BUILD_SHARED=$LIBICMP_BUILD_SHARED" cppflags="$cppflags -I $LIBICMP_DIR/includes" ldflags="-L$LIBICMP_DIR -licmp" if [ "$FINAL_LIBRARY_TYPE" = "shared" ] && [ "$INSTALL_LIBICMP" = "yes" ]; then ldflags="$ldflags -Wl,-rpath,$LIBDIR" fi fi # User-provided flags if [ -n "$USER_CFLAGS" ]; then cflags="$cflags $USER_CFLAGS" fi if [ -n "$USER_CPPFLAGS" ]; then cppflags="$cppflags $USER_CPPFLAGS" fi if [ -n "$USER_LDFLAGS" ]; then ldflags="$ldflags $USER_LDFLAGS" fi # Write build.mk cat > "$BUILD_MK" << EOF # $BUILD_MK - Generated by configure on $timestamp # DO NOT EDIT - This file is automatically generated # libicmp Configuration USE_SYSTEM_LIBICMP = $USE_SYSTEM_LIBICMP BUILD_LOCAL_LIBICMP = $BUILD_LOCAL_LIBICMP LIBICMP_TYPE = $FINAL_LIBRARY_TYPE LIBICMP_DIR = $LIBICMP_DIR LIBICMP_PATH = $LIBICMP_PATH LIBICMP_DEP = $LIBICMP_DEP EOF if [ "$BUILD_LOCAL_LIBICMP" = "yes" ]; then cat >> "$BUILD_MK" << EOF # libicmp build configuration LIBICMP_BUILD_STATIC = $LIBICMP_BUILD_STATIC LIBICMP_BUILD_SHARED = $LIBICMP_BUILD_SHARED LIBICMP_BUILD_FLAGS = $LIBICMP_BUILD_FLAGS LIBICMP_MAKEFILE = $LIBICMP_MAKEFILE EOF fi cat >> "$BUILD_MK" << EOF # Compiler Configuration CC = $CC CPPFLAGS = $cppflags CFLAGS = $cflags LDFLAGS = $ldflags # Individual flag components (for reference) DEBUG_FLAGS = $debug_flags SANITIZER_FLAGS = $sanitizer_flags LTO_FLAGS = $lto_flags STRICT_FLAGS = $strict_flags # Combined flags CFLAGS += \$(DEBUG_FLAGS) \$(SANITIZER_FLAGS) \$(LTO_FLAGS) \$(STRICT_FLAGS) LDFLAGS += \$(SANITIZER_FLAGS) \$(LTO_FLAGS) # Installation Paths PREFIX = $PREFIX BINDIR = $BINDIR LIBDIR = $LIBDIR INCLUDEDIR = $INCLUDEDIR # Test Configuration ENABLE_TESTS = $ENABLE_TESTS SUDO_TESTS = $SUDO_TESTS TEST_LDFLAGS = -lcriterion # Build Options ENABLE_DEBUG = $ENABLE_DEBUG ENABLE_SANITIZERS = $ENABLE_SANITIZERS ENABLE_LTO = $ENABLE_LTO ENABLE_STRICT = $ENABLE_STRICT # libicmp Installation (if built locally) INSTALL_LIBICMP = $INSTALL_LIBICMP LIBICMP_PREFIX = $LIBICMP_PREFIX EOF success "Generated $BUILD_MK" } # SUMMARY show_summary() { if [ "$QUIET" = "yes" ]; then return fi cat << EOF ------------------------------------------------------------------------ Configuration Summary for $PROJECT_NAME ------------------------------------------------------------------------ libicmp Source: $([ "$USE_SYSTEM_LIBICMP" = "yes" ] && echo "system ($PREFIX/lib)" || echo "local ($LIBICMP_DIR/)") Library Type: $FINAL_LIBRARY_TYPE Compiler: $CC Installation Prefix: $PREFIX Build Options: Debug: $ENABLE_DEBUG Sanitizers: $ENABLE_SANITIZERS LTO: $ENABLE_LTO Strict: $ENABLE_STRICT Tests: $ENABLE_TESTS$([ "$ENABLE_TESTS" = "yes" ] && echo " ($([ "$SUDO_TESTS" = "yes" ] && echo "with sudo" || echo "without sudo"))" || echo "") EOF if [ "$BUILD_LOCAL_LIBICMP" = "yes" ]; then cat << EOF libicmp Installation: $([ "$INSTALL_LIBICMP" = "yes" ] && echo "yes (to $LIBICMP_PREFIX)" || echo "no (local use only)") EOF fi cat << EOF Compiler Flags: CPPFLAGS: $(echo $CPPFLAGS | sed 's/^-std=c99 -I includes //') CFLAGS: -Wall -Wextra -Werror -pipe $debug_flags $sanitizer_flags $lto_flags $strict_flags LDFLAGS: $LDFLAGS ------------------------------------------------------------------------ Configuration saved to $BUILD_MK EOF } # MAIN main() { parse_arguments "$@" validate_configuration check_compiler decide_libicmp_source determine_library_type check_test_dependencies generate_build_mk show_summary exit 0 } main "$@"