diff --git a/.gitignore b/.gitignore index 14c96f3..d535e3a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ *.class *.log *.core -minimake +minimake/minimake diff --git a/AUTHORS b/AUTHORS index 69b5c09..d5e33a8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,3 +3,4 @@ guillem.george Special thanks to - Timothée Battefort for the parser +- Amadéo Heaulme for the error handling diff --git a/minimake/src/main.c b/minimake/src/main.c index 07ee99d..4ace197 100644 --- a/minimake/src/main.c +++ b/minimake/src/main.c @@ -88,7 +88,7 @@ static int handle_args(int argc, char **argv, struct list **minimake_files, return flags; } -char *getMakefile(struct list *files) +char *get_makefile(struct list *files) { struct list *file = files; int found = 0; @@ -98,11 +98,14 @@ char *getMakefile(struct list *files) { FILE *test = fopen(file->data, "r"); if (test != NULL) + { + fclose(test); found = 1; - - fclose(test); - if (!found) + } + else + { file = file->next; + } } char *res = NULL; @@ -119,7 +122,7 @@ int main(int argc, char **argv) struct list *rules; int flags = handle_args(argc, argv, &files, &rules); - char *filename = getMakefile(files); + char *filename = get_makefile(files); if (filename == NULL) errx(GENERIC_ERR, "No Makefile found"); diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index 3e37319..fa8697b 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -14,12 +15,34 @@ #include "lists/lists.h" // Static variables -struct hash_map *variables = NULL; -struct hash_map *rules = NULL; + char *program_name; +struct hash_map *variables = NULL; +struct hash_map *rules = NULL; + +// Keeps track of variables and rules order +struct list *variables_list = NULL; +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) @@ -36,25 +59,26 @@ 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_destroy(r->dependencies); - list_destroy(r->recipe); + list_deep_destroy(r->dependencies); + list_deep_destroy(r->recipe); free(r); - free(entry->key); + // free(entry->key); } - if (hash_map == variables) + else if (hash_map == variables) { - struct variable *v = entry->value; - free(v->name); - free(v->value); + // struct variable *v = entry->value; + // free(v->name); + // free(v->value); free(entry->key); free(entry->value); } else { - printf("DEBUG: attempting to free a generic hashmap"); + printf("DEBUG: attempting to free a generic hashmap\n"); free(entry->key); free(entry->value); } @@ -69,31 +93,84 @@ static void hashmap_deep_free(struct hash_map *hash_map) static void free_all() { + list_destroy(rules_list); + list_destroy(variables_list); hashmap_deep_free(rules); hashmap_deep_free(variables); } -// static void dump_database() -// { -// hashmap_dump(variables); -// hashmap_dump(rules); -// } - -static void exit_on_error(int status, char *message) +static void exit_on_error(int status, char *format, ...) { - char *full_msg; - int err = sprintf(full_msg, "%s: %s\n", program_name, message); - free(message); - if (err == -1) - exit(status); + // Print + va_list args; + va_start(args, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); - write(STDERR_FILENO, full_msg, strlen(full_msg)); - - free(full_msg); 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 @@ -107,15 +184,15 @@ static void register_rule(char *name, struct list *dependencies, rule->dependencies = dependencies; rule->recipe = recipe; - int err = hashmap_insert(variables, name, rule, NULL); + int err = hashmap_insert(rules, name, rule, NULL); if (!err) { - char *err_msg; - sprintf(err_msg, - "Internal Error: Couln't add entry for '%s' in the hashmap", - name); - exit_on_error(GENERIC_ERR, err_msg); + exit_on_error( + GENERIC_ERR, + "Internal Error: Couln't add entry for '%s' in the hashmap", name); } + + rules_list = list_append(rules_list, name); } // Registers a new variable in the hashmap @@ -126,12 +203,12 @@ static void register_variable(char *name, char *value) int err = hashmap_insert(variables, name, value, NULL); if (!err) { - char *err_msg; - sprintf(err_msg, - "Internal Error: Couln't add entry for '%s' in the hashmap", - name); - exit_on_error(GENERIC_ERR, err_msg); + exit_on_error( + GENERIC_ERR, + "Internal Error: Couln't add entry for '%s' in the hashmap", name); } + + variables_list = list_append(variables_list, name); } // Parse dependencies from buf and returns them inside a chained list @@ -161,12 +238,10 @@ static struct list *read_deps(struct line *l, size_t offset) if (!ischar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0' && buf[i] != '\n') { - char *err_msg; - sprintf( - err_msg, + exit_on_error( + GENERIC_ERR, "Unexpected character '%c' after rule declaration at %lu:%lu", buf[i], l->number, i); - exit_on_error(GENERIC_ERR, err_msg); } } @@ -202,13 +277,13 @@ static struct list *read_recipe(struct line *l) char *command = strdup(buf + 1); if (command == NULL) { - char *err_msg; - sprintf(err_msg, - "Internal error: couldn't duplicate string (%lu:1)", - l->number); - exit_on_error(GENERIC_ERR, err_msg); + exit_on_error( + GENERIC_ERR, + "Internal error: couldn't duplicate string (%lu:1)", + l->number); } + command[l->length - 2] = '\0'; res = list_append(res, command); } } @@ -218,13 +293,13 @@ static struct list *read_recipe(struct line *l) } // Reads the value after a variable declaration -static char *read_value(char *buf, size_t buf_size) +static char *read_value(char *buf, size_t buf_len) { size_t i = 0; size_t str_buf_size = STRING_BUFFER_SIZE; char *str_buf = malloc(sizeof(char) * str_buf_size); - while (i < buf_size && buf[i] != '\0' && buf[i] != '#') + while (i < buf_len && buf[i] != '\n' && buf[i] != '#' && buf[i] != '\0') { // Reallocate more space if necessary if (i >= str_buf_size - 1) @@ -232,11 +307,7 @@ static char *read_value(char *buf, size_t buf_size) str_buf_size += STRING_BUFFER_SIZE; str_buf = realloc(str_buf, str_buf_size); if (str_buf == NULL) - { - char *err_msg; - sprintf(err_msg, "Could not realloc"); - exit_on_error(GENERIC_ERR, err_msg); - } + exit_on_error(GENERIC_ERR, "Could not realloc"); } str_buf[i] = buf[i]; @@ -248,7 +319,104 @@ static char *read_value(char *buf, size_t buf_size) return str_buf; } -// static char *expand_variable(); +// 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 tmp_buf_size = STRING_BUFFER_SIZE; +// char *tmp_buf = malloc(sizeof(char) * tmp_buf_size); +// // TODO free on err + +// 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"); +// } + +// // Copy +// tmp_buf[i] = buf[i]; +// i++; +// } + +// // End +// tmp_buf[i] = '\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); +// } + +// 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); +// } + +// free(tmp_buf); +// *value = val; +// 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 @@ -274,14 +442,14 @@ static void parse_line(struct line *current_line) struct list *dependencies; struct list *recipe; char *value; - char *err_msg; // Definition type switch (buf[i]) { // Variable case '=': - value = read_value(buf + i, line_size); + i += skipblanks(buf + i + 1, line_size - i) + 1; + value = read_value(buf + i, line_size - i); register_variable(name, value); break; @@ -305,20 +473,18 @@ static void parse_line(struct line *current_line) case '#': if (name != NULL) { - sprintf( - err_msg, + exit_on_error( + GENERIC_ERR, "Unexpected character '%c' after declaration '%s' at line %lu", buf[i], name, current_line->number); - exit_on_error(GENERIC_ERR, err_msg); } - else - break; + break; default: - sprintf(err_msg, - "Unexpected character '%c' after declaration '%s' at line %lu", - buf[i], name, current_line->number); - exit_on_error(GENERIC_ERR, err_msg); + exit_on_error( + GENERIC_ERR, + "Unexpected character '%c' after declaration '%s' at line %lu", + buf[i], name, current_line->number); } // free(name); @@ -341,11 +507,7 @@ void make_parse(char *path) size_t buf_size = BUFFER_SIZE; char *buf = malloc(sizeof(char) * buf_size); if (buf == NULL) - { - char *err_msg; - sprintf(err_msg, "Could not allocate more memory"); - exit_on_error(GENERIC_ERR, err_msg); - } + exit_on_error(GENERIC_ERR, "Could not allocate more memory"); // Parse line by line ssize_t nread; @@ -355,11 +517,8 @@ void make_parse(char *path) while ((nread = getline(&buf, &buf_size, stream)) != -1) { if (nread == -1) - { - char *err_msg; - sprintf(err_msg, "Could not get line %lu", current_line.number); - exit_on_error(GENERIC_ERR, err_msg); - } + exit_on_error(GENERIC_ERR, "Could not get line %lu", + current_line.number); current_line.buffer = buf; current_line.length = nread; @@ -382,10 +541,11 @@ void make(char *path, int flags, char *argv0) { program_name = argv0; - if (flags & FLAGS_PRINT) - errx(GENERIC_ERR, "Not Implemented"); - make_parse(path); - // dump_database(); + + if (flags & FLAGS_PRINT) + // errx(GENERIC_ERR, "Not Implemented"); + dump_database(); + free_all(); } diff --git a/minimake/tests/Makefile.syntax-test b/minimake/tests/Makefile.syntax-test new file mode 100644 index 0000000..fff0427 --- /dev/null +++ b/minimake/tests/Makefile.syntax-test @@ -0,0 +1,35 @@ +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)" diff --git a/minimake/tests/run.sh b/minimake/tests/run.sh new file mode 100644 index 0000000..0fe1e07 --- /dev/null +++ b/minimake/tests/run.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +########################################### +# PIERRE PAGNOUX EXPERIENCE TESTSUITE # +########################################### + +#----------------- COLOR -----------------# +# 0 - No style | 1 - Bold +RED="\e[0;31m" +BRED="\e[1;31m" +GRN="\e[0;32m" +BGRN="\e[1;32m" +YEL="\e[0;33m" +BYEL="\e[1;33m" +BLU="\e[0;34m" +BBLU="\e[1;34m" +PUR="\e[0;35m" +BPUR="\e[1;35m" +CYA="\e[0;36m" +BCYA="\e[1;36m" +WHI="\e[0;37m" +BWHI="\e[1;37m" +GRE="\e[2;37m" + +#----------------- GLOBV -----------------# +ref_out=/tmp/ref_out.out +my_out=/tmp/my_out.out + +#----------------- TESTS -----------------# + +tit_wrap() +{ + echo -e $@$WHI +} + +func_test() +{ + tit_wrap - $PUR $1 + shift + printf $@ > $ref_out + ../../tinyprintf $@ > $my_out + + diff $ref_out $my_out > /tmp/null + + if [ $? -eq 0 ]; then + tit_wrap $GRN GOOD + else + tit_wrap $RED EXPECTED: $BRED$(cat $ref_out) \|$RED GOT: $BRED$(cat $my_out) + fi +} + +func_file() +{ + for file in *; do + if [ -f $file ]; then + func_test $file $(cat $file) + fi + done +} + +func_dir() +{ + cd tests + for dir in *; do + if [ -d $dir ]; then + tit_wrap == $dir == + cd $dir + func_file + cd .. + fi + done + cd .. +} + +# func_dir + +echo "Tests should be here"