This commit is contained in:
Guillem George 2025-10-27 19:29:04 +01:00
parent 1fe9f668b6
commit 44d62db74c
10 changed files with 208 additions and 82 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@
*.class
*.log
*.core
minimake

5
AUTHORS Normal file
View file

@ -0,0 +1,5 @@
guillem.george
Special thanks to
- Timothée Battefort for the parser

View file

@ -1,6 +1,10 @@
CC = gcc
CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla
LDFLAGS=
DBG_CFLAGS = -fsanitize=address -g
DBG_LDFLAGS= -fsanitize=address
SRC_DIR = src
LIB_SRCS = lines/lines.c hash_maps/hash_maps.c lists/lists.c
@ -18,6 +22,7 @@ $(TARGET): $(OBJS)
@echo $(OBJS)
debug: CFLAGS += $(DBG_CFLAGS)
debug: LDFLAGS += $(DBG_LDFLAGS)
debug: $(OBJS)
$(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS)

Binary file not shown.

View file

@ -9,13 +9,14 @@
// Helps to match a string (excludes blanks and special characters)
int isChar(char c)
{
return c != '\0' && !isblank(c) && c != ':' && c != '=' && c != '#';
return c != '\0' && !isblank(c) && c != ':' && c != '=' && c != '#'
&& c != '\n';
}
int skipBlanks(char *buf, size_t buf_len)
{
size_t i = 0;
while (i < buf_len && isblank(buf[i]))
while (i < buf_len && (isblank(buf[i]) || buf[i] == '\n'))
i++;
return i;
}
@ -29,7 +30,7 @@ int isBlankLine(struct line *l)
size_t i = 0;
while (i < line_size)
{
if (!isblank(buf[i]))
if (!isblank(buf[i]) && buf[i] != '\n')
return 0;
if (buf[i] == '#') // Comments
return 1;
@ -41,28 +42,34 @@ int isBlankLine(struct line *l)
// Returns the next word from buf until buf_len,
// and the numbers of read characters in *read_chars
// WARNING allocates the result on the heap
char *readWord(char *buf, size_t buf_len, size_t *read_chars)
size_t readWord(char *buf, size_t buf_len, char **word)
{
size_t i = 0;
size_t str_buf_size = STRING_BUFFER_SIZE;
char *str_buf = malloc(sizeof(char) * str_buf_size);
size_t res_size = STRING_BUFFER_SIZE;
char *res = malloc(sizeof(char) * res_size);
while (i < buf_len && isChar(buf[i]))
{
// Reallocate more space if necessary
if (i >= str_buf_size - 1)
if (i >= res_size - 1)
{
str_buf_size += STRING_BUFFER_SIZE;
str_buf = realloc(str_buf, str_buf_size);
if (str_buf == NULL)
res_size += STRING_BUFFER_SIZE;
res = realloc(res, res_size);
if (res == NULL)
errx(2, "Could not realloc");
}
str_buf[i] = buf[i];
res[i] = buf[i];
i++;
}
res[i] = '\0';
str_buf[i] = '\0';
*read_chars = i;
if (i == 0)
{
free(res);
*word = NULL;
return i;
}
return str_buf;
*word = res;
return i;
}

View file

@ -12,7 +12,7 @@
struct line
{
char* buffer; // Line content
size_t length; // Line length
ssize_t length; // Line length
size_t number; // Line number in file
FILE* file_stream; // Full file stream
};
@ -20,6 +20,6 @@ struct line
int isChar(char c);
int isBlankLine(struct line *l);
int skipBlanks(char *buf, size_t buf_len);
char *readWord(char *buf, size_t buf_len, size_t *read_chars);
size_t readWord(char *buf, size_t buf_len, char **word);
#endif // LINES_H

View file

@ -1,35 +1,127 @@
// Error Codes
#define INVALID_ARG 2
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
// #include <errno.h>
#include <stdio.h>
#include <string.h>
#include "lists/lists.h"
#include "minimake.h"
// TODO:
// Look at perror for stdlib functions
// Create an enum for error handling
static int handle_args(int argc, char **argv)
static void print_help(char *program_name)
{
printf("Usage: %s [options] [target]... ", program_name);
puts("Options:");
puts(" -f FILE Reads FILE as a makefile");
puts(" -h Prints this message and exit");
puts(" -p Prints minimake's internal database");
puts("");
puts("Pas mal non ? C'est français");
}
static int handle_args(int argc, char **argv, struct list **minimake_files,
struct list **minimake_rules)
{
int flags = 0;
struct list *files = NULL;
struct list *rules = NULL;
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-h"))
errx(INVALID_ARG, "-h: not implemented");
else if (strcmp(argv[i], "-f"))
errx(INVALID_ARG, "-f: not implemented");
else if (strcmp(argv[i], "-p"))
errx(INVALID_ARG, "-p: not implemented");
else
errx(INVALID_ARG, ": Pleaase give an argument");
// Options
if (argv[i][0] == '-')
{
// No opt
if (strcmp(argv[i], "--") == 0)
{
// Treat as rules
for (int j = i; j < argc; j++)
{
files = list_append(rules, argv[i]);
}
*minimake_files = files;
*minimake_rules = rules;
return flags;
}
// Help
else if (strcmp(argv[i], "-h") == 0)
{
print_help(argv[0]);
exit(0);
}
// Custom file
else if (strcmp(argv[i], "-f") == 0)
{
flags |= FLAGS_CUSTOM_FILE;
if (i + 1 == argc || argv[i + 1][0] == '-')
errx(INVALID_ARG, "No file specified after '-f'");
files = list_prepend(files, argv[i]);
}
// Print
else if (strcmp(argv[i], "-p") == 0)
{
flags |= FLAGS_PRINT;
}
// Unknown option
else
{
printf("Unknown option '%s'", argv[i]);
print_help(argv[0]);
exit(INVALID_ARG);
}
}
else // Rules
{
files = list_append(rules, argv[i]);
}
}
printf("%s: done", argv[0]);
files = list_append(files, DEFAULT_MAKEFILE);
files = list_append(files, DEFAULT_MAKEFILE_2);
*minimake_files = files;
*minimake_rules = rules;
return flags;
}
char *getMakefile(struct list *files)
{
struct list *file = files;
int found = 0;
// Test file presence
while (file != NULL && !found)
{
FILE *test = fopen(file->data, "r");
if (test != NULL)
found = 1;
fclose(test);
if (!found)
file = file->next;
}
char *res = NULL;
if (found)
res = file->data;
list_destroy(files);
return res;
}
int main(int argc, char **argv)
{
return handle_args(argc, argv);
struct list *files;
struct list *rules;
int flags = handle_args(argc, argv, &files, &rules);
char *filename = getMakefile(files);
if (filename == NULL)
errx(GENERIC_ERR, "No Makefile found");
make(filename, flags);
}

