package fr.monlouyan.bakefile; import java.io.File; import java.io.IOException; /** * Exécuteur des commandes définies dans les règles. * Cette classe est responsable de l'exécution des commandes définies dans les règles * du fichier Bakefile. * * @author Moncef STITI, Yanis HAMOUDI, Louay DARDOURI * @version 1.0 */ public class CommandExecutor { /** * true si le mode debug est activé, false sinon */ private boolean debug; /** * Pour tracker si quelque chose doit être mis à jour */ private boolean needsUpdate = false; /** * Pour tracker si un cycle a été détecté */ private boolean isCircular = false; /** * Pour détecter les timestamps dans le futur */ private boolean futureTimestampDetected = false; /** * Constructeur de la classe CommandExecutor. * @param debug true si le mode debug est activé, false sinon * @param isCircular true si on est en mode circulaire, false sinon */ public CommandExecutor(boolean debug, boolean isCircular) { this.debug = debug; this.isCircular = isCircular; } /** * Exécute les commandes d'une règle. * @param rule La règle à exécuter */ public void execute(Rule rule) { // On vérifie d'abord si cette règle a besoin d'être mise à jour boolean ruleNeedsUpdate = rule.needsUpdate(); if (ruleNeedsUpdate) { needsUpdate = true; // Au moins une règle doit être mise à jour // Vérifier les timestamps des dépendances pour détecter ceux dans le futur for (String dependency : rule.getDependencies()) { if (dependency.startsWith("~")) { continue; } File depFile = new File(dependency); if (depFile.exists() && TimestampManager.getTimestamp(depFile) > System.currentTimeMillis()) { futureTimestampDetected = true; } } } // Si la règle a besoin d'être mise à jour et qu'il n'y a pas de commandes if (ruleNeedsUpdate && rule.getCommands().isEmpty()) { return; // On ne fait rien mais on ne montre pas de message } for (String command : rule.getCommands()) { if (isCircular){ System.out.println(command); } // On n'exécute que si nécessaire if (ruleNeedsUpdate) { try { if(!isCircular){ System.out.println(command); } if (debug) System.out.println("Debug: Executing " + command); ProcessBuilder pb = new ProcessBuilder("sh", "-c", command); pb.inheritIO(); Process process = pb.start(); // Attendre la fin du processus int exitCode = process.waitFor(); if (exitCode != 0) { System.err.println("bake: *** [" + rule.getName() + "] Error " + exitCode); System.exit(2); } } catch (IOException | InterruptedException e) { e.printStackTrace(); System.exit(2); } } } // Si on a détecté des timestamps dans le futur, afficher un avertissement à la fin (comme make) if (futureTimestampDetected && !isCircular) { System.out.println("bake: warning: Clock skew detected. Your build may be incomplete."); } // Vérifier si cette règle est une cible directement demandée par l'utilisateur boolean isRequestedTarget = BakeCLI.getTargets().contains(rule.getName()) || (BakeCLI.getTargets().isEmpty() && rule.getName().equals(BakefileParser.getFirstTarget())); if (!isRequestedTarget || needsUpdate) { // Si ce n'est pas une cible demandée ou si une mise à jour a été nécessaire, // ne pas afficher de message return; } // Vérifier si la cible existe en tant que fichier File targetFile = new File(rule.getName()); boolean fileExists = targetFile.exists(); if (!rule.getCommands().isEmpty() && fileExists) { // Si la cible a des commandes et existe en tant que fichier, // afficher "up to date" System.out.println("bake: `" + rule.getName() + "' is up to date."); } else { // Si la cible n'a pas de commandes ou n'existe pas en tant que fichier, // afficher "Nothing to be done" System.out.println("bake: Nothing to be done for `" + rule.getName() + "'."); } } }