Files
SAE32_2024/src/fr/monlouyan/bakefile/BakefileParser.java

133 lines
4.6 KiB
Java
Raw Normal View History

package fr.monlouyan.bakefile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BakefileParser {
/**
* Nom du fichier Bakefile à parser (donc Bakefile...).
*/
private String filename;
/**
* Regex pour détecter les targets et leurs dépendances.
* Format : "nom : dépendance1 dépendance2"
*/
private static final Pattern TARGET_PATTERN = Pattern.compile("^(\\S+)\\s*:\\s*(.*)$");
/**
* Regex pour détecter les lignes de commande associées à une target.
* Format : " gcc -o program program.c" (ligne indentée)
*/
private static final Pattern COMMAND_PATTERN = Pattern.compile("^\\s+(.+)$");
/**
* Regex pour détecter les définitions de variables.
* Format : "FLAGS = -ansi -pedantic"
*/
private static final Pattern VARIABLE_PATTERN = Pattern.compile("^(\\w+)\\s*=\\s*(.*)$");
/**
* Première cible trouvée dans le fichier Bakefile.
*/
private static String firstTarget;
/**
* Stocke les variables définies dans le Bakefile.
*/
private Map<String, String> variables = new HashMap<>();
public BakefileParser(String filename) {
this.filename = filename;
firstTarget = null;
}
2025-02-04 17:31:25 +01:00
public List<Rule> parse() {
List<Rule> rules = new ArrayList<>();
Set<String> phonyTargets = new HashSet<>();
2025-02-04 10:34:06 +01:00
if (!Files.exists(Paths.get(filename))) {
System.out.println("*** No targets specified and no makefile found. Stop.");
System.exit(1);
}
try {
List<String> lines = Files.readAllLines(Paths.get(filename));
String currentTarget = null;
List<String> dependencies = new ArrayList<>();
List<String> commands = new ArrayList<>();
for (String line : lines) {
Matcher varMatcher = VARIABLE_PATTERN.matcher(line);
Matcher targetMatcher = TARGET_PATTERN.matcher(line);
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
if (varMatcher.matches()) {
// Stocke la variable
variables.put(varMatcher.group(1), varMatcher.group(2));
} else if (targetMatcher.matches()) {
if (firstTarget == null) {
firstTarget = targetMatcher.group(1);
}
if (currentTarget != null) {
rules.add(new Rule(currentTarget, dependencies, replaceVariables(commands), phonyTargets.contains(currentTarget)));
}
currentTarget = targetMatcher.group(1);
dependencies = new ArrayList<>(Arrays.asList(targetMatcher.group(2).trim().split("\\s+")));
commands = new ArrayList<>();
} else if (commandMatcher.matches()) {
commands.add(commandMatcher.group(1));
}
}
if (currentTarget != null) {
rules.add(new Rule(currentTarget, dependencies, replaceVariables(commands), phonyTargets.contains(currentTarget)));
}
} catch (IOException e) {
e.printStackTrace();
}
2025-02-04 17:31:25 +01:00
return rules;
}
/**
* Remplace les variables dans une liste de commandes.
* Ex : "gcc $(FLAGS) -o main main.c" devient "gcc -ansi -pedantic -o main main.c"
* @param commands Liste des commandes à modifier
* @return Liste avec les variables remplacées
*/
private List<String> replaceVariables(List<String> commands) {
List<String> resolvedCommands = new ArrayList<>();
for (String command : commands) {
boolean replaced;
do {
replaced = false;
for (Map.Entry<String, String> entry : variables.entrySet()) {
String key = "$(" + entry.getKey() + ")";
if (command.contains(key)) {
command = command.replace(key, entry.getValue());
replaced = true;
}
}
} while (replaced); // Continue tant qu'on remplace des variables
resolvedCommands.add(command);
}
return resolvedCommands;
}
/**
* 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; }
}