diff --git a/cobc/ChangeLog b/cobc/ChangeLog index 0c5659e71..56bba54af 100644 --- a/cobc/ChangeLog +++ b/cobc/ChangeLog @@ -1,4 +1,20 @@ +2023-07-17 Fabrice Le Fessant + + * cobc.c: add new flags to output dependencies following gcc: -M to + output deps only, -MD to output deps while compiling (in .d files), + -MP to output phony targets, -MG to keep missing copybooks, + -MQ to Makefile-quote target + +2023-07-16 Fabrice Le Fessant + + * cobc.c, flags.def: add new flags to output dependencies: + -foneline-deps outputs all dependencies on a single line instead of + multiple lines; -fcopybook-deps outputs only copybook names instead + of file paths. -fcopybook-deps also forces -E, -foneline-deps, + -MT=copybooks, disables errors on missing copybooks and removes + output on stdout. + 2023-07-26 Simon Sobisch * typeck.c (search_set_keys): improving SEARCH ALL syntax checks diff --git a/cobc/cobc.c b/cobc/cobc.c index b3a52303c..84fb978c8 100644 --- a/cobc/cobc.c +++ b/cobc/cobc.c @@ -106,7 +106,13 @@ enum compile_level { #define CB_FLAG_GETOPT_EBCDIC_TABLE 14 #define CB_FLAG_GETOPT_DEFAULT_COLSEQ 15 #define CB_FLAG_MEMORY_CHECK 16 - +#define CB_FLAG_DEPEND_OUTPUT 17 +#define CB_FLAG_DEPEND_TARGET 18 +#define CB_FLAG_DEPEND_ESCAPE_TARGET 19 +#define CB_FLAG_DEPEND_OUTPUT_FILE 20 +#define CB_FLAG_DEPEND_ADD_PHONY 21 +#define CB_FLAG_DEPEND_KEEP_MISSING 22 +#define CB_FLAG_DEPEND_ON_THE_SIDE 23 /* Info display limits */ #define CB_IMSG_SIZE 24 @@ -244,6 +250,11 @@ FILE *cb_storage_file = NULL; FILE *cb_listing_file = NULL; FILE *cb_depend_file = NULL; const char *cb_ebcdic_table = NULL; +int cb_depend_output = 0; +int cb_depend_output_only = 0; +int cb_depend_add_phony = 0; +int cb_depend_keep_missing = 0; +int cb_depend_target_auto = 0; /* set by option -fttitle= */ char *cb_listing_with_title = NULL; @@ -381,8 +392,9 @@ static char *cobc_libs; /* -l... */ static char *cobc_lib_paths; /* -L... */ static char *cobc_include; /* -I... */ static char *cobc_ldflags; /* -Q / COB_LDFLAGS */ -static char *cb_depend_target; /* -MT <target>... */ +static char *cb_depend_target; /* -MT <target>... */ +static const char *cb_depend_filename; /* -MF <file> */ static size_t cobc_cflags_size; static size_t cobc_libs_size; static size_t cobc_lib_paths_size; @@ -605,8 +617,13 @@ static const struct option long_options[] = { {"j", CB_OP_ARG, NULL, 'j'}, {"Q", CB_RQ_ARG, NULL, 'Q'}, {"A", CB_RQ_ARG, NULL, 'A'}, - {"MT", CB_RQ_ARG, NULL, '!'}, - {"MF", CB_RQ_ARG, NULL, '@'}, + {"M", CB_NO_ARG, NULL, CB_FLAG_DEPEND_OUTPUT }, + {"MT", CB_RQ_ARG, NULL, CB_FLAG_DEPEND_TARGET}, + {"MQ", CB_RQ_ARG, NULL, CB_FLAG_DEPEND_ESCAPE_TARGET}, + {"MF", CB_RQ_ARG, NULL, CB_FLAG_DEPEND_OUTPUT_FILE}, + {"MP", CB_NO_ARG, NULL, CB_FLAG_DEPEND_ADD_PHONY}, + {"MG", CB_NO_ARG, NULL, CB_FLAG_DEPEND_KEEP_MISSING}, + {"MD", CB_NO_ARG, NULL, CB_FLAG_DEPEND_ON_THE_SIDE}, {"coverage", CB_NO_ARG, &cb_coverage_enabled, 1}, {"P", CB_OP_ARG, NULL, 'P'}, {"Xref", CB_NO_ARG, NULL, 'X'}, @@ -2986,6 +3003,74 @@ remove_trailing_slash (char *data) } } +static int string_is_dash (const char*s){ + return ( strcmp (s, COB_DASH) == 0 ); +} + +static void add_depend_target (const char* s){ + if (!cb_depend_target) { + cb_depend_target = cobc_strdup (s); + } else { + /* multiple invocations add to the list */ + const size_t orig_len = strlen (cb_depend_target); + const size_t new_len = strlen (s); + const size_t buff_len = orig_len + 1 + new_len + 1; + cb_depend_target = cobc_realloc (cb_depend_target, buff_len); + memset (cb_depend_target + orig_len, ' ', 1); + memcpy (cb_depend_target + orig_len + 1, s, new_len); + memset (cb_depend_target + orig_len + 1 + new_len, 0, 1); + } +} + +static void add_depend_escape_target (const char* s) +{ + int i, len, nchars; + len = strlen (s); + nchars = 0; + for (i=0; i<len; i++){ + char c = s[i]; + if (c == '$' || c == '#' || c == '*') nchars++; + } + if (nchars){ + char *new_s = cobc_malloc (len+nchars+1); + char *cur_s = new_s ; + for (i=0; i<len; i++){ + char c = s[i]; + if (c == '$'){ + *cur_s++ = '$'; + } else + if (c == '#' || c == '*'){ + *cur_s++ = '\\'; + } + *cur_s++ = c; + } + *cur_s = 0; + add_depend_target (new_s); + cobc_free (new_s); + } else { + add_depend_target (s); + } +} + +static const char* file_replace_extension (const char* file, const char* ext) +{ + int len = strlen (file); + int extlen = strlen (ext); + int i; + for (i=len; i>0; i--){ + char c = file[i]; + if (c == '.'){ + int newlen = i+extlen+1; + char* new_file = cobc_malloc(newlen); + memcpy (new_file, file, i); + memcpy (new_file+i, ext, extlen+1); + return new_file; + } + if (c == '/') break; + } + return cobc_main_stradd_dup (file, ext); +} + /* process command line options */ static int process_command_line (const int argc, char **argv) @@ -3622,28 +3707,29 @@ process_command_line (const int argc, char **argv) cb_define_list = p; break; - case '!': - /* -MT <target> */ - if (!cb_depend_target) { - cb_depend_target = cobc_strdup (cob_optarg); - } else { - /* multiple invocations add to the list */ - const size_t orig_len = strlen (cb_depend_target); - const size_t new_len = strlen (cob_optarg); - const size_t buff_len = orig_len + 1 + new_len + 1; - cb_depend_target = cobc_realloc (cb_depend_target, buff_len); - memset (cb_depend_target + orig_len, ' ', 1); - memcpy (cb_depend_target + orig_len + 1, cob_optarg, new_len); - memset (cb_depend_target + orig_len + 1 + new_len, 0, 1); - } + case CB_FLAG_DEPEND_OUTPUT: /* -M */ + cb_depend_output = 1; + cb_depend_output_only = 1; + cb_compile_level = CB_LEVEL_PREPROCESS; break; - - case '@': - /* -MF <file> */ - cb_depend_file = fopen (cob_optarg, "w"); - if (!cb_depend_file) { - cb_perror (0, "cobc: %s: %s", cob_optarg, cb_get_strerror ()); - } + case CB_FLAG_DEPEND_ON_THE_SIDE: /* -MD */ + cb_depend_output = 1; + cb_depend_target_auto = 1; + break; + case CB_FLAG_DEPEND_ADD_PHONY: /* -MP */ + cb_depend_add_phony = 1; + break; + case CB_FLAG_DEPEND_KEEP_MISSING: /* -MG */ + cb_depend_keep_missing = 1; + break; + case CB_FLAG_DEPEND_TARGET: /* -MT <target> */ + add_depend_target (cob_optarg); + break; + case CB_FLAG_DEPEND_ESCAPE_TARGET: /* -MQ <target> */ + add_depend_escape_target (cob_optarg); + break; + case CB_FLAG_DEPEND_OUTPUT_FILE: /* -MF <file> */ + cb_depend_filename = cobc_strdup(cob_optarg); break; case 'I': @@ -4010,6 +4096,24 @@ process_command_line (const int argc, char **argv) } } + if (cb_flag_copybook_deps){ + /* same as -M, but only COPYBOOK names */ + cb_flag_oneline_deps = 1; + cb_depend_output = 1; + cb_depend_output_only = 1; + cb_depend_keep_missing = 1; + cb_depend_add_phony = 0; + cb_compile_level = CB_LEVEL_PREPROCESS; + } + if (!cb_depend_output && + ( cb_depend_filename || cb_depend_add_phony || cb_depend_target + || cb_depend_keep_missing) ){ + cobc_err_exit ("dependency options require -M or -MD"); + } + if (cb_depend_output_only && cb_compile_level != CB_LEVEL_PREPROCESS){ + cobc_err_exit ("-M is compatible only with -E. Use -MD instead."); + } + /* Load reserved words from fixed word-list if specified */ if (cb_reserved_words != NULL) { cb_load_words(); @@ -4064,7 +4168,29 @@ process_command_line (const int argc, char **argv) } #endif - if (output_name && strcmp (output_name, COB_DASH) == 0) { + if (cb_depend_target_auto){ + if (!cb_depend_filename){ + if (output_name){ + cb_depend_filename = + file_replace_extension (output_name, ".d"); + } + } + } + if (cb_depend_output_only){ + if (cb_depend_filename){ + if (output_name){ + cb_depend_output_only = 0; + } + } else { + if (output_name){ + cb_depend_filename = output_name; + output_name = NULL; + } else + cb_depend_filename = cobc_strdup(COB_DASH); + } + } + + if (output_name && string_is_dash (output_name)) { cb_src_list_file = stdout; if (cb_compile_level != CB_LEVEL_PREPROCESS) { cobc_err_exit (_("output to stdout only valid for preprocess")); @@ -4072,17 +4198,16 @@ process_command_line (const int argc, char **argv) cobc_main_free (output_name); cobc_main_free (output_name_buff); } - -#if 0 /* TODO: */ - if (cb_compile_level == CB_LEVEL_PREPROCESS && output_name && strcmp (output_name, COB_DASH) != 0)) { - cb_depend_file = output_file; - } -#endif - /* TODO: add -M and -MD (breaking change "per GCC" already announced) */ - if (cb_depend_file && !cb_depend_target) { - fclose (cb_depend_file); - cb_depend_file = NULL; - cobc_err_exit (_("-MT must be given to specify target file")); + + if (cb_depend_filename){ + if (string_is_dash(cb_depend_filename)){ + cb_depend_file = stdout; + } else { + cb_depend_file = fopen (cb_depend_filename, "w"); + if (!cb_depend_file) { + cb_perror (0, "cobc: %s: %s", cb_depend_filename, cb_get_strerror ()); + } + } } /* debug: Turn on all exception conditions @@ -4307,7 +4432,7 @@ process_filename (const char *filename) char *full_path; #endif - if (strcmp (filename, COB_DASH) == 0) { + if ( string_is_dash (filename) ) { if (cobc_seen_stdin == 0) { cobc_seen_stdin = 1; file_is_stdin = 1; @@ -5148,7 +5273,9 @@ preprocess (struct filename *fn) #endif if (output_name - || cb_compile_level > CB_LEVEL_PREPROCESS) { + || cb_compile_level > CB_LEVEL_PREPROCESS + || cb_depend_output_only + ) { if (cb_unix_lf) { ppout = fopen(fn->preprocess, "wb"); } else { @@ -5252,7 +5379,7 @@ preprocess (struct filename *fn) fflush (stderr); } if (cb_listing_outputfile && verbose_output >= 0) { - if (strcmp (cb_listing_outputfile, COB_DASH) == 0) { + if (string_is_dash (cb_listing_outputfile)) { cb_src_list_file = stdout; } else { if (cb_unix_lf) { @@ -9109,6 +9236,47 @@ process_file (struct filename *fn, int status) cobc_set_listing_header_code (); } + if (cb_depend_output){ + struct cb_text_list *l; + const char* sep = " \\\n"; + FILE *file = NULL; + + if (cb_flag_oneline_deps) sep = ""; + if (cb_depend_file){ + file = cb_depend_file; + } else { + const char *basename = file_basename (fn->source, NULL); + const char *d_name = file_replace_extension (basename, ".d"); + file = fopen (d_name, "w"); + } + + if (cb_depend_target){ + fprintf (file, "%s:%s", cb_depend_target, sep); + } else { + const char *basename = file_basename (fn->source, NULL); + const char *target = file_replace_extension (basename, "." COB_OBJECT_EXT); + fprintf (file, "%s:%s", target, sep); + } + for (l = cb_depend_list; l; l = l->next) { + fprintf (file, " %s%s", l->text, l->next ? sep : "\n\n"); + } + /* These lines should only be added with -MP */ + if (cb_depend_add_phony){ + for (l = cb_depend_list; l; l = l->next) { + fprintf (file, "%s:\n", l->text); + } + } + if (!cb_depend_file){ + fclose (file); + } + + /* For now, we don't need to free this space as it is + allocated by cobc_plex_malloc() for which no + cobc_plex_free() exists. Everything is freed at the + end. */ + cb_depend_list = NULL; + } + if (cobc_list_file) { putc ('\n', cb_listing_file); } @@ -9258,7 +9426,7 @@ main (int argc, char **argv) memset (cb_listing_header, 0, sizeof (cb_listing_header)); /* If -P=file specified, all lists go to this file */ if (cobc_list_file) { - if (strcmp (cobc_list_file, COB_DASH) == 0) { + if (string_is_dash (cobc_list_file)) { cb_listing_file = stdout; } else if (cb_unix_lf) { @@ -9273,7 +9441,7 @@ main (int argc, char **argv) /* internal complete source listing file */ if (cb_listing_outputfile) { - if (strcmp (cb_listing_outputfile, COB_DASH) == 0) { + if (string_is_dash (cb_listing_outputfile)) { cb_src_list_file = stdout; } else { if (cb_unix_lf) { @@ -9354,15 +9522,7 @@ main (int argc, char **argv) } /* Output dependency list */ - if (cb_depend_file) { - struct cb_text_list *l; - fprintf (cb_depend_file, "%s: \\\n", cb_depend_target); - for (l = cb_depend_list; l; l = l->next) { - fprintf (cb_depend_file, " %s%s\n", l->text, l->next ? " \\" : "\n"); - } - for (l = cb_depend_list; l; l = l->next) { - fprintf (cb_depend_file, "%s:\n", l->text); - } + if (cb_depend_file && cb_depend_file != stdout){ fclose (cb_depend_file); } diff --git a/cobc/cobc.h b/cobc/cobc.h index 73f3d7a23..a87364da7 100644 --- a/cobc/cobc.h +++ b/cobc/cobc.h @@ -472,6 +472,8 @@ extern int cb_saveargc; extern FILE *cb_listing_file; extern FILE *cb_src_list_file; extern FILE *cb_depend_file; +extern int cb_depend_output; +extern int cb_depend_keep_missing; extern struct cb_text_list *cb_depend_list; extern struct cb_text_list *cb_include_list; extern struct cb_text_list *cb_intrinsic_list; diff --git a/cobc/flag.def b/cobc/flag.def index 4850daa60..b4271d30a 100644 --- a/cobc/flag.def +++ b/cobc/flag.def @@ -129,6 +129,12 @@ CB_FLAG (cb_flag_stack_extended, 1, "stack-extended", CB_FLAG_ON (cb_flag_fast_compare, 0, "fast-compare", _(" -fno-fast-compare disables inline comparisions")) +CB_FLAG (cb_flag_copybook_deps, 0, "copybook-deps", + _(" -fcopybook-deps output copybook names as dependencies")) + +CB_FLAG (cb_flag_oneline_deps, 0, "oneline-deps", + _(" -foneline-deps output dependencies on a single line")) + /* Normal flags */ CB_FLAG_ON (cb_flag_remove_unreachable, 1, "remove-unreachable", diff --git a/cobc/pplex.l b/cobc/pplex.l index 623b2e5d4..e7af6c9db 100644 --- a/cobc/pplex.l +++ b/cobc/pplex.l @@ -1376,7 +1376,7 @@ ppopen (const char *name, struct cb_replace_list *replacing_list) } /* add opened file to dependency list */ - if (cb_depend_file) { + if (cb_depend_output && !cb_flag_copybook_deps) { cb_depend_list = pp_text_list_add (cb_depend_list, name, strlen (name)); } @@ -1559,6 +1559,10 @@ ppcopy (const char *name, const char *lib, struct cb_replace_list *replace_list) cb_current_file->copy_line = cb_source_line; } + if (cb_depend_output && cb_flag_copybook_deps){ + cb_depend_list = pp_text_list_add (cb_depend_list, name, strlen (name)); + } + /* TODO: open with path relative to the current file's path, if any (applies both to with and without "lib") */ @@ -1629,10 +1633,21 @@ ppcopy (const char *name, const char *lib, struct cb_replace_list *replace_list) } /* otherwise fall-trough to error handling */ } else { - /* ensure to have errno from name as specified, not from another file */ - (void)access (plexbuff1, R_OK); - /* pass file error as we have no more places to check */ - cb_error ("%s: %s", plexbuff1, cb_get_strerror ()); + /* do not output error if looking only for copybook deps */ + if (!cb_flag_copybook_deps){ + + /* add opened file to dependency list */ + if (cb_depend_output && cb_depend_keep_missing) { + const char *depname = cobc_plex_stradd (plexbuff1, cb_extension_list->text); + + cb_depend_list = pp_text_list_add (cb_depend_list, depname, strlen (depname)); + } + + /* ensure to have errno from name as specified, not from another file */ + (void)access (plexbuff1, R_OK); + /* pass file error as we have no more places to check */ + cb_error ("%s: %s", plexbuff1, cb_get_strerror ()); + } } /* On COPY, open error restore old file */ diff --git a/tests/testsuite.src/used_binaries.at b/tests/testsuite.src/used_binaries.at index 03e6549d1..58bc0ab57 100644 --- a/tests/testsuite.src/used_binaries.at +++ b/tests/testsuite.src/used_binaries.at @@ -271,7 +271,7 @@ AT_CHECK([$GREP 'PARAGRAPH_EX_l_7:' prog.c], [0], ignore, []) AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [bluBb END], []) AT_CAPTURE_FILE([prog.d]) -AT_CHECK([$COBC -I sub/copy prog.cob -ext=cpy -o prog.i -MF prog.d -MT "prog.c prog.h" -MT prog$COB_EXE_EXT -MT prog.$COB_OBJECT_EXT -MT prog.i -fsyntax-only], [0], [], []) +AT_CHECK([$COBC -I sub/copy prog.cob -ext=cpy -o prog.i -M -MP -MF prog.d -MT "prog.c prog.h" -MT prog$COB_EXE_EXT -MT prog.$COB_OBJECT_EXT -MT prog.i -fsyntax-only], [0], [], []) AT_CHECK([$GREP 'prog.c prog.h ' prog.d], [0], ignore, []) AT_CHECK([$GREP ' prog.i:' prog.d], [0], ignore, []) AT_CHECK([$GREP 'sub/copy/PROC.cpy' prog.d], [0], ignore, [], [ @@ -288,7 +288,7 @@ AT_CHECK([$GREP 'sub/copy/PROC.cpy' prog.d], [0], ignore, [], [ ]) # test again with trailing slash which should not result in different files -AT_CHECK([$COBC -I sub/copy/ prog.cob -ext=cpy -o prog.i -MF prog.d -MT "prog.c prog.h" -MT prog$COB_EXE_EXT -MT prog.$COB_OBJECT_EXT -MT prog.i -fsyntax-only], [0], [], []) +AT_CHECK([$COBC -I sub/copy/ prog.cob -ext=cpy -o prog.i -M -MP -MF prog.d -MT "prog.c prog.h" -MT prog$COB_EXE_EXT -MT prog.$COB_OBJECT_EXT -MT prog.i -fsyntax-only], [0], [], []) AT_CHECK([$GREP 'sub/copy/PROC.cpy' prog.d], [0], ignore, [], [ # Previous test "failed" --> no entry with slash available, check backslash for this test AT_CHECK([$GREP 'sub\\copy\\PROC.cpy' prog.d], [0], ignore, []) @@ -1002,3 +1002,121 @@ AT_CHECK([$COBC -fdiagnostics-plain-output -fdiagnostics-show-caret -Wno-others AT_CLEANUP + +AT_SETUP([output dependencies]) +# AT_KEYWORDS([]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + COPY copy1. + PROCEDURE DIVISION. + MAIN-PROC SECTION. + 00. + COPY copy2. + END-PROC SECTION. + COPY copy3 in "sub". + EX. + STOP RUN. +]) + +AT_CHECK([mkdir -p sub]) +AT_DATA([copy1.cpy], []) +AT_DATA([copy2.cpy], []) +AT_DATA([sub/copy3.cpy], []) + +AT_CHECK([$COMPILE_ONLY prog.cob]) + +AT_CHECK([$COMPILE_ONLY -M prog.cob], [0], +[prog.o: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +]) + +AT_CHECK([$COMPILE_ONLY -M -MF prog.dep prog.cob]) + +AT_CHECK([cat prog.dep], [0], +[prog.o: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +]) + +AT_CHECK([$COMPILE_ONLY -M -fcopybook-deps prog.cob], [0], +[prog.o: copy1 copy2 copy3 + +]) + +AT_CHECK([$COMPILE_ONLY -M -MT prog.so prog.cob], [0], +[prog.so: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +]) + +AT_CHECK([$COMPILE_ONLY -M -MQ '$(target)#toto' prog.cob], [0], +[$$(target)\#toto: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +]) + +AT_CHECK([rm -f prog.d]) + +AT_CHECK([$COMPILE -MD prog.cob]) + +AT_CHECK([cat prog.d], [0], +[prog.o: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +]) + +AT_CHECK([$COMPILE -MD -o sub/prog.exe prog.cob]) + +AT_CHECK([cat sub/prog.d], [0], +[prog.o: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +]) + +AT_CHECK([rm -f sub/copy3.cpy]) + +AT_CHECK([$COMPILE_ONLY -M prog.cob], [1], +[prog.o: \ + prog.cob \ + copy1.cpy \ + copy2.cpy + +], +[prog.cob:12: error: sub/copy3: No such file or directory +]) + +AT_CHECK([$COMPILE_ONLY -M -MG --ext cpy prog.cob], [1], +[prog.o: \ + prog.cob \ + copy1.cpy \ + copy2.cpy \ + sub/copy3.cpy + +], +[prog.cob:12: error: sub/copy3: No such file or directory +]) + +AT_CLEANUP