diff --git a/.gitignore b/.gitignore index 1801c29..14c96f3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ *.class *.log *.core +minimake diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..69b5c09 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +guillem.george + +Special thanks to + +- Timothée Battefort for the parser diff --git a/minimake/Makefile b/minimake/Makefile index 895b2d5..fa1a362 100644 --- a/minimake/Makefile +++ b/minimake/Makefile @@ -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) diff --git a/minimake/minimake b/minimake/minimake deleted file mode 100755 index 5a2c793..0000000 Binary files a/minimake/minimake and /dev/null differ diff --git a/minimake/src/lines/lines.c b/minimake/src/lines/lines.c index 4201eea..bf82c56 100644 --- a/minimake/src/lines/lines.c +++ b/minimake/src/lines/lines.c @@ -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; } diff --git a/minimake/src/lines/lines.h b/minimake/src/lines/lines.h index 1eed08c..99e7f10 100644 --- a/minimake/src/lines/lines.h +++ b/minimake/src/lines/lines.h @@ -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 diff --git a/minimake/src/main.c b/minimake/src/main.c index 8a9362a..c2d7a0b 100644 --- a/minimake/src/main.c +++ b/minimake/src/main.c @@ -1,35 +1,127 @@ -// Error Codes -#define INVALID_ARG 2 - #include #include -#include +#include +// #include #include #include +#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); } diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index fa87c7b..c8d66be 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -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); +// } diff --git a/minimake/src/minimake.h b/minimake/src/minimake.h index b5cf465..55910e8 100644 --- a/minimake/src/minimake.h +++ b/minimake/src/minimake.h @@ -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 diff --git a/minimake/tests/Makefile.syntax-test b/minimake/tests/Makefile.syntax-test deleted file mode 100644 index fff0427..0000000 --- a/minimake/tests/Makefile.syntax-test +++ /dev/null @@ -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)"