Concevoir un script de configuration personnalisé pour des projets C sous Linux

Dans le développement de logiciels système en C, il est courant de devoir automatiser la détection de l'environnement et la génération de fichiers de construction (Makefiles). Bien que des outils comme Autotools ou CMake soient standards, comprendre comment écrire un script configure personnalisé en Shell permet un contrôle total sur le processus de compilation.

1. Gestion des arguments d'entrée

Tout script de configuration doit être capable de traiter les options passées par l'utilisateur (comme --prefix). En Bash, nous utilisons deux variables spéciales :

  • $# : Le nombre total d'arguments fournis.
  • $@ : La liste complète des arguments.
#!/bin/bash
# Exemple simple de lecture des arguments
echo "Nombre de paramètres : $#"
echo "Liste des paramètres : $@"

2. Analyse des options de configuration

L'étape suivante consiste à itérer sur ces paramètres pour définir les variables de compilation, telles que le chemin d'installation ou le choix du compilateur.

#!/bin/bash

# Détection initiale du système
os_name=$(uname -s)
default_cc="gcc"
default_ar="ar"
install_dir="/usr/local/custom_project"

# Traitement des arguments
for arg in "$@"; do
    case $arg in
        --help)
            echo "Usage: ./configure [OPTIONS]"
            echo "--prefix=PATH    Chemin d'installation"
            echo "--cc=COMPILER    Compilateur C à utiliser"
            exit 0
            ;;
        --prefix=*)
            install_dir="${arg#*=}"
            ;;
        --cc=*)
            default_cc="${arg#*=}"
            ;;
        --ar=*)
            default_ar="${arg#*=}"
            ;;
    esac
done

echo "Configuration : OS=$os_name, CC=$default_cc, Prefix=$install_dir"

3. Résolution dynamique des chemins

Il est souvent nécessaire de convertir des chemins relatifs en chemins absolus. Une méthode robuste consiste à compiler un petit utilitaire C temporaire pour résoudre les chemins réels via la bibliothèque standard.

# Génération d'un utilitaire de résolution de chemin
cat << __EOF__ > .tmp_resolver.c
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
    if (argc < 2) return 1;
    char absolute[PATH_MAX];
    if (realpath(argv[1], absolute)) {
        printf("%s", absolute);
    } else {
        printf("%s", argv[1]);
    }
    return 0;
}
__EOF__

$default_cc -o path_resolver .tmp_resolver.c
abs_install_path=$(./path_resolver "$install_dir")
rm -f path_resolver .tmp_resolver.c

echo "Chemin absolu d'installation : $abs_install_path"

4. Génération automatique de fichiers d'en-tête C

Pour que le code source C connaisse les chemins définis lors de la configuration, le script doit générer des fichiers .h et .c.

mkdir -p include src

# Création du fichier d'en-tête
cat << __EOF__ > include/app_config.h
#ifndef APP_CONFIG_H
#define APP_CONFIG_H

typedef enum {
    CFG_PATH_ROOT,
    CFG_PATH_LOG,
    CFG_PATH_CONF
} path_type_t;

const char *get_system_path(path_type_t type);

#endif
__EOF__

# Création du fichier source avec les chemins injectés
cat << __EOF__ > src/app_config.c
#include "app_config.h"
#include <stddef.h>

static const char *root_path = "$abs_install_path";
static const char *log_path  = "$abs_install_path/logs";

const char *get_system_path(path_type_t type) {
    switch(type) {
        case CFG_PATH_ROOT: return root_path;
        case CFG_PATH_LOG:  return log_path;
        default: return NULL;
    }
}
__EOF__

5. Création dynamique du Makefile

Le script doit enfin générer un Makefile adapté à l'architecture cible et aux fonctionnalités détectées (comme la présence d'epoll ou de kqueue).

echo "Génération du Makefile..."

cat << __EOF__ > Makefile
CC      = $default_cc
AR      = $default_ar
CFLAGS  = -Iinclude -Wall -O2 -fPIC
LDFLAGS = -lpthread

SRCS    = \$(wildcard src/*.c)
OBJS    = \$(SRCS:.c=.o)
TARGET  = libproject.so

all: \$(TARGET)

\$(TARGET): \$(OBJS)
	\$(CC) -shared -o \$@ \$^ \$(LDFLAGS)

%.o: %.c
	\$(CC) \$(CFLAGS) -c -o \$@ \$<

clean:
	rm -f src/*.o \$(TARGET)

install:
	mkdir -p $abs_install_path/lib
	cp \$(TARGET) $abs_install_path/lib/
__EOF__

# Détection de fonctionnalités spécifiques (ex: epoll)
cat << __EOF__ > .test_epoll.c
#include <sys/epoll.h>
int main() { epoll_create1(0); return 0; }
__EOF__

if $default_cc .test_epoll.c -o .test_epoll 2>/dev/null; then
    echo "Support epoll détecté : Oui"
    echo "CFLAGS += -DHAVE_EPOLL" >> Makefile
else
    echo "Support epoll détecté : Non"
fi
rm -f .test_epoll* .test_epoll.c

6. Finalisation de la configuration

La dernière étape consiste souvent à transformer des ficheirs de templates (comme des fichiers de configuration .conf) en remplaçant des variables par les valeurs réelles calculées.

if [ -f conf/server.conf.in ]; then
    sed "s#{{INSTALL_PATH}}#$abs_install_path#g" conf/server.conf.in > conf/server.conf
fi

echo "Configuration terminée avec succès."

Étiquettes: shell-scripting C Makefile linux-development build-systems

Publié le 15 juillet à 04h37