203 lines
7.2 KiB
Java
203 lines
7.2 KiB
Java
package fr.monlouyan.bakefile;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
|
|
List<String> executableCommands = rule.getCommands();
|
|
List<List<String>> displayCommands = rule.getDisplayCommands();
|
|
|
|
for (int i = 0; i < executableCommands.size(); i++) {
|
|
String command = executableCommands.get(i);
|
|
List<String> displayLines = (i < displayCommands.size()) ? displayCommands.get(i) : null;
|
|
|
|
// Vérifier si la commande commence par @ (ne pas afficher la commande)
|
|
boolean silent = command.startsWith("@");
|
|
// Enlever le @ si présent pour exécuter la commande correctement
|
|
String actualCommand = silent ? command.substring(1) : command;
|
|
|
|
boolean ignoreErrors = actualCommand.startsWith("-");
|
|
|
|
if(ignoreErrors){
|
|
actualCommand = actualCommand.substring(1).trim();
|
|
}
|
|
|
|
if (isCircular){
|
|
if (!silent && displayLines != null && !displayLines.isEmpty()) {
|
|
boolean isFirstLine = true;
|
|
for (String line : displayLines) {
|
|
if (isFirstLine) {
|
|
// Pour la première ligne, supprimer l'indentation
|
|
if (line.startsWith("\t")) {
|
|
System.out.println(line.substring(1));
|
|
} else {
|
|
System.out.println(line);
|
|
}
|
|
isFirstLine = false;
|
|
} else {
|
|
// Pour les lignes suivantes, conserver l'indentation
|
|
System.out.println(line);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// On n'exécute que si nécessaire
|
|
if (ruleNeedsUpdate) {
|
|
try {
|
|
if(!isCircular && !silent){
|
|
// Afficher les lignes formatées avec traitement spécial pour les continuations
|
|
if (displayLines != null && !displayLines.isEmpty()) {
|
|
boolean isFirstLine = true;
|
|
|
|
for (String line : displayLines) {
|
|
if (isFirstLine) {
|
|
// Pour la première ligne, toujours supprimer l'indentation
|
|
if (line.startsWith("\t")) {
|
|
System.out.println(line.substring(1));
|
|
} else {
|
|
System.out.println(line);
|
|
}
|
|
isFirstLine = false;
|
|
} else {
|
|
// Pour les lignes suivantes d'une continuation, conserver l'indentation
|
|
System.out.println(line);
|
|
}
|
|
}
|
|
} else {
|
|
// Cas d'une commande simple (une seule ligne)
|
|
if (command.startsWith("\t")) {
|
|
System.out.println(command.substring(1));
|
|
} else {
|
|
System.out.println(command);
|
|
}
|
|
}
|
|
}
|
|
if (debug) System.out.println("Debug: Executing " + actualCommand);
|
|
ProcessBuilder pb = new ProcessBuilder("sh", "-c", actualCommand);
|
|
pb.inheritIO();
|
|
Process process = pb.start();
|
|
|
|
// Attendre la fin du processus
|
|
int exitCode = process.waitFor();
|
|
|
|
if (exitCode != 0) {
|
|
if (ignoreErrors) {
|
|
System.err.println("bake: [" + rule.getName() + "] Error " + exitCode + " (ignored)");
|
|
} else {
|
|
System.err.println("bake: *** [" + rule.getName() + "] Error " + exitCode);
|
|
System.exit(2);
|
|
}
|
|
} else if (exitCode != 0 && ignoreErrors && debug) {
|
|
System.out.println("Debug: Command failed with exit code " + exitCode + ", but errors are ignored");
|
|
}
|
|
|
|
} catch (IOException | InterruptedException e) {
|
|
if(!ignoreErrors){
|
|
e.printStackTrace();
|
|
System.exit(2);
|
|
} else if(debug){
|
|
System.out.println("Debug: Command execution failed, but errors are ignored");
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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() + "'.");
|
|
}
|
|
}
|
|
} |