View file

@ -29,8 +29,12 @@ static struct variable *createVariable(char *name)
int err = hashMapInsert(variables, name, NULL, NULL);
if (!err)
errx(1, "Internal Error: Couln't add entry for '%s' in the hashmap",
{
// free(name);
free(res);
errx(2, "Internal Error: Couln't add entry for '%s' in the hashmap",
name);
}
return res;
}
@ -54,29 +58,37 @@ static struct rule *createRule(char *name)
}
// Parse dependencies from buf and returns them inside a chained list
static struct list *readDependencies(char *buf, size_t buf_size)
static struct list *readDependencies(struct line *l, size_t offset)
{
size_t i = 0;
size_t i = offset;
size_t buf_size = l->length;
char *buf = l->buffer;
struct list *res = NULL;
while (i < buf_size)
{
i += skipBlanks(buf + i, buf_size - i);
// Read word
size_t skipped_chars = 0;
char *dep_name = readWord(buf + i, buf_size - i, &skipped_chars);
if (skipped_chars != 0)
char *dep_name;
i += readWord(buf + i, buf_size - i, &dep_name);
if (dep_name != NULL)
// Add to list
res = list_append(res, dep_name);
i += skipped_chars;
// Comments
if (buf[i] == '#')
return res;
// Unknown chars
if (!isChar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0')
errx(2, "Unexpected character '%c'", buf[i]);
if (!isChar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0'
&& buf[i] != '\n')
{
printf("DEBUG: line: %s", l->buffer);
errx(2,
"Unexpected character '%c' after rule declaration at %lu:%lu",
buf[i], l->number, i);
}
}
return res;
@ -94,7 +106,7 @@ static struct list *readRecipe(struct line *l)
struct list *res = NULL;
// Getline
while ((l->length = getline(&buf, &buf_size, stream)))
while ((l->length = getline(&buf, &buf_size, stream)) != -1)
{
l->number++;
@ -148,6 +160,8 @@ static char *readValue(char *buf, size_t buf_size)
return str_buf;
}
// static char *expand_variable();
// Takes a buffer containing the line to parse and it length
// As well as the line number in the file for error handling
static void parseLine(struct line *current_line)
@ -160,9 +174,11 @@ static void parseLine(struct line *current_line)
i += skipBlanks(buf + i, line_size - i);
// Read name
size_t skipped_chars = 0;
char *name = readWord(buf + i, line_size - i, &skipped_chars);
i += skipped_chars;
char *name;
i += readWord(buf + i, line_size - i, &name);
// if (name == NULL)
// errx(1, "Il s'est passé quoi là ? \nUnexpected character at %lu:%lu",
// current_line->number, i);
i += skipBlanks(buf + i, line_size - i);
@ -182,10 +198,16 @@ static void parseLine(struct line *current_line)
// Rule
case ':':
new_rule = createRule(name);
new_rule->dependencies = readDependencies(buf + i, line_size);
new_rule->dependencies = readDependencies(current_line, i + 1);
new_rule->recipe = readRecipe(current_line);
// Check for EOF
if (current_line->length == -1)
return;
parseLine(current_line);
// TODO: check for loooooops
break;
// Blank line
@ -203,7 +225,7 @@ static void parseLine(struct line *current_line)
buf[i], name, current_line->number);
}
free(name);
// free(name);
}
void makeParse(char *path)
@ -249,3 +271,21 @@ void makeParse(char *path)
}
// ==== Runtime ====
// ==== MAKE ====
void make(char *path, int flags)
{
if (flags & FLAGS_PRINT)
errx(GENERIC_ERR, "Not Implemented");
makeParse(path);
}
// ==== Misc ====
// static void free_all()
// {
// hashMapFree(rules);
// hashMapFree(variables);
// }

