Améliorations des dépendances circulaire
This commit is contained in:
BIN
bakefile.jar
BIN
bakefile.jar
Binary file not shown.
@@ -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);
|
||||||
|
@@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
11
tests/Java/test-03-circular/Bakefile
Normal file
11
tests/Java/test-03-circular/Bakefile
Normal 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
|
7
tests/Java/test-03-circular/ClasseA.java
Normal file
7
tests/Java/test-03-circular/ClasseA.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
public class ClasseA {
|
||||||
|
private ClasseB b;
|
||||||
|
|
||||||
|
public ClasseA(ClasseB b) {
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
}
|
7
tests/Java/test-03-circular/ClasseB.java
Normal file
7
tests/Java/test-03-circular/ClasseB.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
public class ClasseB {
|
||||||
|
private ClasseA a;
|
||||||
|
|
||||||
|
public ClasseB(ClasseA a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
}
|
9
tests/Java/test-03-circular/ClasseC.java
Normal file
9
tests/Java/test-03-circular/ClasseC.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
16
tests/Java/test-03-circular/Main.java
Normal file
16
tests/Java/test-03-circular/Main.java
Normal 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.
Reference in New Issue
Block a user