Améliorations du parser et ajout de nouveaux fichiers de test pour vérifier le comportement des variables sur plusieurs lignes avec Bake et Make, et suppression des fichiers obsolètes.
This commit is contained in:
parent
a228509e8f
commit
51d6970ef1
src/fr/monlouyan/bakefile
tests/C/test-16-strange-variables
@ -9,41 +9,54 @@ import java.util.regex.Pattern;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class BakefileParser {
|
public class BakefileParser {
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* Nom du fichier Bakefile à parser (donc Bakefile...).
|
* Nom du fichier Bakefile à parser (donc Bakefile...).
|
||||||
*/
|
*/
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex pour détecter les targets et leurs dépendances.
|
* Regex pour détecter les targets et leurs dépendances.
|
||||||
* Format : "nom : dépendance1 dépendance2"
|
* Format : "nom : dépendance1 dépendance2"
|
||||||
*/
|
*/
|
||||||
private static final Pattern TARGET_PATTERN = Pattern.compile("^(\\S+)\\s*:\\s*([^#]*?)\\s*(?:#.*)?$");
|
private static final Pattern TARGET_PATTERN = Pattern.compile("^(\\S+)\\s*:\\s*([^#]*?)\\s*(?:#.*)?$");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex pour détecter les lignes de commande associées à une target.
|
* Regex pour détecter les lignes de commande associées à une target.
|
||||||
* Format : " gcc -o program program.c" (ligne indentée)
|
* Format : " gcc -o program program.c" (ligne indentée)
|
||||||
*/
|
*/
|
||||||
private static final Pattern COMMAND_PATTERN = Pattern.compile("^\\t(.+)$");
|
private static final Pattern COMMAND_PATTERN = Pattern.compile("^\\t(.+)$");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex pour détecter les définitions de variables.
|
* Regex pour détecter les définitions de variables.
|
||||||
* Format : "FLAGS = -ansi -pedantic"
|
* Format : "FLAGS = -ansi -pedantic"
|
||||||
*/
|
*/
|
||||||
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
|
* Regex pour détecter les déclarations .PHONY
|
||||||
* Format : ".PHONY: clean all"
|
* Format : ".PHONY: clean all"
|
||||||
*/
|
*/
|
||||||
private static final Pattern PHONY_PATTERN = Pattern.compile("^\\.PHONY:\\s*([^#]*?)\\s*(?:#.*)?$");
|
private static final Pattern PHONY_PATTERN = Pattern.compile("^\\.PHONY:\\s*([^#]*?)\\s*(?:#.*)?$");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Regex pour détecter les lignes de continuation.
|
||||||
|
* Format : " gcc -o program program.c \"
|
||||||
|
*/
|
||||||
|
private static final Pattern CONTINUATION_PATTERN = Pattern.compile("^(.*)\\\\\\s*$");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex pour détecter les références de variables.
|
||||||
|
* Format : "${VAR}" ou "$(VAR)"
|
||||||
|
*/
|
||||||
|
private static final Pattern VARIABLE_REFERENCE = Pattern.compile("\\$\\{(\\w+)\\}|\\$\\((\\w+)\\)");
|
||||||
|
|
||||||
|
/**
|
||||||
* Première cible trouvée dans le fichier Bakefile.
|
* Première cible trouvée dans le fichier Bakefile.
|
||||||
*/
|
*/
|
||||||
private static String firstTarget;
|
private static String firstTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stocke les variables définies dans le Bakefile.
|
* Stocke les variables définies dans le Bakefile.
|
||||||
*/
|
*/
|
||||||
private Map<String, String> variables = new HashMap<>();
|
private Map<String, String> variables = new HashMap<>();
|
||||||
@ -53,15 +66,77 @@ public class BakefileParser {
|
|||||||
firstTarget = null;
|
firstTarget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String handleContinuationLines(List<String> lines, int startIndex) {
|
||||||
|
StringBuilder combinedLine = new StringBuilder();
|
||||||
|
int i = startIndex;
|
||||||
|
|
||||||
|
while (i < lines.size()) {
|
||||||
|
String line = lines.get(i);
|
||||||
|
Matcher contMatcher = CONTINUATION_PATTERN.matcher(line);
|
||||||
|
|
||||||
|
if (contMatcher.matches()) {
|
||||||
|
// Ajouter la ligne sans le backslash
|
||||||
|
combinedLine.append(contMatcher.group(1).trim()).append(" ");
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// Ajouter la dernière ligne et sortir
|
||||||
|
combinedLine.append(line.trim());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedLine.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String replaceVariables(String input) {
|
||||||
|
if (input == null) return null;
|
||||||
|
|
||||||
|
String result = input;
|
||||||
|
Set<String> processedVars = new HashSet<>();
|
||||||
|
boolean changed;
|
||||||
|
|
||||||
|
do {
|
||||||
|
changed = false;
|
||||||
|
Matcher matcher = VARIABLE_REFERENCE.matcher(result);
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
String varName = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
|
||||||
|
if (!processedVars.contains(varName) && variables.containsKey(varName)) {
|
||||||
|
String replacement = variables.get(varName);
|
||||||
|
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
|
||||||
|
changed = true;
|
||||||
|
processedVars.add(varName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matcher.appendTail(sb);
|
||||||
|
result = sb.toString();
|
||||||
|
|
||||||
|
// Si aucun changement n'a été fait dans ce passage, arrêter
|
||||||
|
if (!changed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Réinitialiser processedVars pour le prochain passage si nécessaire
|
||||||
|
processedVars.clear();
|
||||||
|
|
||||||
|
} while (changed);
|
||||||
|
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> replaceVariablesInList(List<String> items) {
|
||||||
|
return items.stream()
|
||||||
|
.map(this::replaceVariables)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
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<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remplacer les variables avant de split
|
|
||||||
String resolvedStr = replaceVariables(depStr.trim());
|
String resolvedStr = replaceVariables(depStr.trim());
|
||||||
|
|
||||||
// Split sur un ou plusieurs espaces et filtrer les chaînes vides
|
|
||||||
return Arrays.stream(resolvedStr.split("\\s+"))
|
return Arrays.stream(resolvedStr.split("\\s+"))
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.filter(s -> !s.isEmpty())
|
.filter(s -> !s.isEmpty())
|
||||||
@ -82,34 +157,48 @@ public class BakefileParser {
|
|||||||
String currentTarget = null;
|
String currentTarget = null;
|
||||||
List<String> dependencies = new ArrayList<>();
|
List<String> dependencies = new ArrayList<>();
|
||||||
List<String> commands = new ArrayList<>();
|
List<String> commands = new ArrayList<>();
|
||||||
int lineNumber = 0;
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
for (String line : lines) {
|
String line = lines.get(i);
|
||||||
lineNumber++;
|
|
||||||
|
// Vérifier si la ligne a un caractère de continuation
|
||||||
Matcher varMatcher = VARIABLE_PATTERN.matcher(line);
|
Matcher contMatcher = CONTINUATION_PATTERN.matcher(line);
|
||||||
Matcher targetMatcher = TARGET_PATTERN.matcher(line);
|
if (contMatcher.matches()) {
|
||||||
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
|
// Récupérer toute la définition multi-ligne
|
||||||
Matcher phonyMatcher = PHONY_PATTERN.matcher(line);
|
line = handleContinuationLines(lines, i);
|
||||||
|
// Ajuster i pour sauter les lignes traitées
|
||||||
|
while (i + 1 < lines.size() &&
|
||||||
|
CONTINUATION_PATTERN.matcher(lines.get(i)).matches()) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (line.trim().isEmpty()) {
|
if (line.trim().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.matches("^ +.*$")) {
|
if (line.matches("^ +.*$")) {
|
||||||
System.err.println(filename + ":" + lineNumber + ": *** missing separator. Stop.");
|
System.err.println(filename + ":" + (i+1) + ": *** missing separator. Stop.");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Matcher varMatcher = VARIABLE_PATTERN.matcher(line);
|
||||||
|
Matcher targetMatcher = TARGET_PATTERN.matcher(line);
|
||||||
|
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
|
||||||
|
Matcher phonyMatcher = PHONY_PATTERN.matcher(line);
|
||||||
|
|
||||||
if (phonyMatcher.matches()) {
|
if (phonyMatcher.matches()) {
|
||||||
// Ajouter les cibles .PHONY à l'ensemble des cibles phony
|
|
||||||
String[] phonies = phonyMatcher.group(1).trim().split("\\s+");
|
String[] phonies = phonyMatcher.group(1).trim().split("\\s+");
|
||||||
Collections.addAll(phonyTargets, phonies);
|
Collections.addAll(phonyTargets, phonies);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (varMatcher.matches()) {
|
if (varMatcher.matches()) {
|
||||||
variables.put(varMatcher.group(1), varMatcher.group(2).trim());
|
String varName = varMatcher.group(1);
|
||||||
|
String varValue = varMatcher.group(2).trim();
|
||||||
|
// Évaluer les variables référencées dans la valeur
|
||||||
|
varValue = replaceVariables(varValue);
|
||||||
|
variables.put(varName, varValue);
|
||||||
} else if (targetMatcher.matches()) {
|
} else if (targetMatcher.matches()) {
|
||||||
if (currentTarget != null) {
|
if (currentTarget != null) {
|
||||||
String resolvedTarget = replaceVariables(currentTarget.trim());
|
String resolvedTarget = replaceVariables(currentTarget.trim());
|
||||||
@ -121,7 +210,6 @@ public class BakefileParser {
|
|||||||
phonyTargets.contains(resolvedTarget)
|
phonyTargets.contains(resolvedTarget)
|
||||||
));
|
));
|
||||||
|
|
||||||
// Ne définir firstTarget que si ce n'est pas une cible .PHONY
|
|
||||||
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
|
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
|
||||||
firstTarget = resolvedTarget;
|
firstTarget = resolvedTarget;
|
||||||
}
|
}
|
||||||
@ -145,7 +233,6 @@ public class BakefileParser {
|
|||||||
phonyTargets.contains(resolvedTarget)
|
phonyTargets.contains(resolvedTarget)
|
||||||
));
|
));
|
||||||
|
|
||||||
// Vérifier une dernière fois pour firstTarget
|
|
||||||
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
|
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
|
||||||
firstTarget = resolvedTarget;
|
firstTarget = resolvedTarget;
|
||||||
}
|
}
|
||||||
@ -157,36 +244,10 @@ public class BakefileParser {
|
|||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String replaceVariables(String input) {
|
/**
|
||||||
if (input == null) return null;
|
* Récupérer la première cible
|
||||||
String result = input;
|
* @return String la première cible
|
||||||
boolean replaced;
|
*/
|
||||||
do {
|
|
||||||
replaced = false;
|
|
||||||
for (Map.Entry<String, String> entry : variables.entrySet()) {
|
|
||||||
String key = "$(" + entry.getKey() + ")";
|
|
||||||
if (result.contains(key)) {
|
|
||||||
// Préserver les espaces dans les valeurs des variables
|
|
||||||
result = result.replace(key, entry.getValue());
|
|
||||||
replaced = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (replaced);
|
|
||||||
return result.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> replaceVariablesInList(List<String> items) {
|
|
||||||
List<String> resolved = new ArrayList<>();
|
|
||||||
for (String item : items) {
|
|
||||||
resolved.add(replaceVariables(item));
|
|
||||||
}
|
|
||||||
return resolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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() {
|
public static String getFirstTarget() {
|
||||||
return firstTarget;
|
return firstTarget;
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,21 @@
|
|||||||
# Ce makefile viens de : https://iut-fbleau.fr/sitebp/doc_make/makefile/
|
COMPILER = gcc
|
||||||
### VARIABLES ###
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
CFLAGS = -Wall \
|
CFLAGS = -Wall \
|
||||||
-ansi \
|
-ansi \
|
||||||
-pedantic
|
-pedantic
|
||||||
LIBS = -lm
|
OUTPUT = program
|
||||||
EXE = exemple
|
|
||||||
OFILES = extension.o \
|
OBJS = main.o module.o
|
||||||
main.o
|
|
||||||
|
# Règle principale
|
||||||
### BUT PAR DEFAUT ###
|
$(OUTPUT): $(OBJS)
|
||||||
|
$(COMPILER) $(CFLAGS) -o $(OUTPUT) $(OBJS)
|
||||||
but : ${EXE}
|
|
||||||
|
main.o: main.c
|
||||||
### REGLES ESSENTIELLES ###
|
$(COMPILER) $(CFLAGS) -c main.c -o main.o
|
||||||
|
|
||||||
extension.o : extension.h
|
module.o: module.c
|
||||||
|
$(COMPILER) $(CFLAGS) -c module.c -o module.o
|
||||||
main.o : extension.h
|
|
||||||
|
clean:
|
||||||
${EXE} : ${OFILES}
|
rm -f $(OUTPUT) $(OBJS)
|
||||||
$(CC) $(CFLAGS) -o ${EXE} ${OFILES} ${LIBS}
|
|
||||||
|
|
||||||
### REGLES OPTIONNELLES ###
|
|
||||||
|
|
||||||
run : but
|
|
||||||
./${EXE}
|
|
||||||
|
|
||||||
clean :
|
|
||||||
-rm -f ${OFILES} ${EXE}
|
|
||||||
|
|
||||||
mrproper : clean but
|
|
||||||
|
|
||||||
### BUTS FACTICES ###
|
|
||||||
|
|
||||||
.PHONY : but clean mrproper
|
|
||||||
|
|
||||||
### FIN ###
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include "extension.h"
|
|
||||||
|
|
||||||
void afficher_message(void) {
|
|
||||||
printf("Hello, world! Ceci est un message depuis extension.c\n");
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#ifndef EXTENSION_H
|
|
||||||
#define EXTENSION_H
|
|
||||||
|
|
||||||
void afficher_message(void);
|
|
||||||
|
|
||||||
#endif /* EXTENSION_H */
|
|
@ -1,6 +1,8 @@
|
|||||||
#include "extension.h"
|
#include <stdio.h>
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
afficher_message();
|
printf("Hello from main!\n");
|
||||||
|
print_message();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
6
tests/C/test-16-strange-variables/bake/module.c
Normal file
6
tests/C/test-16-strange-variables/bake/module.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
|
void print_message(void) {
|
||||||
|
printf("Hello from module!\n");
|
||||||
|
}
|
6
tests/C/test-16-strange-variables/bake/module.h
Normal file
6
tests/C/test-16-strange-variables/bake/module.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef MODULE_H
|
||||||
|
#define MODULE_H
|
||||||
|
|
||||||
|
void print_message(void);
|
||||||
|
|
||||||
|
#endif
|
@ -1,40 +1,21 @@
|
|||||||
# Ce makefile viens de : https://iut-fbleau.fr/sitebp/doc_make/makefile/
|
COMPILER = gcc
|
||||||
### VARIABLES ###
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
CFLAGS = -Wall \
|
CFLAGS = -Wall \
|
||||||
-ansi \
|
-ansi \
|
||||||
-pedantic
|
-pedantic
|
||||||
LIBS = -lm
|
OUTPUT = program
|
||||||
EXE = exemple
|
|
||||||
OFILES = extension.o \
|
OBJS = main.o module.o
|
||||||
main.o
|
|
||||||
|
# Règle principale
|
||||||
### BUT PAR DEFAUT ###
|
$(OUTPUT): $(OBJS)
|
||||||
|
$(COMPILER) $(CFLAGS) -o $(OUTPUT) $(OBJS)
|
||||||
but : ${EXE}
|
|
||||||
|
main.o: main.c
|
||||||
### REGLES ESSENTIELLES ###
|
$(COMPILER) $(CFLAGS) -c main.c -o main.o
|
||||||
|
|
||||||
extension.o : extension.h
|
module.o: module.c
|
||||||
|
$(COMPILER) $(CFLAGS) -c module.c -o module.o
|
||||||
main.o : extension.h
|
|
||||||
|
clean:
|
||||||
${EXE} : ${OFILES}
|
rm -f $(OUTPUT) $(OBJS)
|
||||||
$(CC) $(CFLAGS) -o ${EXE} ${OFILES} ${LIBS}
|
|
||||||
|
|
||||||
### REGLES OPTIONNELLES ###
|
|
||||||
|
|
||||||
run : but
|
|
||||||
./${EXE}
|
|
||||||
|
|
||||||
clean :
|
|
||||||
-rm -f ${OFILES} ${EXE}
|
|
||||||
|
|
||||||
mrproper : clean but
|
|
||||||
|
|
||||||
### BUTS FACTICES ###
|
|
||||||
|
|
||||||
.PHONY : but clean mrproper
|
|
||||||
|
|
||||||
### FIN ###
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include "extension.h"
|
|
||||||
|
|
||||||
void afficher_message(void) {
|
|
||||||
printf("Hello, world! Ceci est un message depuis extension.c\n");
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#ifndef EXTENSION_H
|
|
||||||
#define EXTENSION_H
|
|
||||||
|
|
||||||
void afficher_message(void);
|
|
||||||
|
|
||||||
#endif /* EXTENSION_H */
|
|
@ -1,6 +1,8 @@
|
|||||||
#include "extension.h"
|
#include <stdio.h>
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
afficher_message();
|
printf("Hello from main!\n");
|
||||||
|
print_message();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
6
tests/C/test-16-strange-variables/make/module.c
Normal file
6
tests/C/test-16-strange-variables/make/module.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
|
void print_message(void) {
|
||||||
|
printf("Hello from module!\n");
|
||||||
|
}
|
6
tests/C/test-16-strange-variables/make/module.h
Normal file
6
tests/C/test-16-strange-variables/make/module.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef MODULE_H
|
||||||
|
#define MODULE_H
|
||||||
|
|
||||||
|
void print_message(void);
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user