Amélioration de la gestion des dépendances circulaires
This commit is contained in:
parent
0af4eb8f1b
commit
0941b5e09e
bakefile.jar
src/fr/monlouyan/bakefile
tests
BIN
bakefile.jar
BIN
bakefile.jar
Binary file not shown.
@ -20,7 +20,7 @@ public class BakeCLI {
|
|||||||
/*
|
/*
|
||||||
* Liste des arguments passés en ligne de commande
|
* Liste des arguments passés en ligne de commande
|
||||||
*/
|
*/
|
||||||
private List<String> targets;
|
private static List<String> targets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructeur de la classe BakeCLI
|
* Constructeur de la classe BakeCLI
|
||||||
@ -31,7 +31,7 @@ public class BakeCLI {
|
|||||||
*/
|
*/
|
||||||
public BakeCLI(String[] args){
|
public BakeCLI(String[] args){
|
||||||
debug = false;
|
debug = false;
|
||||||
this.targets = new ArrayList<>();
|
targets = new ArrayList<>();
|
||||||
parseArgs(args);
|
parseArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,5 +60,5 @@ public class BakeCLI {
|
|||||||
* Permet de récupérer les arguments autres que "-d" passés en ligne de commande
|
* Permet de récupérer les arguments autres que "-d" passés en ligne de commande
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<String> getTargets(){ return targets; }
|
public static List<String> getTargets(){ return targets; }
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,11 @@ package fr.monlouyan.bakefile;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BakeEngine {
|
public class BakeEngine {
|
||||||
private BakeCLI cli;
|
|
||||||
private BakefileParser parser;
|
private BakefileParser parser;
|
||||||
private DependencyResolver resolver;
|
private DependencyResolver resolver;
|
||||||
private CommandExecutor executor;
|
private CommandExecutor executor;
|
||||||
|
|
||||||
public BakeEngine(BakeCLI cli) {
|
public BakeEngine() {
|
||||||
this.cli = cli;
|
|
||||||
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());
|
this.executor = new CommandExecutor(BakeCLI.isDebug());
|
||||||
@ -17,7 +15,7 @@ public class BakeEngine {
|
|||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
List<Rule> rules = parser.parse();
|
List<Rule> rules = parser.parse();
|
||||||
List<Rule> rulesToBuild = resolver.resolve(rules, cli.getTargets());
|
List<Rule> rulesToBuild = resolver.resolve(rules, BakeCLI.getTargets());
|
||||||
|
|
||||||
for (Rule rule : rulesToBuild) {
|
for (Rule rule : rulesToBuild) {
|
||||||
executor.execute(rule);
|
executor.execute(rule);
|
||||||
|
@ -8,6 +8,9 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class BakefileParser {
|
public class BakefileParser {
|
||||||
|
/**
|
||||||
|
* Nom du fichier Bakefile à parser (donc Bakefile...).
|
||||||
|
*/
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,8 +25,15 @@ public class BakefileParser {
|
|||||||
*/
|
*/
|
||||||
private static final Pattern COMMAND_PATTERN = Pattern.compile("^\\s+(.+)$");
|
private static final Pattern COMMAND_PATTERN = Pattern.compile("^\\s+(.+)$");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Première cible trouvée dans le fichier Bakefile.
|
||||||
|
*/
|
||||||
|
private static String firstTarget;
|
||||||
|
|
||||||
|
|
||||||
public BakefileParser(String filename) {
|
public BakefileParser(String filename) {
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
|
firstTarget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Rule> parse() {
|
public List<Rule> parse() {
|
||||||
@ -46,6 +56,10 @@ public class BakefileParser {
|
|||||||
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
|
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
|
||||||
|
|
||||||
if (targetMatcher.matches()) {
|
if (targetMatcher.matches()) {
|
||||||
|
if (firstTarget == null) {
|
||||||
|
firstTarget = targetMatcher.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Sauvegarde de la règle précédente si elle existe
|
// Sauvegarde de la règle précédente si elle existe
|
||||||
if (currentTarget != null) {
|
if (currentTarget != null) {
|
||||||
rules.add(new Rule(currentTarget, dependencies, commands, phonyTargets.contains(currentTarget)));
|
rules.add(new Rule(currentTarget, dependencies, commands, phonyTargets.contains(currentTarget)));
|
||||||
@ -71,4 +85,10 @@ public class BakefileParser {
|
|||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de récupérer la première cible trouvée dans le fichier Bakefile.
|
||||||
|
* @return La première cible trouvée
|
||||||
|
*/
|
||||||
|
public static String getFirstTarget() { return firstTarget; }
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,14 @@ public class CommandExecutor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rule.needsUpdate()){
|
if (!rule.needsUpdate()) {
|
||||||
System.out.println("bake: '" + rule.getName() + "' is up to date.");
|
if (rule.getName().equals(BakefileParser.getFirstTarget())) {
|
||||||
|
System.out.println("bake: '" + rule.getName() + "' is up to date.");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (String command : rule.getCommands()) {
|
for (String command : rule.getCommands()) {
|
||||||
System.out.println(command); // Affichage de la commande executée
|
System.out.println(command); // Affichage de la commande executée
|
||||||
|
@ -1,26 +1,80 @@
|
|||||||
package fr.monlouyan.bakefile;
|
package fr.monlouyan.bakefile;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DependencyResolver {
|
public class DependencyResolver {
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
private Map<String, Rule> ruleMap;
|
||||||
|
|
||||||
public DependencyResolver(boolean debug) {
|
public DependencyResolver(boolean debug) {
|
||||||
this.debug = debug;
|
this.debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
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<>();
|
||||||
|
Set<String> builtRules = new HashSet<>(); // Éviter d'ajouter des règles déjà vérifiées
|
||||||
|
|
||||||
for (Rule rule : allRules) {
|
for (Rule rule : allRules) {
|
||||||
if (requestedRules.isEmpty() || requestedRules.contains(rule.getName()) || rule.isPhony()) {
|
ruleMap.put(rule.getName(), rule);
|
||||||
if (debug){
|
}
|
||||||
System.out.println("Rule " + rule.getName() + " is requested");
|
|
||||||
}
|
Set<String> visited = new HashSet<>();
|
||||||
rulesToBuild.add(rule);
|
Set<String> stack = new HashSet<>();
|
||||||
|
|
||||||
|
for (String ruleName : requestedRules.isEmpty() ? ruleMap.keySet() : requestedRules) {
|
||||||
|
if (ruleMap.containsKey(ruleName)) {
|
||||||
|
detectCycle(ruleName, visited, stack);
|
||||||
|
collectDependencies(ruleName, rulesToBuild, builtRules);
|
||||||
|
} else if (debug) {
|
||||||
|
System.out.println("Warning: Rule '" + ruleName + "' not found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rulesToBuild;
|
return rulesToBuild;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Détection des cycles avec DFS
|
||||||
|
*/
|
||||||
|
private void detectCycle(String ruleName, Set<String> visited, Set<String> stack) {
|
||||||
|
if (stack.contains(ruleName)) {
|
||||||
|
throw new RuntimeException("Dependency cycle detected involving rule: " + ruleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visited.contains(ruleName)) {
|
||||||
|
visited.add(ruleName);
|
||||||
|
stack.add(ruleName);
|
||||||
|
|
||||||
|
Rule rule = ruleMap.get(ruleName);
|
||||||
|
if (rule != null) {
|
||||||
|
for (String dependency : rule.getDependencies()) {
|
||||||
|
if (ruleMap.containsKey(dependency)) {
|
||||||
|
detectCycle(dependency, visited, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack.remove(ruleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collecte récursive des règles à construire en évitant les doublons
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,8 +16,9 @@ public class Main{
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args){
|
public static void main(String[] args){
|
||||||
|
@SuppressWarnings("unused")
|
||||||
BakeCLI cli = new BakeCLI(args);
|
BakeCLI cli = new BakeCLI(args);
|
||||||
BakeEngine engine = new BakeEngine(cli);
|
BakeEngine engine = new BakeEngine();
|
||||||
engine.run();
|
engine.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,10 +2,10 @@ main: a b c
|
|||||||
gcc a b c -o main
|
gcc a b c -o main
|
||||||
|
|
||||||
a: a.c a.h b.h
|
a: a.c a.h b.h
|
||||||
gcc -Wall -Werror -Wextra -Pendatic -c a.c -o a
|
gcc -Wall -Werror -Wextra -ansi -pedantic -c a.c -o a
|
||||||
|
|
||||||
b: b.c b.h c.h
|
b: b.c b.h c.h
|
||||||
gcc -Wall -Werror -Wextra -Pendatic -c b.c -o b
|
gcc -Wall -Werror -Wextra -ansi -pedantic -c b.c -o b
|
||||||
|
|
||||||
c: c.c c.h a.h
|
c: c.c c.h a.h
|
||||||
gcc -Wall -Werror -Wextra -Pendatic -c c.c -o c
|
gcc -Wall -Werror -Wextra -ansi -pedantic -c c.c -o c
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
# Test 3 : Dépendances circulaire
|
# Test 3 : Gestion des dépendances circulaires
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Ce test vérifie que le système de compilation peut détecter et gérer correctement les dépendances circulaires. Il simule un projet où plusieurs fichiers `.c` et `.h` s'incluent mutuellement, créant ainsi une boucle dans les dépendances.
|
||||||
|
|
||||||
|
## Fichiers utilisés
|
||||||
|
- `a.c` : Implémente functionA qui appelle functionB.
|
||||||
|
- `b.c` : Implémente functionB qui appelle functionC.
|
||||||
|
- `c.c` : Implémente functionC et inclut a.h, créant une boucle indirecte.
|
||||||
|
- `a.h`, `b.h`, `c.h` : Fichiers d'en-tête correspondant.
|
||||||
|
- `Bakefile` : Contient les règles de compilation et les dépendances.
|
||||||
|
- `bakefile.jar` : Version compilée de notre système de build.
|
||||||
|
|
||||||
|
## Résultat attendu
|
||||||
|
Si le système de build détecte une dépendance circulaire, il doit la gérer automatiquement en évitant la boucle infinie et en compilant correctement les fichiers.
|
@ -1,12 +1,12 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "b.h"
|
#include "b.h"
|
||||||
|
|
||||||
void functionA() {
|
void functionA(void) {
|
||||||
printf("Function A called\n");
|
printf("Fonction A appelée\n");
|
||||||
functionB(); // Appelle une fonction de b.c
|
functionB();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main(void) {
|
||||||
functionA();
|
functionA();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef A_H
|
#ifndef A_H
|
||||||
#define A_H
|
#define A_H
|
||||||
|
|
||||||
void functionA();
|
void functionA(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "c.h"
|
#include "c.h"
|
||||||
|
|
||||||
void functionB() {
|
void functionB(void) {
|
||||||
printf("Function B called\n");
|
printf("Fonction B appelée\n");
|
||||||
functionC(); // Appelle une fonction de c.c
|
functionC();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef B_H
|
#ifndef B_H
|
||||||
#define B_H
|
#define B_H
|
||||||
|
|
||||||
void functionB();
|
void functionB(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Binary file not shown.
@ -1,7 +1,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include "a.h"
|
#include "a.h"
|
||||||
|
|
||||||
void functionC() {
|
void functionC(void) {
|
||||||
printf("Function C called\n");
|
printf("Fonction C appelée\n");
|
||||||
functionA(); // Appelle une fonction de a.c -> dépendance circulaire
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef C_H
|
#ifndef C_H
|
||||||
#define C_H
|
#define C_H
|
||||||
|
|
||||||
void functionC();
|
void functionC(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user