From c358562ecc709831dad989ee963a96c6e92e73e3 Mon Sep 17 00:00:00 2001 From: Guillem George Date: Thu, 30 Oct 2025 21:14:53 +0100 Subject: [PATCH 01/10] EXPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnd --- minimake/src/lines/lines.c | 160 +++++++++- minimake/src/lines/lines.h | 4 + minimake/src/lists/lists.c | 13 + minimake/src/lists/lists.h | 14 +- minimake/src/main.c | 14 +- minimake/src/minimake.c | 428 +++++++++++++-------------- minimake/src/minimake.h | 4 + minimake/tests/Makefile2.syntax-test | 36 +++ 8 files changed, 443 insertions(+), 230 deletions(-) create mode 100644 minimake/tests/Makefile2.syntax-test diff --git a/minimake/src/lines/lines.c b/minimake/src/lines/lines.c index dcf0c19..fcd33e1 100644 --- a/minimake/src/lines/lines.c +++ b/minimake/src/lines/lines.c @@ -4,8 +4,11 @@ #include #include #include +#include #include +#include "../minimake.h" + // Helps to match a string (excludes blanks and special characters) int ischar(char c) { @@ -50,13 +53,7 @@ size_t readword(char *buf, size_t buf_len, char **word) while (i < buf_len && ischar(buf[i])) { // Reallocate more space if necessary - if (i >= res_size - 1) - { - res_size += STRING_BUFFER_SIZE; - res = realloc(res, res_size); - if (res == NULL) - errx(2, "Could not realloc"); - } + resize_buf(&res, res_size, i); res[i] = buf[i]; i++; @@ -73,3 +70,152 @@ size_t readword(char *buf, size_t buf_len, char **word) *word = res; return i; } + +// Resizes buf if index is greater or equal than buf_size-1 +// Returns new buffer size +size_t resize_buf(char **buf, size_t buf_size, size_t index) +{ + if (index >= buf_size - 1) + { + buf_size += BUFFER_SIZE; + *buf = realloc(*buf, buf_size); + if (buf == NULL) + drop(2, "Could not realloc"); + } + return buf_size; +} + +// Inserts str into base at index and returns the resulting string +// WARNING allocates the result on the heap, do not forget to free it +char *insert_str(char *base, char *str, size_t index) +{ + size_t res_size = BUFFER_SIZE; + char *res = malloc(res_size * sizeof(char)); + if (res == NULL) + drop(GENERIC_ERR, "Could not allocate more memory"); + + // Copy until index + size_t i = 0; + while (i < index) + { + // Reallocate more space if necessary + res_size = resize_buf(&res, res_size, i); + + res[i] = base[i]; + i++; + } + + // Copy str + size_t str_i = 0; + while (str[str_i] != '\0') + { + // Reallocate more space if necessary + res_size = resize_buf(&res, res_size, i); + + res[i] = str[str_i]; + i++; + str_i++; + } + + // Copy rest + while (base[i] != '\0') + { + // Reallocate more space if necessary + res_size = resize_buf(&res, res_size, i); + + res[i] = base[i]; + i++; + } + + res[i] = '\0'; + return res; +} + +// Appends str into base at index and stores the resulting string in *result +// Returns the size of the resulting buffer +// WARNING allocates the result on the heap, do not forget to free it +size_t append_str(char *base, char *str, size_t index, char **result) +{ + size_t res_size = BUFFER_SIZE; + char *res = malloc(res_size * sizeof(char)); + if (res == NULL) + drop(GENERIC_ERR, "Could not allocate more memory"); + + // Copy until index + size_t i = 0; + while (i < index) + { + // Reallocate more space if necessary + res_size = resize_buf(&res, res_size, i); + + res[i] = base[i]; + i++; + } + + // Copy str + size_t str_i = 0; + while (str[str_i] != '\0') + { + // Reallocate more space if necessary + res_size = resize_buf(&res, res_size, i); + + res[i] = str[str_i]; + i++; + str_i++; + } + res[i] = '\0'; + + *result = res; + return res_size; +} + +// Read word until ':', '=' and blanks and expands variable if any is found +// Returns the number of skipped chars and stores the resulting string in word +size_t read_declaration(struct line *line, size_t index, char **word) +{ + size_t i = index; + size_t buf_len = line->length; + char *buf = line->buffer; + + size_t res_size = STRING_BUFFER_SIZE; + char *res = malloc(sizeof(char) * res_size); + if (res == NULL) + drop(GENERIC_ERR, "Could not allocate more memory"); + + size_t res_i = 0; + while (i < buf_len && ischar(buf[i])) + { + // Reallocate more space if necessary + res_size = resize_buf(&res, res_size, i); + + // Expand variable + if (buf[i] == '$') + { + char *expanded_var; + char *tmp_res_buf; + i += expand_variable(line, i + 1, &expanded_var); + res_size = append_str(res, expanded_var, i, &tmp_res_buf); + // Replace res with res + expanded_variable + res_i = strlen(tmp_res_buf); + free(res); + // free(expanded_var); + res = tmp_res_buf; + continue; + } + + res[res_i] = buf[i]; + i++; + res_i++; + } + res[res_i] = '\0'; + + if (i == index) + { + free(res); + *word = NULL; + return i - index; + } + + *word = res; + return i - index; +} diff --git a/minimake/src/lines/lines.h b/minimake/src/lines/lines.h index 01ace9d..081d305 100644 --- a/minimake/src/lines/lines.h +++ b/minimake/src/lines/lines.h @@ -22,5 +22,9 @@ int ischar(char c); int isblankline(struct line *l); int skipblanks(char *buf, size_t buf_len); size_t readword(char *buf, size_t buf_len, char **word); +size_t resize_buf(char** buf, size_t buf_size, size_t index); +char *insert_str(char *base, char *str, size_t index); +size_t read_declaration(struct line *line, size_t index, char **word); +size_t append_str(char *base, char *str, size_t index, char **res); #endif // LINES_H diff --git a/minimake/src/lists/lists.c b/minimake/src/lists/lists.c index aa355a1..e8a681b 100644 --- a/minimake/src/lists/lists.c +++ b/minimake/src/lists/lists.c @@ -331,3 +331,16 @@ struct list *list_split(struct list *list, size_t index) elt->next = NULL; return res; } + +void list_deep_destroy(struct list *l) +{ + struct list *elt = l; + struct list *next_elt; + while (elt != NULL) + { + next_elt = elt->next; + free(elt->data); + free(elt); + elt = next_elt; + } +} diff --git a/minimake/src/lists/lists.h b/minimake/src/lists/lists.h index a8f9b10..8c4114f 100644 --- a/minimake/src/lists/lists.h +++ b/minimake/src/lists/lists.h @@ -33,6 +33,12 @@ void list_print(struct list *list); */ void list_destroy(struct list *list); +/* +** Release the memory used by the list and its content +** Does nothing if `list` is `NULL`. +*/ +void list_deep_destroy(struct list *l); + /* ** Append a node containing `value` at the end of the list. ** Return `NULL` if an error occured. @@ -72,7 +78,7 @@ int list_find(struct list *list, void* value); ** Return `list2` if `list` is `NULL`. */ // START PROTO list_concat -struct list *list_concat(struct list *list, struct list *list2); +// struct list *list_concat(struct list *list, struct list *list2); // END PROTO list_concat /* @@ -80,7 +86,7 @@ struct list *list_concat(struct list *list, struct list *list2); ** Return the new list. */ // START PROTO list_sort -struct list *list_sort(struct list *list); +// struct list *list_sort(struct list *list); // END PROTO list_sort /* @@ -88,7 +94,7 @@ struct list *list_sort(struct list *list); ** Return the new list. */ // START PROTO list_reverse -struct list *list_reverse(struct list *list); +// struct list *list_reverse(struct list *list); // END PROTO list_reverse /* @@ -98,7 +104,7 @@ struct list *list_reverse(struct list *list); ** Return `NULL` if `list` is `NULL` or `index` is invalid. */ // START PROTO list_split -struct list *list_split(struct list *list, size_t index); +// struct list *list_split(struct list *list, size_t index); // END PROTO list_split #endif /* ! LIST_H */ diff --git a/minimake/src/main.c b/minimake/src/main.c index 4ace197..9ab0ada 100644 --- a/minimake/src/main.c +++ b/minimake/src/main.c @@ -40,7 +40,7 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, // Treat as rules for (int j = i; j < argc; j++) { - files = list_append(rules, argv[i]); + rules = list_append(rules, argv[i]); } *minimake_files = files; *minimake_rules = rules; @@ -50,6 +50,8 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, else if (strcmp(argv[i], "-h") == 0) { print_help(argv[0]); + list_destroy(files); + list_destroy(rules); exit(0); } // Custom file @@ -59,7 +61,7 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, if (i + 1 == argc || argv[i + 1][0] == '-') errx(INVALID_ARG, "No file specified after '-f'"); - files = list_prepend(files, argv[i]); + files = list_prepend(files, argv[i + 1]); } // Print else if (strcmp(argv[i], "-p") == 0) @@ -71,12 +73,14 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, { printf("Unknown option '%s'", argv[i]); print_help(argv[0]); + list_destroy(files); + list_destroy(rules); exit(INVALID_ARG); } } else // Rules { - files = list_append(rules, argv[i]); + rules = list_append(rules, argv[i]); } } @@ -112,7 +116,6 @@ char *get_makefile(struct list *files) if (found) res = file->data; - list_destroy(files); return res; } @@ -127,4 +130,7 @@ int main(int argc, char **argv) errx(GENERIC_ERR, "No Makefile found"); make(filename, flags, argv[0]); + list_destroy(files); + list_destroy(rules); + return 0; } diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index fa8697b..8bfee8e 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -27,22 +27,6 @@ struct list *rules_list = NULL; // ==== Misc ==== -#define FLAGS_FREE_RULES 1 -#define FLAGS_FREE_VARIABLES 2 - -static void list_deep_destroy(struct list *l) -{ - struct list *elt = l; - struct list *next_elt; - while (elt != NULL) - { - next_elt = elt->next; - free(elt->data); - free(elt); - elt = next_elt; - } -} - static void hashmap_deep_free(struct hash_map *hash_map) { if (hash_map == NULL) @@ -59,20 +43,15 @@ static void hashmap_deep_free(struct hash_map *hash_map) // Ok c moche mais eh, ça fonctionne if (hash_map == rules) { - // printf("DEBUG: %s\n", entry->key); struct rule *r = entry->value; free(r->name); list_deep_destroy(r->dependencies); list_deep_destroy(r->recipe); free(r); - // free(entry->key); } else if (hash_map == variables) { - // struct variable *v = entry->value; - // free(v->name); - // free(v->value); - + // printf("Att to free: %s\n", entry->key); free(entry->key); free(entry->value); } @@ -91,7 +70,7 @@ static void hashmap_deep_free(struct hash_map *hash_map) free(hash_map); } -static void free_all() +static void free_all(void) { list_destroy(rules_list); list_destroy(variables_list); @@ -99,78 +78,6 @@ static void free_all() hashmap_deep_free(variables); } -static void exit_on_error(int status, char *format, ...) -{ - // Print - va_list args; - va_start(args, format); - fprintf(stderr, "%s: ", program_name); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); - - free_all(); - exit(status); -} - -static void dump_database() -{ - struct list *elt; - - // Dump variables - elt = variables_list; - puts("# variables"); - while (elt != NULL) - { - // Get var - char *val = hashmap_get(variables, elt->data); - if (val == NULL) - exit_on_error(GENERIC_ERR, - "Could not get variable '%s' in database", elt->data); - - // Print - char *key = elt->data; - printf("%s = %s\n", key, val); - - elt = elt->next; - } - // Dump rules - elt = rules_list; - puts("# rules"); - while (elt != NULL) - { - // Get rule - struct rule *rule = hashmap_get(rules, elt->data); - if (rule == NULL) - exit_on_error(GENERIC_ERR, "Could not get rule '%s' in database", - elt->data); - - // Print name - printf("(%s) :", rule->name); - - // Print dependencies - struct list *dep = rule->dependencies; - while (dep != NULL) - { - char *dep_str = dep->data; - printf(" [%s]", dep_str); - dep = dep->next; - } - putchar('\n'); - - // Print recipe - struct list *rcp = rule->recipe; - while (rcp != NULL) - { - char *rcp_str = rcp->data; - printf("\t'%s'\n", rcp_str); - rcp = rcp->next; - } - - elt = elt->next; - } -} - // ==== Parsing ==== // Registers a new rule in the hashmap @@ -187,9 +94,8 @@ static void register_rule(char *name, struct list *dependencies, int err = hashmap_insert(rules, name, rule, NULL); if (!err) { - exit_on_error( - GENERIC_ERR, - "Internal Error: Couln't add entry for '%s' in the hashmap", name); + drop(GENERIC_ERR, + "Internal Error: Couln't add entry for '%s' in the hashmap", name); } rules_list = list_append(rules_list, name); @@ -203,9 +109,8 @@ static void register_variable(char *name, char *value) int err = hashmap_insert(variables, name, value, NULL); if (!err) { - exit_on_error( - GENERIC_ERR, - "Internal Error: Couln't add entry for '%s' in the hashmap", name); + drop(GENERIC_ERR, + "Internal Error: Couln't add entry for '%s' in the hashmap", name); } variables_list = list_append(variables_list, name); @@ -238,10 +143,9 @@ static struct list *read_deps(struct line *l, size_t offset) if (!ischar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0' && buf[i] != '\n') { - exit_on_error( - GENERIC_ERR, - "Unexpected character '%c' after rule declaration at %lu:%lu", - buf[i], l->number, i); + drop(GENERIC_ERR, + "Unexpected character '%c' after rule declaration at %lu:%lu", + buf[i], l->number, i); } } @@ -274,16 +178,17 @@ static struct list *read_recipe(struct line *l) } else // Add recipe to list { - char *command = strdup(buf + 1); + size_t offset = 1 + skipblanks(buf + 1, l->length - 1); + + char *command = strdup(buf + offset); if (command == NULL) { - exit_on_error( - GENERIC_ERR, - "Internal error: couldn't duplicate string (%lu:1)", - l->number); + drop(GENERIC_ERR, + "Internal error: couldn't duplicate string (%lu:1)", + l->number); } - command[l->length - 2] = '\0'; + command[l->length - offset - 1] = '\0'; res = list_append(res, command); } } @@ -292,8 +197,10 @@ static struct list *read_recipe(struct line *l) return res; } +// === Variable Expansion === + // Reads the value after a variable declaration -static char *read_value(char *buf, size_t buf_len) +static char *read_variable_value(char *buf, size_t buf_len) { size_t i = 0; size_t str_buf_size = STRING_BUFFER_SIZE; @@ -307,7 +214,7 @@ static char *read_value(char *buf, size_t buf_len) str_buf_size += STRING_BUFFER_SIZE; str_buf = realloc(str_buf, str_buf_size); if (str_buf == NULL) - exit_on_error(GENERIC_ERR, "Could not realloc"); + drop(GENERIC_ERR, "Could not realloc"); } str_buf[i] = buf[i]; @@ -319,104 +226,123 @@ static char *read_value(char *buf, size_t buf_len) return str_buf; } +// Reads variable name from buf until ')' and stores it into *result +// Returns its length on success and -1 on fail +// WARNING allocates memory on the heap, free *result after use +static size_t read_variable_name(struct line *line, size_t index, char **result) +{ + size_t i = index; + char *buf = line->buffer; + + // Alloc var_buf + size_t var_buf_size = STRING_BUFFER_SIZE; + char *var_buf = malloc(sizeof(char) * var_buf_size); + if (var_buf == NULL) + drop(GENERIC_ERR, "Could not allocate more memory"); + + size_t var_i = 0; + while (buf[i] != '\n' && buf[i] != '\0' && buf[i] != '#' && buf[i] != ')') + { + // Reallocate more space if necessary + var_buf_size = resize_buf(&var_buf, var_buf_size, i); + + // Copy + var_buf[var_i] = buf[i]; + i++; + var_i++; + } + var_buf[var_i] = '\0'; + + // Mismatched parenthesis + if (buf[i] != ')') + { + free(var_buf); + *result = NULL; + return 0; + } + + i += 2; + *result = var_buf; + return i - index; +} + +// Searches for variable in database and returns its value +// if no value is found, it drops an error and exit the program +// WARNING variable must be allocated on the heap as this function may attempt +// to free it +// NOTE Takes line_number to print an helpful error message +static char *get_variable_value(char *variable_name, size_t line_number) +{ + char *value = hashmap_get(variables, variable_name); + if (value == NULL) + { + // Adds tmp_buf to the list of items to free + variables_list = list_append(variables_list, variable_name); + drop(GENERIC_ERR, "Could not find specified variable '%s' at line %lu", + variable_name, line_number); + } + + free(variable_name); + return value; +} + // Gets the corresponding value of the variable and stores it in *value // Returns the number of skipped characters -// static size_t expand_variable(char *buf, size_t line_number, char **value) -// { -// size_t i = 0; -// if (buf[i] == '(') -// { -// // Read Value +size_t expand_variable(struct line *line, size_t index, char **value) +{ + size_t i = index; + char *buf = line->buffer; -// size_t tmp_buf_size = STRING_BUFFER_SIZE; -// char *tmp_buf = malloc(sizeof(char) * tmp_buf_size); -// // TODO free on err + // Name in parenthesis + if (buf[i] == '(') + { + // Read variable + char *var_name; + i++; + i += read_variable_name(line, i, &var_name); + if (var_name == NULL) + drop(GENERIC_ERR, "Mismatched parenthesis at %lu:%lu", i, + line->number); -// while (buf[i] != '\n' && buf[i] != '\0' && buf[i] != '#' -// && buf[i] != ')') -// { -// // Reallocate more space if necessary -// if (i >= tmp_buf_size - 1) -// { -// tmp_buf_size += STRING_BUFFER_SIZE; -// tmp_buf = realloc(tmp_buf, tmp_buf_size); -// if (tmp_buf == NULL) -// exit_on_error(GENERIC_ERR, "Could not realloc"); -// } + // Get corresponding value + *value = get_variable_value(var_name, line->number); -// // Copy -// tmp_buf[i] = buf[i]; -// i++; -// } + // Return + return i - index; + } + else // Single char variable + { + // '$ ' + if (isblank(buf[i]) || isspace(buf[i])) + drop(2, "Unauthorized character '%c' at %lu:%lu", buf[i], + line->number, i); + else + { + // Allocate temporary buffer to hold variable name + char *var_name = malloc(2 * sizeof(char)); + if (var_name == NULL) + // Seriously, not even two bytes + drop(GENERIC_ERR, "Could not allocate memory"); -// // End -// tmp_buf[i] = '\0'; + var_name[0] = buf[i]; + var_name[1] = '\0'; -// // Check for mismatched parenthesis -// if (buf[i] == ')') -// { -// // Get corresponding value -// char *val = hashmap_get(variables, tmp_buf); -// if (val == NULL) -// { -// // Adds tmp_buf to the list of items to free -// variables_list = list_append(variables_list, tmp_buf); -// exit_on_error(GENERIC_ERR, -// "Could not find specified variable '%s' at -// line", tmp_buf, line_number); -// } + // Escape '$$' + if (buf[i] == '$') + { + *value = var_name; + return 2; + } -// free(tmp_buf); -// *value = val; -// return i + 1; -// } -// else -// { -// free(tmp_buf); -// exit_on_error(GENERIC_ERR, "Mismatched parenthesis"); -// } -// } -// else -// { -// if (isblank(buf[i]) || isspace(buf[i])) -// { -// exit_on_error(GENERIC_ERR, -// "Special character '$' cannot be used alone"); -// } -// else if (buf[i] == '$') -// { -// *value = "$"; -// return 2; -// } -// else -// { -// // Get corresponding value -// char *tmp_buf = malloc(2 * sizeof(char)); -// if (tmp_buf == NULL) -// exit_on_error(GENERIC_ERR, -// "Could not allocate memory (seriously, not even -// " "two bytes)"); -// tmp_buf[0] = buf[i]; -// tmp_buf[1] = '\0'; -// char *val = hashmap_get(variables, tmp_buf); -// if (val == NULL) -// { -// // Adds tmp_buf to the list of items to free -// variables_list = list_append(variables_list, tmp_buf); -// exit_on_error(GENERIC_ERR, -// "Could not find specified variable '%s' at -// line", tmp_buf, line_number); -// } + // Get corresponding value + *value = get_variable_value(var_name, line->number); + } -// free(tmp_buf); -// *value = val; -// return 2; -// } + return 2; + } +} -// return 1; // Discard warnings -// } -// return 1; // Discard warnings -// } +// ================= // Takes a buffer containing the line to parse and it length // As well as the line number in the file for error handling @@ -431,7 +357,8 @@ static void parse_line(struct line *current_line) // Read name char *name; - i += readword(buf + i, line_size - i, &name); + // i += readword(buf + i, line_size - i, &name); + i += read_declaration(current_line, i, &name); // if (name == NULL) // errx(1, "Il s'est passé quoi là ? \nUnexpected character at %lu:%lu", // current_line->number, i); @@ -449,7 +376,7 @@ static void parse_line(struct line *current_line) // Variable case '=': i += skipblanks(buf + i + 1, line_size - i) + 1; - value = read_value(buf + i, line_size - i); + value = read_variable_value(buf + i, line_size - i); register_variable(name, value); break; @@ -473,18 +400,16 @@ static void parse_line(struct line *current_line) case '#': if (name != NULL) { - exit_on_error( - GENERIC_ERR, - "Unexpected character '%c' after declaration '%s' at line %lu", - buf[i], name, current_line->number); + drop(GENERIC_ERR, + "Unexpected character '%c' after declaration '%s' at line %lu", + buf[i], name, current_line->number); } break; default: - exit_on_error( - GENERIC_ERR, - "Unexpected character '%c' after declaration '%s' at line %lu", - buf[i], name, current_line->number); + drop(GENERIC_ERR, + "Unexpected character '%c' after declaration '%s' at line %lu", + buf[i], name, current_line->number); } // free(name); @@ -507,7 +432,7 @@ void make_parse(char *path) size_t buf_size = BUFFER_SIZE; char *buf = malloc(sizeof(char) * buf_size); if (buf == NULL) - exit_on_error(GENERIC_ERR, "Could not allocate more memory"); + drop(GENERIC_ERR, "Could not allocate more memory"); // Parse line by line ssize_t nread; @@ -517,8 +442,7 @@ void make_parse(char *path) while ((nread = getline(&buf, &buf_size, stream)) != -1) { if (nread == -1) - exit_on_error(GENERIC_ERR, "Could not get line %lu", - current_line.number); + drop(GENERIC_ERR, "Could not get line %lu", current_line.number); current_line.buffer = buf; current_line.length = nread; @@ -535,6 +459,9 @@ void make_parse(char *path) // ==== Runtime ==== +// static void make_run(void) +// {} + // ==== MAKE ==== void make(char *path, int flags, char *argv0) @@ -544,8 +471,79 @@ void make(char *path, int flags, char *argv0) make_parse(path); if (flags & FLAGS_PRINT) - // errx(GENERIC_ERR, "Not Implemented"); dump_database(); free_all(); } + +// Prints an error message before exiting gracefully (by freeing all variables) +void drop(int status, char *format, ...) +{ + // Print + va_list args; + va_start(args, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); + + free_all(); + exit(status); +} + +void dump_database(void) +{ + struct list *elt; + + // Dump variables + elt = variables_list; + puts("# variables"); + while (elt != NULL) + { + // Get var + char *val = hashmap_get(variables, elt->data); + if (val == NULL) + drop(GENERIC_ERR, "Could not get variable '%s' in database", + elt->data); + + // Print + char *key = elt->data; + printf("%s = %s\n", key, val); + + elt = elt->next; + } + // Dump rules + elt = rules_list; + puts("# rules"); + while (elt != NULL) + { + // Get rule + struct rule *rule = hashmap_get(rules, elt->data); + if (rule == NULL) + drop(GENERIC_ERR, "Could not get rule '%s' in database", elt->data); + + // Print name + printf("(%s) :", rule->name); + + // Print dependencies + struct list *dep = rule->dependencies; + while (dep != NULL) + { + char *dep_str = dep->data; + printf(" [%s]", dep_str); + dep = dep->next; + } + putchar('\n'); + + // Print recipe + struct list *rcp = rule->recipe; + while (rcp != NULL) + { + char *rcp_str = rcp->data; + printf("\t'%s'\n", rcp_str); + rcp = rcp->next; + } + + elt = elt->next; + } +} diff --git a/minimake/src/minimake.h b/minimake/src/minimake.h index ccd5f08..98f4ccb 100644 --- a/minimake/src/minimake.h +++ b/minimake/src/minimake.h @@ -19,6 +19,7 @@ #include #include "lists/lists.h" +#include "lines/lines.h" // Holds variable information // WARNING its values must be freed after use @@ -39,5 +40,8 @@ struct rule void make(char *path, int flags, char* program_name); void make_parse(char *path); +void drop(int status, char *format, ...); +void dump_database(void); +size_t expand_variable(struct line *line, size_t index, char **value); #endif // ! MINIMAKE_H diff --git a/minimake/tests/Makefile2.syntax-test b/minimake/tests/Makefile2.syntax-test new file mode 100644 index 0000000..ac3c606 --- /dev/null +++ b/minimake/tests/Makefile2.syntax-test @@ -0,0 +1,36 @@ +SIMPLE_VAR = coucou +SIMPLE_VAR_COMMENT = the comment is gone # comment +$(SIMPLE_VAR) = 1 + +# 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)" From a8ca407247dac93024c549aac7fab333cb265d72 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 31 Oct 2025 21:34:09 +0100 Subject: [PATCH 02/10] JUSTOCASOU --- minimake/Makefile | 2 +- minimake/src/files/files.c | 99 +++++++++++++++++++++++++++++++++++ minimake/src/files/files.h | 14 +++++ minimake/src/main.c | 38 +++----------- minimake/src/minimake.c | 95 ++++++++++++++++++++++++++++++--- minimake/src/minimake.h | 7 +-- minimake/tests/Makefile.test3 | 1 + minimake/tests/Makefile.test4 | 5 ++ 8 files changed, 220 insertions(+), 41 deletions(-) create mode 100644 minimake/src/files/files.c create mode 100644 minimake/src/files/files.h create mode 100644 minimake/tests/Makefile.test3 create mode 100644 minimake/tests/Makefile.test4 diff --git a/minimake/Makefile b/minimake/Makefile index c12a97e..db29234 100644 --- a/minimake/Makefile +++ b/minimake/Makefile @@ -7,7 +7,7 @@ DBG_LDFLAGS= -fsanitize=address SRC_DIR = src -LIB_SRCS = lines/lines.c hash_maps/hash_maps.c lists/lists.c +LIB_SRCS = lines/lines.c hash_maps/hash_maps.c lists/lists.c files/files.c MAIN_SRCS = main.c minimake.c # SRCS = $(patsubst %,$(SRC_DIR)/%, $(MAIN_SRCS)) diff --git a/minimake/src/files/files.c b/minimake/src/files/files.c new file mode 100644 index 0000000..2d46ecb --- /dev/null +++ b/minimake/src/files/files.c @@ -0,0 +1,99 @@ +#include "files.h" + +#include +#include +#include +#include +#include +#include + +// #include "../lines/lines.h" + +char *get_makefile(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) + { + fclose(test); + found = 1; + } + else + { + file = file->next; + } + } + + char *res = NULL; + if (found) + res = file->data; + + return res; +} + +ssize_t get_file_lastmodiftime(char *filename) +{ + struct stat stat; + if (lstat(filename, &stat) == -1) + return -1; + return stat.st_mtime; +} + +// Calls a shell that will run the given command +// It will first print the command to run unless the it starts with '@' +// Returns the return code o the command or -1 if a syscall fails +int run_command(char *command) +{ + if (command[0] == '@') + command++; + else + { + puts(command); + } + + // Just in case + fflush(stdout); + fflush(stderr); + + // char *executable; + // size_t args_offset = readword(command, strlen(command), &executable); + + int id = fork(); + if (id < 0) + { + perror("fork"); + return -1; + } + + if (id == 0) + { + int res = 0; + wait(&res); + // printf("Got return code: %d\n", res); + return res; + } + else + { + int res = execl("/bin/sh", "(make)", "-c", command, NULL); + return res; + } +} + +// Returns 1 if file exists (and is readable), 0 otherwise +int file_exists(char *filename) +{ + if (filename == NULL) + return 0; + + FILE *test = fopen(filename, "r"); + if (test == NULL) + return 0; + + fclose(test); + return 1; +} diff --git a/minimake/src/files/files.h b/minimake/src/files/files.h new file mode 100644 index 0000000..da7c555 --- /dev/null +++ b/minimake/src/files/files.h @@ -0,0 +1,14 @@ +#ifndef FILES_H +#define FILES_H + +#define _XOPEN_SOURCE 500 + +#include "../lists/lists.h" +#include + +char* get_makefile(struct list *files); +ssize_t get_file_lastmodiftime(char* filename); +int run_command(char *command); +int file_exists(char *filename); + +#endif // FILES_H diff --git a/minimake/src/main.c b/minimake/src/main.c index 9ab0ada..ec0473f 100644 --- a/minimake/src/main.c +++ b/minimake/src/main.c @@ -5,6 +5,7 @@ #include #include +#include "files/files.h" #include "lists/lists.h" #include "minimake.h" @@ -58,10 +59,12 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, else if (strcmp(argv[i], "-f") == 0) { flags |= FLAGS_CUSTOM_FILE; - if (i + 1 == argc || argv[i + 1][0] == '-') + i++; + + if (i == argc || argv[i][0] == '-') errx(INVALID_ARG, "No file specified after '-f'"); - files = list_prepend(files, argv[i + 1]); + files = list_prepend(files, argv[i]); } // Print else if (strcmp(argv[i], "-p") == 0) @@ -92,33 +95,6 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, return flags; } -char *get_makefile(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) - { - fclose(test); - found = 1; - } - else - { - file = file->next; - } - } - - char *res = NULL; - if (found) - res = file->data; - - return res; -} - int main(int argc, char **argv) { struct list *files; @@ -129,8 +105,8 @@ int main(int argc, char **argv) if (filename == NULL) errx(GENERIC_ERR, "No Makefile found"); - make(filename, flags, argv[0]); + int res = make(filename, rules, argv[0], flags); list_destroy(files); list_destroy(rules); - return 0; + return res; } diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index 8bfee8e..675702c 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -10,6 +10,7 @@ #include #include +#include "files/files.h" #include "hash_maps/hash_maps.h" #include "lines/lines.h" #include "lists/lists.h" @@ -459,21 +460,103 @@ void make_parse(char *path) // ==== Runtime ==== -// static void make_run(void) -// {} +// Expands and run a specified rule +static int run_rule(struct rule *rule) +{ + struct list *commands = rule->recipe; + struct list *dependencies = rule->dependencies; + + // Expand dependencies variables + // TODO + + // Build dependencies + while (dependencies != NULL) + { + // Check file existence + if (!file_exists(dependencies->data)) + { + // Check rule existence instead + struct rule *dep_rule = hashmap_get(rules, dependencies->data); + if (dep_rule != NULL) + { + int res = run_rule(dep_rule); + if (res != 0) // Exit on error + return res; + } + else + { + drop(GENERIC_ERR, "No rule to make target '%s'", + dependencies->data); + } + } + } + + while (commands != NULL) + { + // Expand command variables + // TODO + + // Run + int res = run_command(commands->data); + if (res != 0) // Exit on error + return res; + + commands = commands->next; + } + + return 0; +} + +// Run the given rules after parsing +int make_run(struct list *given_rules) +{ + if (given_rules == NULL) + { + // No rule specified => run first rule found in the Makefile + if (rules_list == NULL) + drop(GENERIC_ERR, "No targets"); + + struct rule *full_rule = hashmap_get(rules, rules_list->data); + if (full_rule == NULL) + drop(GENERIC_ERR, + "Internal error: Could not retrieve target '%s' in database", + rules_list->data); + + return run_rule(full_rule); + } + else + { + while (given_rules != NULL) + { + struct rule *full_rule = hashmap_get(rules, given_rules->data); + if (full_rule == NULL) + drop(GENERIC_ERR, "No rule to make target '%s'", + given_rules->data); + + int res = run_rule(full_rule); + if (res != 0) + return res; + + given_rules = given_rules->next; + } + return 0; + } +} // ==== MAKE ==== -void make(char *path, int flags, char *argv0) +int make(char *path, struct list *rules, char *exec_name, int flags) { - program_name = argv0; + program_name = exec_name; make_parse(path); if (flags & FLAGS_PRINT) dump_database(); + int status = make_run(rules); free_all(); + return status; } // Prints an error message before exiting gracefully (by freeing all variables) @@ -482,9 +565,9 @@ void drop(int status, char *format, ...) // Print va_list args; va_start(args, format); - fprintf(stderr, "%s: ", program_name); + fprintf(stderr, "%s: *** ", program_name); vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + fprintf(stderr, ". Stop.\n"); va_end(args); free_all(); diff --git a/minimake/src/minimake.h b/minimake/src/minimake.h index 98f4ccb..f6ed073 100644 --- a/minimake/src/minimake.h +++ b/minimake/src/minimake.h @@ -10,8 +10,8 @@ #define FLAGS_PRINT 2 // Default values -#define DEFAULT_MAKEFILE "Makefile" -#define DEFAULT_MAKEFILE_2 "makefile" +#define DEFAULT_MAKEFILE "makefile" +#define DEFAULT_MAKEFILE_2 "Makefile" #define BUFFER_SIZE 1024 #define HASHMAP_SIZE 32 @@ -38,8 +38,9 @@ struct rule struct list *recipe; }; -void make(char *path, int flags, char* program_name); +int make(char *path, struct list* rules, char *argv0, int flags); void make_parse(char *path); +int make_run(struct list *rules); void drop(int status, char *format, ...); void dump_database(void); size_t expand_variable(struct line *line, size_t index, char **value); diff --git a/minimake/tests/Makefile.test3 b/minimake/tests/Makefile.test3 new file mode 100644 index 0000000..7a42836 --- /dev/null +++ b/minimake/tests/Makefile.test3 @@ -0,0 +1 @@ +VAR = 1 diff --git a/minimake/tests/Makefile.test4 b/minimake/tests/Makefile.test4 new file mode 100644 index 0000000..45ee423 --- /dev/null +++ b/minimake/tests/Makefile.test4 @@ -0,0 +1,5 @@ +rule1: + echo Toto + +rule2: + @echo Toto From 1efc35a59ec7c8617378e5765c61e7c831d8c640 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 31 Oct 2025 22:31:08 +0100 Subject: [PATCH 03/10] JUSTOCASOUUUU --- minimake/Makefile | 8 +++--- minimake/src/files/files.h | 7 ++--- minimake/src/hash_maps/hash_maps.h | 2 +- minimake/src/lines/lines.c | 42 ++++++++++++++++++++++++++++++ minimake/src/lines/lines.h | 12 ++++----- minimake/src/lists/.gitignore | 1 - minimake/src/lists/lists.h | 16 ++++++------ minimake/src/minimake.c | 5 ++-- minimake/src/minimake.h | 6 ++--- 9 files changed, 70 insertions(+), 29 deletions(-) delete mode 100644 minimake/src/lists/.gitignore diff --git a/minimake/Makefile b/minimake/Makefile index db29234..cb0ae0a 100644 --- a/minimake/Makefile +++ b/minimake/Makefile @@ -21,10 +21,10 @@ $(TARGET): $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) @echo $(OBJS) -debug: CFLAGS += $(DBG_CFLAGS) -debug: LDFLAGS += $(DBG_LDFLAGS) -debug: $(OBJS) - $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) +# debug: CFLAGS += $(DBG_CFLAGS) +# debug: LDFLAGS += $(DBG_LDFLAGS) +# debug: $(OBJS) +# $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) check: dash ./tests/run.sh diff --git a/minimake/src/files/files.h b/minimake/src/files/files.h index da7c555..d4893c3 100644 --- a/minimake/src/files/files.h +++ b/minimake/src/files/files.h @@ -3,11 +3,12 @@ #define _XOPEN_SOURCE 500 -#include "../lists/lists.h" #include -char* get_makefile(struct list *files); -ssize_t get_file_lastmodiftime(char* filename); +#include "../lists/lists.h" + +char *get_makefile(struct list *files); +ssize_t get_file_lastmodiftime(char *filename); int run_command(char *command); int file_exists(char *filename); diff --git a/minimake/src/hash_maps/hash_maps.h b/minimake/src/hash_maps/hash_maps.h index 97a84da..634c993 100644 --- a/minimake/src/hash_maps/hash_maps.h +++ b/minimake/src/hash_maps/hash_maps.h @@ -24,7 +24,7 @@ struct hash_map size_t hash(const char *str); struct hash_map *hashmap_init(size_t size); bool hashmap_insert(struct hash_map *hash_map, char *key, void *value, - bool *updated); + bool *updated); void hashmap_free(struct hash_map *hash_map); void hashmap_dump(struct hash_map *hash_map); void *hashmap_get(const struct hash_map *hash_map, char *key); diff --git a/minimake/src/lines/lines.c b/minimake/src/lines/lines.c index fcd33e1..238b576 100644 --- a/minimake/src/lines/lines.c +++ b/minimake/src/lines/lines.c @@ -219,3 +219,45 @@ size_t read_declaration(struct line *line, size_t index, char **word) *word = res; return i - index; } + +// Takes a string, expands its variables and returns the resulting buffer +// WARNING allocates the result on the heap, free result after use +char *expand_str(char *str) +{ + size_t str_i = 0; + size_t buf_i = 0; + size_t buf_size = BUFFER_SIZE; + char *buf = malloc(BUFFER_SIZE * sizeof(char)); + while (str[str_i] != '\0') + { + // Reallocate more space if necessary + resize_buf(&buf, buf_size, buf_i); + + // Expand variable + if (buf[buf_i] == '$') + { + // Temporary variables + char *expanded_var; + char *tmp_res_buf; + struct line false_line = { + .buffer = buf, .file_stream = NULL, .length = 0, .number = 0 + }; + str_i += expand_variable(&false_line, str_i + 1, &expanded_var); + buf_size = append_str(buf, expanded_var, buf_i, &tmp_res_buf); + // Replace buf with buf + expanded_variable + buf_i = strlen(tmp_res_buf); + free(buf); + // free(expanded_var); + buf = tmp_res_buf; + continue; + } + + buf[buf_i] = str[str_i]; + buf_i++; + str_i++; + } + + buf[buf_i] = '\0'; + + return buf; +} diff --git a/minimake/src/lines/lines.h b/minimake/src/lines/lines.h index 081d305..5782327 100644 --- a/minimake/src/lines/lines.h +++ b/minimake/src/lines/lines.h @@ -4,25 +4,25 @@ #define STRING_BUFFER_SIZE 32 #include -#include #include +#include // Holds line information // Exists only because of EPITA's annoying 4 parameters limit // WARNING buffer must be freed after use struct line { - char* buffer; // Line content - ssize_t length; // Line length - size_t number; // Line number in file - FILE* file_stream; // Full file stream + char *buffer; // Line content + ssize_t length; // Line length + size_t number; // Line number in file + FILE *file_stream; // Full file stream }; int ischar(char c); int isblankline(struct line *l); int skipblanks(char *buf, size_t buf_len); size_t readword(char *buf, size_t buf_len, char **word); -size_t resize_buf(char** buf, size_t buf_size, size_t index); +size_t resize_buf(char **buf, size_t buf_size, size_t index); char *insert_str(char *base, char *str, size_t index); size_t read_declaration(struct line *line, size_t index, char **word); size_t append_str(char *base, char *str, size_t index, char **res); diff --git a/minimake/src/lists/.gitignore b/minimake/src/lists/.gitignore deleted file mode 100644 index 3309b20..0000000 --- a/minimake/src/lists/.gitignore +++ /dev/null @@ -1 +0,0 @@ -list.c diff --git a/minimake/src/lists/lists.h b/minimake/src/lists/lists.h index 8c4114f..116bd1f 100644 --- a/minimake/src/lists/lists.h +++ b/minimake/src/lists/lists.h @@ -1,11 +1,11 @@ -#ifndef LIST_H -#define LIST_H +#ifndef LISTS_H +#define LISTS_H #include struct list { - void* data; + void *data; struct list *next; }; @@ -13,7 +13,7 @@ struct list ** Insert a node containing `value` at the beginning of the list. ** Return `NULL` if an error occured. */ -struct list *list_prepend(struct list *list, void* value); +struct list *list_prepend(struct list *list, void *value); /* ** Return the lenght of the list. @@ -44,7 +44,7 @@ void list_deep_destroy(struct list *l); ** Return `NULL` if an error occured. */ // START PROTO list_append -struct list *list_append(struct list *list, void* value); +struct list *list_append(struct list *list, void *value); // END PROTO list_append /* @@ -54,7 +54,7 @@ struct list *list_append(struct list *list, void* value); ** Return `NULL` if an error occured. */ // START PROTO list_insert -struct list *list_insert(struct list *list, void* value, size_t index); +struct list *list_insert(struct list *list, void *value, size_t index); // END PROTO list_insert /* @@ -70,7 +70,7 @@ struct list *list_remove(struct list *list, size_t index); ** Return `-1` if nothing is found. */ // START PROTO list_find -int list_find(struct list *list, void* value); +int list_find(struct list *list, void *value); // END PROTO list_find /* @@ -107,4 +107,4 @@ int list_find(struct list *list, void* value); // struct list *list_split(struct list *list, size_t index); // END PROTO list_split -#endif /* ! LIST_H */ +#endif /* ! LISTS_H */ diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index 675702c..c28aed3 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -466,12 +466,11 @@ static int run_rule(struct rule *rule) struct list *commands = rule->recipe; struct list *dependencies = rule->dependencies; - // Expand dependencies variables - // TODO - // Build dependencies while (dependencies != NULL) { + // Expand variables + // Check file existence if (!file_exists(dependencies->data)) { diff --git a/minimake/src/minimake.h b/minimake/src/minimake.h index f6ed073..7bb5f39 100644 --- a/minimake/src/minimake.h +++ b/minimake/src/minimake.h @@ -18,8 +18,8 @@ #include #include -#include "lists/lists.h" #include "lines/lines.h" +#include "lists/lists.h" // Holds variable information // WARNING its values must be freed after use @@ -33,12 +33,12 @@ struct variable // WARNING its values must be freed after use struct rule { - char* name; + char *name; struct list *dependencies; struct list *recipe; }; -int make(char *path, struct list* rules, char *argv0, int flags); +int make(char *path, struct list *rules, char *argv0, int flags); void make_parse(char *path); int make_run(struct list *rules); void drop(int status, char *format, ...); From 4b3df88e8a3268c2bb20329639d1031363952f86 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 31 Oct 2025 22:48:30 +0100 Subject: [PATCH 04/10] fixed print --- minimake/src/minimake.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index c28aed3..468ce23 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -488,6 +488,8 @@ static int run_rule(struct rule *rule) dependencies->data); } } + + dependencies = dependencies->next; } while (commands != NULL) @@ -550,9 +552,15 @@ int make(char *path, struct list *rules, char *exec_name, int flags) make_parse(path); + // Print if (flags & FLAGS_PRINT) + { dump_database(); + free_all(); + return 0; + } + // Run int status = make_run(rules); free_all(); return status; @@ -590,7 +598,7 @@ void dump_database(void) // Print char *key = elt->data; - printf("%s = %s\n", key, val); + printf("'%s' = '%s'\n", key, val); elt = elt->next; } @@ -605,7 +613,7 @@ void dump_database(void) drop(GENERIC_ERR, "Could not get rule '%s' in database", elt->data); // Print name - printf("(%s) :", rule->name); + printf("(%s):", rule->name); // Print dependencies struct list *dep = rule->dependencies; From db4bf470ffa31d61bca5f8986a69d277143aea74 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 31 Oct 2025 23:12:01 +0100 Subject: [PATCH 05/10] even more fixes --- minimake/Makefile | 8 ++++---- minimake/src/files/files.c | 12 ++++-------- minimake/tests/Makefile.test4 | 4 +++- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/minimake/Makefile b/minimake/Makefile index cb0ae0a..db29234 100644 --- a/minimake/Makefile +++ b/minimake/Makefile @@ -21,10 +21,10 @@ $(TARGET): $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) @echo $(OBJS) -# debug: CFLAGS += $(DBG_CFLAGS) -# debug: LDFLAGS += $(DBG_LDFLAGS) -# debug: $(OBJS) -# $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) +debug: CFLAGS += $(DBG_CFLAGS) +debug: LDFLAGS += $(DBG_LDFLAGS) +debug: $(OBJS) + $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) check: dash ./tests/run.sh diff --git a/minimake/src/files/files.c b/minimake/src/files/files.c index 2d46ecb..ef00e46 100644 --- a/minimake/src/files/files.c +++ b/minimake/src/files/files.c @@ -60,9 +60,6 @@ int run_command(char *command) fflush(stdout); fflush(stderr); - // char *executable; - // size_t args_offset = readword(command, strlen(command), &executable); - int id = fork(); if (id < 0) { @@ -72,14 +69,13 @@ int run_command(char *command) if (id == 0) { - int res = 0; - wait(&res); - // printf("Got return code: %d\n", res); - return res; + int res = execl("/bin/sh", "(make)", "-c", command, NULL); + exit(res); } else { - int res = execl("/bin/sh", "(make)", "-c", command, NULL); + int res = 0; + wait(&res); return res; } } diff --git a/minimake/tests/Makefile.test4 b/minimake/tests/Makefile.test4 index 45ee423..4da97dd 100644 --- a/minimake/tests/Makefile.test4 +++ b/minimake/tests/Makefile.test4 @@ -1,5 +1,7 @@ rule1: echo Toto + echo Tata + echo Tutu rule2: - @echo Toto + @echo Toto encore From 7acbfd6c9fb554746a847184713666bdcc4b21ce Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 31 Oct 2025 23:14:34 +0100 Subject: [PATCH 06/10] hmmmm --- minimake/src/files/files.c | 1 - 1 file changed, 1 deletion(-) diff --git a/minimake/src/files/files.c b/minimake/src/files/files.c index ef00e46..b27627d 100644 --- a/minimake/src/files/files.c +++ b/minimake/src/files/files.c @@ -2,7 +2,6 @@ #include #include -#include #include #include #include From bb1591a96ae4ff91b2cd4e87458e57791189b8fc Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Thu, 18 Dec 2025 13:17:48 +0100 Subject: [PATCH 07/10] nothing to be done reload --- minimake/src/minimake.c | 10 +++++++++- minimake/tests/Makefile.empty | 1 + minimake/tests/Makefile.vars | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 minimake/tests/Makefile.empty create mode 100644 minimake/tests/Makefile.vars diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index 468ce23..e76e664 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -469,7 +469,8 @@ static int run_rule(struct rule *rule) // Build dependencies while (dependencies != NULL) { - // Expand variables + // Expand variable + // TODO // Check file existence if (!file_exists(dependencies->data)) @@ -492,6 +493,13 @@ static int run_rule(struct rule *rule) dependencies = dependencies->next; } + // Empty recipe + if (commands == NULL) + { + printf("%s: Nothing to be done for '%s'.\n", program_name, rule->name); + return 0; + } + while (commands != NULL) { // Expand command variables diff --git a/minimake/tests/Makefile.empty b/minimake/tests/Makefile.empty new file mode 100644 index 0000000..e9228f2 --- /dev/null +++ b/minimake/tests/Makefile.empty @@ -0,0 +1 @@ +empty_target: diff --git a/minimake/tests/Makefile.vars b/minimake/tests/Makefile.vars new file mode 100644 index 0000000..b5ce417 --- /dev/null +++ b/minimake/tests/Makefile.vars @@ -0,0 +1,4 @@ +V=2 + +main: + echo $V From 57c7743ba07eb45a566497789604b3502f9663ae Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 19 Dec 2025 20:12:03 +0100 Subject: [PATCH 08/10] si ca passe pas jsui cuit --- minimake/src/minimake.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index e76e664..ff913ab 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "files/files.h" @@ -337,6 +338,7 @@ size_t expand_variable(struct line *line, size_t index, char **value) // Get corresponding value *value = get_variable_value(var_name, line->number); + free(var_name); } return 2; @@ -460,9 +462,34 @@ void make_parse(char *path) // ==== Runtime ==== +// Return 1 if rule is up to date, 0 otherwise +static int uptodate(struct rule *rule) +{ + struct stat path_stat; + + if (lstat(rule->name, &path_stat) != 0) // target doesn't exists + return 0; + + struct list *dependencies = rule->dependencies; + while (dependencies != NULL) + { + char *depname = dependencies->data; + if (lstat(depname, &path_stat) != 0) // Dependecy doesn't exists + return 0; + } + return 1; +} + // Expands and run a specified rule static int run_rule(struct rule *rule) { + // Check if p to date + if (uptodate(rule)) + { + printf("%s: '%s' is up to date.", program_name, rule->name); + return 0; + } + struct list *commands = rule->recipe; struct list *dependencies = rule->dependencies; From 018bae577a9ba0abafe5d8176e55ed4fc1f523a9 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 19 Dec 2025 20:13:58 +0100 Subject: [PATCH 09/10] et on dis ciao aux clang format --- minimake/src/minimake.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index ff913ab..4368296 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -18,14 +18,14 @@ // Static variables -char *program_name; +static char *program_name; -struct hash_map *variables = NULL; -struct hash_map *rules = NULL; +static struct hash_map *variables = NULL; +static struct hash_map *rules = NULL; // Keeps track of variables and rules order -struct list *variables_list = NULL; -struct list *rules_list = NULL; +static struct list *variables_list = NULL; +static struct list *rules_list = NULL; // ==== Misc ==== From 9731ba9a7a6edb6adf287558157414924d6b63ef Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 19 Jun 2026 11:11:29 +0200 Subject: [PATCH 10/10] New presentation and organization --- minimake/Makefile => Makefile | 0 README.md | 73 +++++++++++ micromake/src/Microfile | 3 - micromake/src/micromake.c | 119 ------------------ microshell/microshell.c | 30 ----- simple_ls/simple_ls.c | 37 ------ {minimake/src => src}/files/files.c | 0 {minimake/src => src}/files/files.h | 0 {minimake/src => src}/hash_maps/hash_maps.c | 0 {minimake/src => src}/hash_maps/hash_maps.h | 0 {minimake/src => src}/lines/lines.c | 0 {minimake/src => src}/lines/lines.h | 0 {minimake/src => src}/lists/lists.c | 0 {minimake/src => src}/lists/lists.h | 0 {minimake/src => src}/main.c | 0 {minimake/src => src}/minimake.c | 0 {minimake/src => src}/minimake.h | 0 {minimake/tests => tests}/Makefile.empty | 0 .../tests => tests}/Makefile.syntax-test | 0 {minimake/tests => tests}/Makefile.test3 | 0 {minimake/tests => tests}/Makefile.test4 | 0 {minimake/tests => tests}/Makefile.vars | 0 .../tests => tests}/Makefile2.syntax-test | 0 {minimake/tests => tests}/run.sh | 0 24 files changed, 73 insertions(+), 189 deletions(-) rename minimake/Makefile => Makefile (100%) create mode 100644 README.md delete mode 100644 micromake/src/Microfile delete mode 100644 micromake/src/micromake.c delete mode 100644 microshell/microshell.c delete mode 100644 simple_ls/simple_ls.c rename {minimake/src => src}/files/files.c (100%) rename {minimake/src => src}/files/files.h (100%) rename {minimake/src => src}/hash_maps/hash_maps.c (100%) rename {minimake/src => src}/hash_maps/hash_maps.h (100%) rename {minimake/src => src}/lines/lines.c (100%) rename {minimake/src => src}/lines/lines.h (100%) rename {minimake/src => src}/lists/lists.c (100%) rename {minimake/src => src}/lists/lists.h (100%) rename {minimake/src => src}/main.c (100%) rename {minimake/src => src}/minimake.c (100%) rename {minimake/src => src}/minimake.h (100%) rename {minimake/tests => tests}/Makefile.empty (100%) rename {minimake/tests => tests}/Makefile.syntax-test (100%) rename {minimake/tests => tests}/Makefile.test3 (100%) rename {minimake/tests => tests}/Makefile.test4 (100%) rename {minimake/tests => tests}/Makefile.vars (100%) rename {minimake/tests => tests}/Makefile2.syntax-test (100%) rename {minimake/tests => tests}/run.sh (100%) diff --git a/minimake/Makefile b/Makefile similarity index 100% rename from minimake/Makefile rename to Makefile diff --git a/README.md b/README.md new file mode 100644 index 0000000..7cc185b --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# Minimake + +Minimake is a small project written in about a week in C99. Its goal was to reproduce the main features of the well known GNU Make utility. + +> **Note** This is a school project, therefore it probably won't interest you if you are looking for something useful. + +## Build + +```sh +make +``` +or even better +```sh +minimake +``` + +## How it works + +If you're not familiar with Make, what it does is that it reads a file named +`Makefile` in the current directory which contains instructions to build a +project in the form of recipes. It includes support for variables, dependencies +, implicit rules and more. + +Then depending on the user input it automatically executes a recipe with its dependencies. + + +Here is what a basic one can look like +```make +BENCH_FLAGS = --all + +test_and_bench: + bash ./runtests.sh + bash ./runbenchs.sh $(BENCH_FLAGS) +``` + +But it will more realisticly look like that + +```make +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 files/files.c +MAIN_SRCS = main.c minimake.c + +SRCS = $(MAIN_SRCS:%=$(SRC_DIR)/%) $(LIB_SRCS:%=$(SRC_DIR)/%) +OBJS = $(SRCS:.c=.o) + +TARGET= minimake +DBG_TARGET = minimake-dbg + +$(TARGET): $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) + @echo $(OBJS) + +debug: CFLAGS += $(DBG_CFLAGS) +debug: LDFLAGS += $(DBG_LDFLAGS) +debug: $(OBJS) + $(CC) -o $(DBG_TARGET) $^ $(LDFLAGS) $(LDLIBS) + +check: + dash ./tests/run.sh + +clean: + $(RM) $(TARGET) + $(RM) $(OBJS) + +``` diff --git a/micromake/src/Microfile b/micromake/src/Microfile deleted file mode 100644 index 607b02a..0000000 --- a/micromake/src/Microfile +++ /dev/null @@ -1,3 +0,0 @@ -qwertyu: -cbejw: a bc d -dmwq :d wdwd diff --git a/micromake/src/micromake.c b/micromake/src/micromake.c deleted file mode 100644 index fda3fe2..0000000 --- a/micromake/src/micromake.c +++ /dev/null @@ -1,119 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#define BUFFER_SIZE 1024 -#define STRING_BUFFER_SIZE 32 - -#include -#include -#include -#include - -// Helps to match a string (excludes blanks and special characters) -static int is_char(char c) -{ - return c != '\0' && !isblank(c) && c != ':' && c != '=' && c != '#'; -} - -// 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 -static char *readWord(char *buf, size_t buf_len, size_t *read_chars) -{ - size_t i = 0; - size_t str_buf_size = STRING_BUFFER_SIZE; - char *str_buf = malloc(sizeof(char) * str_buf_size); - while (i < buf_len && is_char(buf[i])) - { - // Reallocate more space if necessary - if (i >= str_buf_size - 1) - { - str_buf_size += STRING_BUFFER_SIZE; - str_buf = realloc(str_buf, str_buf_size); - if (str_buf == NULL) - errx(2, "Could not realloc"); - } - - str_buf[i] = buf[i]; - i++; - } - - str_buf[i] = '\0'; - *read_chars = i; - - return str_buf; -} - -int skip_blanks(char *buf, size_t buf_len) -{ - size_t i = 0; - while (i < buf_len && isblank(buf[i])) - i++; - return i; -} - -int main(int argc, char **argv) -{ - if (argc < 2) - errx(2, "Not enough arguments"); - if (argc > 2) - errx(2, "Not enough arguments"); - - // Open file - FILE *stream = fopen(argv[1], "r"); - if (stream == 0) - errx(2, "Could not open file"); - - // Allocate buffer - size_t buf_size = BUFFER_SIZE; - char *buf = malloc(sizeof(char) * buf_size); - if (buf == NULL) - errx(2, "Could not allocate more memory"); - - // Read - ssize_t nread; - while ((nread = getline(&buf, &buf_size, stream)) != -1) - { - size_t u_nread = nread; - size_t i = 0; - // Skip blanks - i += skip_blanks(buf + i, nread - i); - - // Read target name - size_t skipped_chars = 0; - char *rule_name = readWord(buf + i, nread - i, &skipped_chars); - if (skipped_chars != 0) - printf("%s:", rule_name); - i += skipped_chars; - - // Skip until ':' - i += skip_blanks(buf + i, nread - i); - if (buf[i] != ':') - errx(2, "Unexpected character '%c' after rule declaration '%s'", - buf[i], rule_name); - i++; - - // Read deps - while (i < u_nread) - { - // Skip blanks - i += skip_blanks(buf + i, nread - i); - - // Read word - size_t skipped_chars = 0; - char *dep_name = readWord(buf + i, nread - i, &skipped_chars); - if (skipped_chars != 0) - printf(" %s", dep_name); - free(dep_name); - i += skipped_chars; - - if (!is_char(buf[i]) && !isblank(buf[i]) && buf[i] != '\0') - errx(2, "Unexpected character '%c'", buf[i]); - } - - i++; - free(rule_name); - } - - free(buf); - fclose(stream); - return 0; -} diff --git a/microshell/microshell.c b/microshell/microshell.c deleted file mode 100644 index 4a7b661..0000000 --- a/microshell/microshell.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - if (argc < 2) - err(1, "Not enough args"); - - int id = fork(); - if (id < 0) - err(1, "Cannot fork"); - - if (id == 0) - { - int res = 0; - wait(&res); - // printf("%d\n", res); - return res; - } - else - { - int res = execl("/bin/sh", "supershell", "-c", argv[1], NULL); - printf("process exit status: %d\n", res); - // exit(res); - return res; - } -} diff --git a/simple_ls/simple_ls.c b/simple_ls/simple_ls.c deleted file mode 100644 index 508c5df..0000000 --- a/simple_ls/simple_ls.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include - -static void simple_ls(char *path) -{ - if (path == NULL) - errx(1, "Internal error: Passed NULL path"); - - DIR *dir = opendir(path); - if (dir == NULL) - errx(1, "Internal error: cannot open directory"); - - struct dirent *element; - while ((element = readdir(dir))) - { - puts(element->d_name); - } - - int res = closedir(dir); - if (res == -1) - errx(1, "Could not close dir"); -} - -int main(int argc, char **argv) -{ - if (argc < 2) - simple_ls("."); - else - { - for (int i = 1; i < argc; i++) - { - simple_ls(argv[i]); - } - } - return 0; -} diff --git a/minimake/src/files/files.c b/src/files/files.c similarity index 100% rename from minimake/src/files/files.c rename to src/files/files.c diff --git a/minimake/src/files/files.h b/src/files/files.h similarity index 100% rename from minimake/src/files/files.h rename to src/files/files.h diff --git a/minimake/src/hash_maps/hash_maps.c b/src/hash_maps/hash_maps.c similarity index 100% rename from minimake/src/hash_maps/hash_maps.c rename to src/hash_maps/hash_maps.c diff --git a/minimake/src/hash_maps/hash_maps.h b/src/hash_maps/hash_maps.h similarity index 100% rename from minimake/src/hash_maps/hash_maps.h rename to src/hash_maps/hash_maps.h diff --git a/minimake/src/lines/lines.c b/src/lines/lines.c similarity index 100% rename from minimake/src/lines/lines.c rename to src/lines/lines.c diff --git a/minimake/src/lines/lines.h b/src/lines/lines.h similarity index 100% rename from minimake/src/lines/lines.h rename to src/lines/lines.h diff --git a/minimake/src/lists/lists.c b/src/lists/lists.c similarity index 100% rename from minimake/src/lists/lists.c rename to src/lists/lists.c diff --git a/minimake/src/lists/lists.h b/src/lists/lists.h similarity index 100% rename from minimake/src/lists/lists.h rename to src/lists/lists.h diff --git a/minimake/src/main.c b/src/main.c similarity index 100% rename from minimake/src/main.c rename to src/main.c diff --git a/minimake/src/minimake.c b/src/minimake.c similarity index 100% rename from minimake/src/minimake.c rename to src/minimake.c diff --git a/minimake/src/minimake.h b/src/minimake.h similarity index 100% rename from minimake/src/minimake.h rename to src/minimake.h diff --git a/minimake/tests/Makefile.empty b/tests/Makefile.empty similarity index 100% rename from minimake/tests/Makefile.empty rename to tests/Makefile.empty diff --git a/minimake/tests/Makefile.syntax-test b/tests/Makefile.syntax-test similarity index 100% rename from minimake/tests/Makefile.syntax-test rename to tests/Makefile.syntax-test diff --git a/minimake/tests/Makefile.test3 b/tests/Makefile.test3 similarity index 100% rename from minimake/tests/Makefile.test3 rename to tests/Makefile.test3 diff --git a/minimake/tests/Makefile.test4 b/tests/Makefile.test4 similarity index 100% rename from minimake/tests/Makefile.test4 rename to tests/Makefile.test4 diff --git a/minimake/tests/Makefile.vars b/tests/Makefile.vars similarity index 100% rename from minimake/tests/Makefile.vars rename to tests/Makefile.vars diff --git a/minimake/tests/Makefile2.syntax-test b/tests/Makefile2.syntax-test similarity index 100% rename from minimake/tests/Makefile2.syntax-test rename to tests/Makefile2.syntax-test diff --git a/minimake/tests/run.sh b/tests/run.sh similarity index 100% rename from minimake/tests/run.sh rename to tests/run.sh