package fr.monlouyan.bakefile; import java.io.File; import java.util.List; /** * Représente une règle du fichier Bakefile. * Chaque règle est composée d'un nom, d'une liste de dépendances et d'une liste de commandes. * Une règle peut être phony, c'est-à-dire qu'elle n'a pas de fichier cible associé. * * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI * @version 1.0 */ public class Rule { /*** Nom de la règle */ private String name; /*** Liste des dépendances de la règle */ private List dependencies; /*** Liste des commandes */ private List commands; /*** true si la règle est phony, false sinon */ private boolean isPhony; /** * Constructeur de la classe Rule. * @param name Nom de la règle * @param dependencies Liste des dépendances * @param commands Liste des commandes * @param isPhony true si la règle est phony, false sinon */ public Rule(String name, List dependencies, List commands, boolean isPhony) { this.name = name; this.dependencies = dependencies; this.commands = commands; this.isPhony = isPhony; } /** * Récupère le nom de la règle. * @return Le nom de la règle */ public String getName() { return name; } /** * Récupère la liste des dépendances de la règle. * @return La liste des dépendances */ public List getDependencies() { return dependencies; } /** * Récupère la liste des commandes de la règle. * @return La liste des commandes */ public List getCommands() { return commands; } /** * Vérifie si la règle est phony. * @return true si la règle est phony, false sinon. */ public boolean isPhony() { return isPhony; } /** * Vérifie si la règle est vide (sans dépendances ni commandes). * @return true si la règle est vide, false sinon */ public boolean isEmpty() { return dependencies.isEmpty() && commands.isEmpty(); } /** * Vérifie si la règle doit être mise à jour. * @return true si la règle doit être mise à jour, false sinon */ public boolean needsUpdate() { if (BakeCLI.isDebug()){ System.out.println("Debug : Checking if rule " + name + " needs update"); } // Les règles phony sont toujours mises à jour if (isPhony) { if (BakeCLI.isDebug()) { System.out.println("Debug : Rule " + name + " is phony, always needs update"); } return true; } // Skip targets with tilde in path (home directory) to match make behavior if (name.startsWith("~")) { if (BakeCLI.isDebug()) { System.out.println("Debug : Skipping home directory path: " + name); } return false; } // Vérifier d'abord toutes les dépendances avant d'exécuter quoi que ce soit for (String dependency : dependencies) { // Skip dependencies with tilde in path if (dependency.startsWith("~")) { continue; } File depFile = new File(dependency); boolean hasRule = BakeEngine.hasRule(dependency); if (!depFile.exists() && !dependency.isEmpty() && !hasRule) { System.out.println("bake: *** No rule to make target `" + dependency + "', needed by `" + name + "'. Stop."); System.exit(2); } } // Si le fichier cible n'existe pas et qu'il y a des commandes, il doit être mis à jour File targetFile = new File(name); if (!targetFile.exists() && !commands.isEmpty()) { if (BakeCLI.isDebug()) { System.out.println("Debug : Target file " + name + " does not exist and has commands, needs update"); } return true; } // Si la règle n'a pas de commandes, on vérifie seulement si une dépendance doit être mise à jour if (commands.isEmpty()) { for (String dependency : dependencies) { // Skip dependencies with tilde in path if (dependency.startsWith("~")) { continue; } Rule depRule = BakeEngine.getRule(dependency); if (depRule != null && depRule.needsUpdate()) { if (BakeCLI.isDebug()) { System.out.println("Debug : Dependency rule " + dependency + " needs update"); } return true; } } return false; } // Pour les règles avec des commandes, on vérifie aussi les timestamps if (BakeCLI.isDebug()){ System.out.println("Debug : Checking if target file " + name + " exist and is up to date"); } long targetTimestamp = targetFile.exists() ? TimestampManager.getTimestamp(targetFile) : 0; if (BakeCLI.isDebug()) { System.out.println("Debug : Target file '" + name + "' last modified at " + TimestampManager.formatTimestamp(targetTimestamp)); } long currentTime = System.currentTimeMillis(); for (String dependency : dependencies) { // Skip dependencies with tilde in path if (dependency.startsWith("~")) { continue; } File depFile = new File(dependency); if (!depFile.exists()) { continue; } long depTimestamp = TimestampManager.getTimestamp(depFile); // Vérifier si le timestamp de la dépendance est dans le futur if (depTimestamp > currentTime) { // Avertissement similaire à make System.out.println("bake: Warning: File '" + dependency + "' has modification time " + ((depTimestamp - currentTime) / 1000) + " s in the future"); if (BakeCLI.isDebug()) { System.out.println("Debug : Dependency " + dependency + " has a timestamp in the future, needs update"); } return true; } if (BakeCLI.isDebug()) { System.out.println("Debug : Dependency '" + dependency + "' last modified at " + TimestampManager.formatTimestamp(depTimestamp)); } // Si la dépendance est une règle et qu'elle a besoin d'être mise à jour Rule depRule = BakeEngine.getRule(dependency); if (depRule != null && depRule.needsUpdate()) { if (BakeCLI.isDebug()) { System.out.println("Debug : Dependency rule " + dependency + " needs update"); } return true; } // Vérifier les timestamps seulement si le fichier existe if (depTimestamp > targetTimestamp) { if (BakeCLI.isDebug()) { System.out.println("Debug : Dependency " + dependency + " is newer than target file " + name + ", needs update"); } return true; } } return false; } }