View file

@ -1,6 +1,17 @@
#ifndef MINIMAKE_H
#define MINIMAKE_H
// Error Codes
#define INVALID_ARG 2
#define GENERIC_ERR 2
// Flags
#define FLAGS_CUSTOM_FILE 1
#define FLAGS_PRINT 2
// Default values
#define DEFAULT_MAKEFILE "Makefile"
#define DEFAULT_MAKEFILE_2 "makefile"
#define BUFFER_SIZE 1024
#define HASHMAP_SIZE 32
@ -25,6 +36,6 @@ struct rule
struct list *recipe;
};
int make(char *path, int flags);
void make(char *path, int flags);
#endif // ! MINIMAKE_H

View file

@ -1,35 +0,0 @@
SIMPLE_VAR = coucou
SIMPLE_VAR_COMMENT = the comment is gone # comment
# the following line starts with a space then a tab
SPACES_BEFORE_TAB = var_beginning var_end
sparse_rule: depa depb
command 1
command 2
B = B_var_beginning B_var_end
packed_rule: depa depb
command 1
command 2
silent_rule: depa depb
@ command 1
@command 2
rule_comment: depa depb # comment
command_space_rule: depa depb
echo spaces before
echo spaces after
echo this is a # comment
simple_rule: simple_dep
no_dep_rule:
variable_rule: beginning $(SIMPLE_VAR) end
echo "shouldn't be expanded: $(SIMPLE_VAR)"