Ajout de la méthode getRule dans BakeEngine et amélioration de la gestion des dépendances dans la classe Rule

This commit is contained in:
Moncef STITI 2025-02-08 23:15:07 +01:00
parent 2e51ca7491
commit 79f285d020
3 changed files with 84 additions and 95 deletions

@ -17,9 +17,12 @@ public class BakeEngine {
} }
public static boolean hasRule(String target) { public static boolean hasRule(String target) {
// Vérifie si une règle existe pour créer cette cible
return ruleMap.containsKey(target); return ruleMap.containsKey(target);
} }
public static Rule getRule(String target) {
return ruleMap.get(target);
}
public void run() { public void run() {
List<Rule> rules = parser.parse(); List<Rule> rules = parser.parse();
@ -35,4 +38,4 @@ public class BakeEngine {
executor.execute(rule); executor.execute(rule);
} }
} }
} }

@ -32,6 +32,12 @@ public class BakefileParser {
*/ */
private static final Pattern VARIABLE_PATTERN = Pattern.compile("^(\\w+)\\s*=\\s*([^#]*?)\\s*(?:#.*)?$"); private static final Pattern VARIABLE_PATTERN = Pattern.compile("^(\\w+)\\s*=\\s*([^#]*?)\\s*(?:#.*)?$");
/**
* Regex pour détecter les déclarations .PHONY
* Format : ".PHONY: clean all"
*/
private static final Pattern PHONY_PATTERN = Pattern.compile("^\\.PHONY:\\s*([^#]*?)\\s*(?:#.*)?$");
/** /**
* Première cible trouvée dans le fichier Bakefile. * Première cible trouvée dans le fichier Bakefile.
*/ */
@ -42,13 +48,12 @@ public class BakefileParser {
*/ */
private Map<String, String> variables = new HashMap<>(); private Map<String, String> variables = new HashMap<>();
public BakefileParser(String filename) { public BakefileParser(String filename) {
this.filename = filename; this.filename = filename;
firstTarget = null; firstTarget = null;
} }
private List<String> splitDependencies(String depStr) { private List<String> splitDependencies(String depStr) {
if (depStr == null || depStr.trim().isEmpty()) { if (depStr == null || depStr.trim().isEmpty()) {
return new ArrayList<>(); return new ArrayList<>();
} }
@ -63,7 +68,6 @@ public class BakefileParser {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public List<Rule> parse() { public List<Rule> parse() {
List<Rule> rules = new ArrayList<>(); List<Rule> rules = new ArrayList<>();
Set<String> phonyTargets = new HashSet<>(); Set<String> phonyTargets = new HashSet<>();
@ -86,6 +90,7 @@ public class BakefileParser {
Matcher varMatcher = VARIABLE_PATTERN.matcher(line); Matcher varMatcher = VARIABLE_PATTERN.matcher(line);
Matcher targetMatcher = TARGET_PATTERN.matcher(line); Matcher targetMatcher = TARGET_PATTERN.matcher(line);
Matcher commandMatcher = COMMAND_PATTERN.matcher(line); Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
Matcher phonyMatcher = PHONY_PATTERN.matcher(line);
if (line.trim().isEmpty()) { if (line.trim().isEmpty()) {
continue; continue;
@ -96,32 +101,35 @@ public class BakefileParser {
System.exit(1); System.exit(1);
} }
if (phonyMatcher.matches()) {
// Ajouter les cibles .PHONY à l'ensemble des cibles phony
String[] phonies = phonyMatcher.group(1).trim().split("\\s+");
Collections.addAll(phonyTargets, phonies);
continue;
}
if (varMatcher.matches()) { if (varMatcher.matches()) {
// Stocke la variable en enlevant les espaces en début et fin
variables.put(varMatcher.group(1), varMatcher.group(2).trim()); variables.put(varMatcher.group(1), varMatcher.group(2).trim());
} else if (targetMatcher.matches()) { } else if (targetMatcher.matches()) {
if (currentTarget != null) { if (currentTarget != null) {
String resolvedTarget = replaceVariables(currentTarget.trim());
rules.add(new Rule( rules.add(new Rule(
replaceVariables(currentTarget.trim()), resolvedTarget,
splitDependencies(dependencies.stream() splitDependencies(dependencies.stream()
.collect(Collectors.joining(" "))), .collect(Collectors.joining(" "))),
replaceVariablesInList(commands), replaceVariablesInList(commands),
phonyTargets.contains(currentTarget.trim()) phonyTargets.contains(resolvedTarget)
)); ));
// Ne définir firstTarget que si ce n'est pas une cible .PHONY
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
firstTarget = resolvedTarget;
}
} }
currentTarget = targetMatcher.group(1); currentTarget = targetMatcher.group(1);
String depStr = targetMatcher.group(2); String depStr = targetMatcher.group(2);
dependencies = splitDependencies(depStr); dependencies = splitDependencies(depStr);
if (firstTarget == null) {
firstTarget = replaceVariables(currentTarget.trim());
}
if (currentTarget.equals("clean")) {
phonyTargets.add(currentTarget);
}
commands = new ArrayList<>(); commands = new ArrayList<>();
} else if (commandMatcher.matches()) { } else if (commandMatcher.matches()) {
commands.add(commandMatcher.group(1)); commands.add(commandMatcher.group(1));
@ -129,12 +137,18 @@ public class BakefileParser {
} }
if (currentTarget != null) { if (currentTarget != null) {
String resolvedTarget = replaceVariables(currentTarget.trim());
rules.add(new Rule( rules.add(new Rule(
replaceVariables(currentTarget.trim()), resolvedTarget,
replaceVariablesInList(dependencies), replaceVariablesInList(dependencies),
replaceVariablesInList(commands), replaceVariablesInList(commands),
phonyTargets.contains(currentTarget.trim()) phonyTargets.contains(resolvedTarget)
)); ));
// Vérifier une dernière fois pour firstTarget
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
firstTarget = resolvedTarget;
}
} }
} catch (IOException e) { } catch (IOException e) {
@ -143,7 +157,7 @@ public class BakefileParser {
return rules; return rules;
} }
private String replaceVariables(String input) { private String replaceVariables(String input) {
if (input == null) return null; if (input == null) return null;
String result = input; String result = input;
boolean replaced; boolean replaced;
@ -161,7 +175,7 @@ public class BakefileParser {
return result.trim(); return result.trim();
} }
private List<String> replaceVariablesInList(List<String> items) { private List<String> replaceVariablesInList(List<String> items) {
List<String> resolved = new ArrayList<>(); List<String> resolved = new ArrayList<>();
for (String item : items) { for (String item : items) {
resolved.add(replaceVariables(item)); resolved.add(replaceVariables(item));
@ -169,10 +183,11 @@ public class BakefileParser {
return resolved; return resolved;
} }
/** /**
* Permet de récupérer la première cible trouvée dans le fichier Bakefile. * Permet de récupérer la première cible trouvée dans le fichier Bakefile.
* @return La première cible trouvée * @return La première cible trouvée
*/ */
public static String getFirstTarget() { return firstTarget; } public static String getFirstTarget() {
} return firstTarget;
}
}

@ -3,42 +3,12 @@ package fr.monlouyan.bakefile;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
/**
* Représente une règle dans un fichier Bakefile
* Dernière modification : 04/02/2025
*
* @author Moncef STITI, Yanis HAMOUDI
* @version 1.0
* @date 04/02/2025
*/
public class Rule { public class Rule {
/**
* Nom de la règle
*/
private String name; private String name;
/**
* Liste des dépendances de la règle
*/
private List<String> dependencies; private List<String> dependencies;
/**
* Liste des commandes de la règle (actions à exécuter)
*/
private List<String> commands; private List<String> commands;
/**
* Indique si la règle est une règle phony
*/
private boolean isPhony; private boolean isPhony;
/**
* Constructeur de la classe Rule
* @param name Nom de la règle
* @param dependencies Liste des dépendances de la règle
* @param commands Liste des commandes de la règle à exécuter
* @param isPhony Si la règle est une règle phony ou non
*/
public Rule(String name, List<String> dependencies, List<String> commands, boolean isPhony) { public Rule(String name, List<String> dependencies, List<String> commands, boolean isPhony) {
this.name = name; this.name = name;
this.dependencies = dependencies; this.dependencies = dependencies;
@ -46,60 +16,50 @@ public class Rule {
this.isPhony = isPhony; this.isPhony = isPhony;
} }
/**
* Permet de récupérer le nom de la règle
* @return Le nom de la règle
*/
public String getName() { return name; } public String getName() { return name; }
/**
* Permet de récupérer les dépendances de la règle
* @return La liste des dépendances de la règle
*/
public List<String> getDependencies() { return dependencies; } public List<String> getDependencies() { return dependencies; }
/**
* Permet de récupérer les commandes de la règle
* @return La liste des commandes de la règle
*/
public List<String> getCommands() { return commands; } public List<String> getCommands() { return commands; }
/**
* Permet de savoir si la règle est une règle phony
* @return true si la règle est une règle phony, false sinon
*/
public boolean isPhony() { return isPhony; } public boolean isPhony() { return isPhony; }
/**
* Permet de savoir 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(); } public boolean isEmpty() { return dependencies.isEmpty() && commands.isEmpty(); }
/**
* Détermine si la règle doit être mise à jour.
* Une règle doit être mise à jour si l'un de ses fichiers de sortie est plus ancien qu'un de ses fichiers de dépendance.
* De plus, les règles phony sont toujours mises à jour.
*
* @return true si la règle doit être mise à jour, false sinon.
*/
public boolean needsUpdate() { public boolean needsUpdate() {
if (BakeCLI.isDebug()){ if (BakeCLI.isDebug()){
System.out.println("Debug : Checking if rule " + name + " needs update"); System.out.println("Debug : Checking if rule " + name + " needs update");
} }
// Les règles phony sont toujours mises à jour
if (isPhony) { if (isPhony) {
if (BakeCLI.isDebug()) { if (BakeCLI.isDebug()) {
System.out.println("Debug : Rule " + name + " is phony, always needs update"); System.out.println("Debug : Rule " + name + " is phony, always needs update");
} }
return true; // Les règles phony sont toujours mises à jour return true;
} }
File targetFile = new File(name);
// 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) {
File depFile = new File(dependency);
if (!depFile.exists() && !dependency.isEmpty() && !BakeEngine.hasRule(dependency)) {
System.out.println("bake: *** No rule to make target '" + dependency + "', needed by '" + name + "'. Stop.");
System.exit(1);
}
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
File targetFile = new File(name);
if (BakeCLI.isDebug()){ if (BakeCLI.isDebug()){
System.out.println("Debug : Checking if target file " + name + " exist and is up to date"); System.out.println("Debug : Checking if target file " + name + " exist and is up to date");
} }
long targetTimestamp = targetFile.exists() ? TimestampManager.getTimestamp(targetFile) : 0; long targetTimestamp = targetFile.exists() ? TimestampManager.getTimestamp(targetFile) : 0;
if (BakeCLI.isDebug()) { if (BakeCLI.isDebug()) {
@ -114,6 +74,7 @@ public class Rule {
System.out.println("Debug : Dependency '" + dependency + "' last modified at " + TimestampManager.formatTimestamp(depTimestamp)); System.out.println("Debug : Dependency '" + dependency + "' last modified at " + TimestampManager.formatTimestamp(depTimestamp));
} }
// Vérifier si une règle existe pour cette dépendance
boolean hasRule = BakeEngine.hasRule(dependency); boolean hasRule = BakeEngine.hasRule(dependency);
if (!depFile.exists() && !dependency.isEmpty() && !hasRule) { if (!depFile.exists() && !dependency.isEmpty() && !hasRule) {
@ -121,6 +82,16 @@ public class Rule {
System.exit(1); System.exit(1);
} }
// 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 (depFile.exists() && TimestampManager.getTimestamp(depFile) > targetTimestamp) { if (depFile.exists() && TimestampManager.getTimestamp(depFile) > targetTimestamp) {
if (BakeCLI.isDebug()) { if (BakeCLI.isDebug()) {
System.out.println("Debug : Dependency " + dependency + " is newer than target file " + name + ", needs update"); System.out.println("Debug : Dependency " + dependency + " is newer than target file " + name + ", needs update");
@ -130,4 +101,4 @@ public class Rule {
} }
return false; return false;
} }
} }