Améliorations des dépendances circulaire

This commit is contained in:
2025-02-04 21:25:16 +01:00
parent c20a508069
commit 1f31e318dc
35 changed files with 156 additions and 58 deletions

Binary file not shown.

View File

@@ -10,12 +10,12 @@ public class BakeEngine {
public BakeEngine() { public BakeEngine() {
this.parser = new BakefileParser("Bakefile"); this.parser = new BakefileParser("Bakefile");
this.resolver = new DependencyResolver(BakeCLI.isDebug()); this.resolver = new DependencyResolver(BakeCLI.isDebug());
this.executor = new CommandExecutor(BakeCLI.isDebug());
} }
public void run() { public void run() {
List<Rule> rules = parser.parse(); List<Rule> rules = parser.parse();
List<Rule> rulesToBuild = resolver.resolve(rules, BakeCLI.getTargets()); List<Rule> rulesToBuild = resolver.resolve(rules, BakeCLI.getTargets());
this.executor = new CommandExecutor(BakeCLI.isDebug(), resolver.isCircular());
for (Rule rule : rulesToBuild) { for (Rule rule : rulesToBuild) {
executor.execute(rule); executor.execute(rule);

View File

@@ -4,9 +4,12 @@ import java.io.IOException;
public class CommandExecutor { public class CommandExecutor {
private boolean debug; private boolean debug;
private boolean needsUpdate = false; // Pour tracker si quelque chose doit être mis à jour
public CommandExecutor(boolean debug) { private boolean isCircular = false; // Pour tracker si un cycle a été détecté
public CommandExecutor(boolean debug, boolean isCircular) {
this.debug = debug; this.debug = debug;
this.isCircular = isCircular;
} }
public void execute(Rule rule) { public void execute(Rule rule) {
@@ -15,28 +18,43 @@ public class CommandExecutor {
return; return;
} }
if (!rule.needsUpdate()) { // On vérifie d'abord si cette règle a besoin d'être mise à jour
if (rule.getName().equals(BakefileParser.getFirstTarget())) { boolean ruleNeedsUpdate = rule.needsUpdate();
System.out.println("bake: '" + rule.getName() + "' is up to date."); if (ruleNeedsUpdate) {
} needsUpdate = true; // Au moins une règle doit être mise à jour
return;
} }
for (String command : rule.getCommands()) {
try { if (isCircular){
for (String command : rule.getCommands()) { System.out.println(command);
System.out.println(command); // Affichage de la commande executée }
ProcessBuilder pb = new ProcessBuilder("sh", "-c", command);
Process process = pb.start(); // Mais on n'exécute que si nécessaire
int exitCode = process.waitFor(); if (ruleNeedsUpdate) {
if (debug) System.out.println("Executed: " + command + " with exit code " + exitCode); try {
if (exitCode != 0) { if(!isCircular){
System.err.println("Error executing " + rule.getName() + ""); System.out.println(command);
break; }
if (debug) System.out.println("Debug: Executing " + command);
ProcessBuilder pb = new ProcessBuilder("sh", "-c", command);
Process process = pb.start();
int exitCode = process.waitFor();
if (exitCode != 0) {
System.err.println("Error executing " + rule.getName());
System.exit(1);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
System.exit(1);
} }
} }
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} }
}
} // On affiche "up to date" seulement après avoir traité TOUTES les règles
// et seulement si AUCUNE règle n'avait besoin d'être mise à jour
if (rule.getName().equals(BakefileParser.getFirstTarget()) && !needsUpdate) {
System.out.println("bake: '" + rule.getName() + "' is up to date.");
}
}
}

View File

@@ -5,52 +5,98 @@ import java.util.*;
public class DependencyResolver { public class DependencyResolver {
private boolean debug; private boolean debug;
private Map<String, Rule> ruleMap; private Map<String, Rule> ruleMap;
private boolean isCircular;
public DependencyResolver(boolean debug) { public DependencyResolver(boolean debug) {
this.debug = debug; this.debug = debug;
this.isCircular = false;
} }
public List<Rule> resolve(List<Rule> allRules, List<String> requestedRules) { public List<Rule> resolve(List<Rule> allRules, List<String> requestedRules) {
List<Rule> rulesToBuild = new ArrayList<>(); List<Rule> rulesToBuild = new ArrayList<>();
ruleMap = new HashMap<>(); ruleMap = new HashMap<>();
Set<String> builtRules = new HashSet<>(); // Éviter d'ajouter des règles déjà vérifiées
// Construire la map des règles
for (Rule rule : allRules) { for (Rule rule : allRules) {
ruleMap.put(rule.getName(), rule); ruleMap.put(rule.getName(), rule);
} }
// Premier passage : détection et suppression des cycles
Set<String> visited = new HashSet<>(); Set<String> visited = new HashSet<>();
Set<String> stack = new HashSet<>(); Set<String> stack = new HashSet<>();
List<String> targetRules = requestedRules.isEmpty() ?
Collections.singletonList(BakefileParser.getFirstTarget()) :
requestedRules;
for (String ruleName : requestedRules.isEmpty() ? ruleMap.keySet() : requestedRules) { for (String ruleName : targetRules) {
if (ruleMap.containsKey(ruleName)) { if (ruleMap.containsKey(ruleName)) {
detectCycle(ruleName, visited, stack); detectCycle(ruleName, visited, stack, ruleName);
collectDependencies(ruleName, rulesToBuild, builtRules);
} else if (debug) { } else if (debug) {
System.out.println("Warning: Rule '" + ruleName + "' not found."); System.out.println("Warning: Rule '" + ruleName + "' not found.");
} }
} }
// Deuxième passage : collecte des dépendances dans l'ordre
Set<String> processed = new HashSet<>();
List<String> buildOrder = new ArrayList<>();
for (String ruleName : targetRules) {
topologicalSort(ruleName, processed, buildOrder);
}
// Construire la liste finale des règles dans l'ordre
for (String ruleName : buildOrder) {
Rule rule = ruleMap.get(ruleName);
if (rule != null) {
rulesToBuild.add(rule);
}
}
return rulesToBuild; return rulesToBuild;
} }
/** private void topologicalSort(String ruleName, Set<String> processed, List<String> buildOrder) {
* Détection des cycles avec DFS if (!ruleMap.containsKey(ruleName) || processed.contains(ruleName)) {
*/ return;
private void detectCycle(String ruleName, Set<String> visited, Set<String> stack) {
if (stack.contains(ruleName)) {
throw new RuntimeException("Dependency cycle detected involving rule: " + ruleName);
} }
processed.add(ruleName);
Rule rule = ruleMap.get(ruleName);
// D'abord traiter les dépendances
for (String dep : rule.getDependencies()) {
topologicalSort(dep, processed, buildOrder);
}
// Puis ajouter la règle courante
buildOrder.add(ruleName);
}
// La méthode detectCycle reste inchangée
private void detectCycle(String ruleName, Set<String> visited, Set<String> stack, String parent) {
if (stack.contains(ruleName)) {
if (parent != null) {
System.out.println("make: Circular " + parent + " <- " + ruleName + " dependency dropped.");
this.isCircular = true;
Rule parentRule = ruleMap.get(parent);
if (parentRule != null) {
parentRule.getDependencies().remove(ruleName);
}
}
return;
}
if (!visited.contains(ruleName)) { if (!visited.contains(ruleName)) {
visited.add(ruleName); visited.add(ruleName);
stack.add(ruleName); stack.add(ruleName);
Rule rule = ruleMap.get(ruleName); Rule rule = ruleMap.get(ruleName);
if (rule != null) { if (rule != null) {
for (String dependency : rule.getDependencies()) { List<String> dependenciesCopy = new ArrayList<>(rule.getDependencies());
for (String dependency : dependenciesCopy) {
if (ruleMap.containsKey(dependency)) { if (ruleMap.containsKey(dependency)) {
detectCycle(dependency, visited, stack); detectCycle(dependency, visited, stack, ruleName);
} }
} }
} }
@@ -58,23 +104,7 @@ public class DependencyResolver {
} }
} }
/** public boolean isCircular() {
* Collecte récursive des règles à construire en évitant les doublons return isCircular;
*/
private void collectDependencies(String ruleName, List<Rule> rulesToBuild, Set<String> builtRules) {
if (builtRules.contains(ruleName)) {
return; // Évite d'ajouter une règle plusieurs fois
}
Rule rule = ruleMap.get(ruleName);
if (rule != null) {
for (String dependency : rule.getDependencies()) {
if (ruleMap.containsKey(dependency)) {
collectDependencies(dependency, rulesToBuild, builtRules);
}
}
rulesToBuild.add(rule);
builtRules.add(ruleName);
}
} }
} }

View File

@@ -0,0 +1,11 @@
main: ClasseA ClasseB ClasseC Main.java
javac Main.java
ClasseA: ClasseB
javac ClasseA.java
ClasseB: ClasseA
javac ClasseB.java
ClasseC: ClasseB ClasseA
javac ClasseC.java

View File

@@ -0,0 +1,7 @@
public class ClasseA {
private ClasseB b;
public ClasseA(ClasseB b) {
this.b = b;
}
}

View File

@@ -0,0 +1,7 @@
public class ClasseB {
private ClasseA a;
public ClasseB(ClasseA a) {
this.a = a;
}
}

View File

@@ -0,0 +1,9 @@
public class ClasseC {
private ClasseA a;
private ClasseB b;
public ClasseC(ClasseA a, ClasseB b) {
this.a = a;
this.b = b;
}
}

View File

@@ -0,0 +1,16 @@
public class Main {
private ClasseA a;
private ClasseB b;
private ClasseC c;
public Main() {
this.a = new ClasseA(b);
this.b = new ClasseB(a);
this.c = new ClasseC(a,b);
}
public static void main(String[] args) {
Main m = new Main();
System.out.println("Ceci est un test de dépendences circulaires");
}
}

Binary file not shown.