/* gm2spec.cc specific flags and argument handling within GNU Modula-2. Copyright (C) 2007-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Modula-2; see the file COPYING3. If not see . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "xregex.h" #include "obstack.h" #include "intl.h" #include "prefix.h" #include "opt-suggestions.h" #include "gcc.h" #include "opts.h" #include "vec.h" #include "m2/gm2config.h" #ifdef HAVE_DIRENT_H #include #else #ifdef HAVE_SYS_NDIR_H #include #endif #ifdef HAVE_SYS_DIR_H #include #endif #ifdef HAVE_NDIR_H #include #endif #endif /* This bit is set if we saw a `-xfoo' language specification. */ #define LANGSPEC (1<<1) /* This bit is set if they did `-lm' or `-lmath'. */ #define MATHLIB (1<<2) /* This bit is set if they did `-lc'. */ #define WITHLIBC (1<<3) /* Skip this option. */ #define SKIPOPT (1<<4) #ifndef MATH_LIBRARY #define MATH_LIBRARY "m" #endif #ifndef MATH_LIBRARY_PROFILE #define MATH_LIBRARY_PROFILE MATH_LIBRARY #endif #ifndef LIBSTDCXX #define LIBSTDCXX "stdc++" #endif #ifndef LIBSTDCXX_PROFILE #define LIBSTDCXX_PROFILE LIBSTDCXX #endif #ifndef LIBSTDCXX_STATIC #define LIBSTDCXX_STATIC NULL #endif #ifndef LIBCXX #define LIBCXX "c++" #endif #ifndef LIBCXX_PROFILE #define LIBCXX_PROFILE LIBCXX #endif #ifndef LIBCXX_STATIC #define LIBCXX_STATIC NULL #endif #ifndef LIBCXXABI #define LIBCXXABI "c++abi" #endif #ifndef LIBCXXABI_PROFILE #define LIBCXXABI_PROFILE LIBCXXABI #endif #ifndef LIBCXXABI_STATIC #define LIBCXXABI_STATIC NULL #endif /* The values used here must match those of the stdlib_kind enumeration in c.opt. */ enum stdcxxlib_kind { USE_LIBSTDCXX = 1, USE_LIBCXX = 2 }; #define DEFAULT_DIALECT "pim" #undef DEBUG_ARG typedef enum { iso, pim, min, logitech, pimcoroutine, maxlib } libs; /* These are the library names which are installed as part of gm2 and reflect -flibs=name. The -flibs= option provides the user with a short cut to add libraries without having to know the include and link path. */ static const char *library_name[maxlib] = { "m2iso", "m2pim", "m2min", "m2log", "m2cor" }; /* They match the installed archive name for example libm2iso.a, libm2pim.a, libm2min.a, libm2log.a and libm2cor.a. They also match a subdirectory name where the definition modules are kept. The driver checks the argument to -flibs= for an entry in library_name or alternatively the existance of the subdirectory (to allow for third party libraries to coexist). */ static const char *library_abbrev[maxlib] = { "iso", "pim", "min", "log", "cor" }; /* Users may specifiy -flibs=pim,iso etc which are mapped onto -flibs=m2pim,m2iso respectively. This provides a match between the dialect of Modula-2 and the library set. */ static const char *add_include (const char *libpath, const char *library); static bool seen_scaffold_static = false; static bool seen_scaffold_dynamic = false; static bool seen_scaffold_main = false; static bool scaffold_static = false; static bool scaffold_dynamic = true; // Default uses -fscaffold-dynamic. static bool scaffold_main = false; static bool seen_gen_module_list = false; static bool seen_uselist = false; static bool uselist = false; static bool gen_module_list = true; // Default uses -fgen-module-list=-. static const char *gen_module_filename = "-"; /* The original argument list and related info is copied here. */ static unsigned int gm2_xargc; static const struct cl_decoded_option *gm2_x_decoded_options; static void append_arg (const struct cl_decoded_option *); /* The new argument list will be built here. */ static unsigned int gm2_newargc; static struct cl_decoded_option *gm2_new_decoded_options; static const char *full_libraries = NULL; static const char *libraries = NULL; /* Abbreviated libraries. */ /* Return whether strings S1 and S2 are both NULL or both the same string. */ static bool strings_same (const char *s1, const char *s2) { return s1 == s2 || (s1 != NULL && s2 != NULL && strcmp (s1, s2) == 0); } bool options_same (const struct cl_decoded_option *opt1, const struct cl_decoded_option *opt2) { return (opt1->opt_index == opt2->opt_index && strings_same (opt1->arg, opt2->arg) && strings_same (opt1->orig_option_with_args_text, opt2->orig_option_with_args_text) && strings_same (opt1->canonical_option[0], opt2->canonical_option[0]) && strings_same (opt1->canonical_option[1], opt2->canonical_option[1]) && strings_same (opt1->canonical_option[2], opt2->canonical_option[2]) && strings_same (opt1->canonical_option[3], opt2->canonical_option[3]) && (opt1->canonical_option_num_elements == opt2->canonical_option_num_elements) && opt1->value == opt2->value && opt1->errors == opt2->errors); } /* Append another argument to the list being built. */ static void append_arg (const struct cl_decoded_option *arg) { static unsigned int newargsize; if (gm2_new_decoded_options == gm2_x_decoded_options && gm2_newargc < gm2_xargc && options_same (arg, &gm2_x_decoded_options[gm2_newargc])) { ++gm2_newargc; return; /* Nothing new here. */ } if (gm2_new_decoded_options == gm2_x_decoded_options) { /* Make new arglist. */ unsigned int i; newargsize = (gm2_xargc << 2) + 20; /* This should handle all. */ gm2_new_decoded_options = XNEWVEC (struct cl_decoded_option, newargsize); /* Copy what has been done so far. */ for (i = 0; i < gm2_newargc; ++i) gm2_new_decoded_options[i] = gm2_x_decoded_options[i]; } if (gm2_newargc == newargsize) fatal_error (input_location, "overflowed output argument list for %qs", arg->orig_option_with_args_text); gm2_new_decoded_options[gm2_newargc++] = *arg; } /* Append an option described by OPT_INDEX, ARG and VALUE to the list being built. */ static void append_option (size_t opt_index, const char *arg, int value) { struct cl_decoded_option decoded; generate_option (opt_index, arg, value, CL_DRIVER, &decoded); append_arg (&decoded); } /* safe_strdup safely duplicates a string. */ static char * safe_strdup (const char *s) { if (s != NULL) return xstrdup (s); return NULL; } static char * concat_option (char *dest, const char *pre, const char *path, const char *post) { if (dest == NULL) { dest = (char *) xmalloc (strlen (pre) + strlen (path) + strlen (post) + 1); strcpy (dest, pre); strcat (dest, path); strcat (dest, post); return dest; } else { char *result = (char *) xmalloc (strlen (dest) + strlen (pre) + strlen (path) + strlen (post) + 1 + 1); strcpy (result, dest); strcat (result, " "); strcat (result, pre); strcat (result, path); strcat (result, post); free (dest); return result; } } /* add_default_libs adds the -l option which is derived from the libraries. */ static int add_default_libs (const char *libraries) { const char *l = libraries; const char *e; char *libname; unsigned int libcount = 0; while ((l != NULL) && (l[0] != (char)0)) { e = index (l, ','); if (e == NULL) { libname = xstrdup (l); l = NULL; append_option (OPT_l, safe_strdup (libname), 1); libcount++; free (libname); } else { libname = xstrndup (l, e - l); l = e + 1; append_option (OPT_l, safe_strdup (libname), 1); libcount++; free (libname); } } return libcount; } /* add_word returns a new string which has the contents of lib appended to list. If list is NULL then lib is duplicated and returned otherwise the list is appended by "," and the contents of lib. */ static const char * add_word (const char *list, const char *lib) { char *copy; if (list == NULL) return xstrdup (lib); copy = (char *) xmalloc (strlen (list) + strlen (lib) + 1 + 1); strcpy (copy, list); strcat (copy, ","); strcat (copy, lib); return copy; } /* convert_abbreviation checks abbreviation against known library abbreviations. If an abbreviation is found it converts the element to the full library name, otherwise the user supplied name is added to the full_libraries list. A new string is returned. */ static const char * convert_abbreviation (const char *full_libraries, const char *abbreviation) { for (int i = 0; i < maxlib; i++) if (strcmp (abbreviation, library_abbrev[i]) == 0) return add_word (full_libraries, library_name[i]); /* Perhaps the user typed in the whole lib name rather than an abbrev. */ for (int i = 0; i < maxlib; i++) if (strcmp (abbreviation, library_name[i]) == 0) return add_word (full_libraries, abbreviation); /* Not found, probably a user typo. */ error ("%qs is not a valid Modula-2 system library name or abbreviation", abbreviation); return full_libraries; } /* convert_abbreviations checks each element in the library list to see if an a known library abbreviation was used. If found it converts the element to the full library name, otherwise the element is copied into the list. A new string is returned. */ static const char * convert_abbreviations (const char *libraries) { const char *start = libraries; const char *end; const char *full_libraries = NULL; do { end = index (start, ','); if (end == NULL) { full_libraries = convert_abbreviation (full_libraries, start); start = NULL; } else { full_libraries = convert_abbreviation (full_libraries, xstrndup (start, end - start)); start = end + 1; } } while ((start != NULL) && (start[0] != (char)0)); return full_libraries; } void lang_specific_driver (struct cl_decoded_option **in_decoded_options, unsigned int *in_decoded_options_count, int *in_added_libraries) { unsigned int argc = *in_decoded_options_count; struct cl_decoded_option *decoded_options = *in_decoded_options; unsigned int i; /* True if we saw a `-xfoo' language specification on the command line. This function will add a -xmodula-2 if the user has not already placed one onto the command line. */ bool seen_x_flag = false; const char *language = NULL; /* If nonzero, the user gave us the `-p' or `-pg' flag. */ int saw_profile_flag = 0; /* What action to take for the c++ runtime library: -1 means we should not link it in. 0 means we should link it if it is needed. 1 means it is needed and should be linked in. 2 means it is needed but should be linked statically. */ int library = 0; /* Which c++ runtime library to link. */ stdcxxlib_kind which_library = USE_LIBSTDCXX; const char *dialect = DEFAULT_DIALECT; /* An array used to flag each argument that needs a bit set for LANGSPEC, MATHLIB, or WITHLIBC. */ int *args; /* Have we seen -fmod=? */ bool seen_module_extension = false; /* Should the driver perform a link? */ bool linking = true; /* Should the driver link the shared gm2 libs? */ bool shared_libgm2 = true; /* "-lm" or "-lmath" if it appears on the command line. */ const struct cl_decoded_option *saw_math = NULL; /* "-lc" if it appears on the command line. */ const struct cl_decoded_option *saw_libc = NULL; /* By default, we throw on the math library if we have one. */ int need_math = (MATH_LIBRARY[0] != '\0'); /* 1 if we should add -lpthread to the command-line. FIXME: the default should be a configuration choice. */ int need_pthread = 1; /* True if we saw -static. */ int static_link = 0; /* True if we should add -shared-libgcc to the command-line. */ int shared_libgcc = 1; /* Have we seen the -v flag? */ bool verbose = false; /* The number of libraries added in. */ int added_libraries; #ifdef ENABLE_PLUGIN /* True if we should add -fplugin=m2rte to the command-line. */ bool need_plugin = true; #else bool need_plugin = false; #endif /* True if we should set up include paths and library paths. */ bool allow_libraries = true; #if defined(DEBUG_ARG) printf ("argc = %d\n", argc); fprintf (stderr, "Incoming:"); for (i = 0; i < argc; i++) fprintf (stderr, " %s", decoded_options[i].orig_option_with_args_text); fprintf (stderr, "\n"); #endif gm2_xargc = argc; gm2_x_decoded_options = decoded_options; gm2_newargc = 0; gm2_new_decoded_options = decoded_options; added_libraries = *in_added_libraries; args = XCNEWVEC (int, argc); /* First pass through arglist. If -nostdlib or a "turn-off-linking" option is anywhere in the command line, don't do any library-option processing (except relating to -x). */ for (i = 1; i < argc; i++) { const char *arg = decoded_options[i].arg; args[i] = 0; #if defined(DEBUG_ARG) printf ("1st pass: %s\n", decoded_options[i].orig_option_with_args_text); #endif switch (decoded_options[i].opt_index) { case OPT_fiso: dialect = "iso"; break; case OPT_fpim2: dialect = "pim2"; break; case OPT_fpim3: dialect = "pim3"; break; case OPT_fpim4: dialect = "pim4"; break; case OPT_fpim: dialect = "pim"; break; case OPT_flibs_: libraries = xstrdup (arg); allow_libraries = decoded_options[i].value; args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fmod_: seen_module_extension = true; args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fpthread: need_pthread = decoded_options[i].value; args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fm2_plugin: need_plugin = decoded_options[i].value; #ifndef ENABLE_PLUGIN if (need_plugin) error ("plugin support is disabled; configure with " "%<--enable-plugin%>"); #endif args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fscaffold_dynamic: seen_scaffold_dynamic = true; scaffold_dynamic = decoded_options[i].value; args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fscaffold_static: seen_scaffold_static = true; scaffold_static = decoded_options[i].value; args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fscaffold_main: seen_scaffold_main = true; scaffold_main = decoded_options[i].value; args[i] |= SKIPOPT; /* We will add the option if it is needed. */ break; case OPT_fgen_module_list_: seen_gen_module_list = true; gen_module_list = decoded_options[i].value; if (gen_module_list) gen_module_filename = decoded_options[i].arg; break; case OPT_fuse_list_: seen_uselist = true; uselist = decoded_options[i].value; break; case OPT_nostdlib: case OPT_nostdlib__: case OPT_nodefaultlibs: library = -1; break; case OPT_l: if (strcmp (arg, MATH_LIBRARY) == 0) { args[i] |= MATHLIB; need_math = 0; } else if (strcmp (arg, "c") == 0) args[i] |= WITHLIBC; else /* Unrecognized libraries (e.g. -lfoo) may require libstdc++. */ library = (library == 0) ? 1 : library; break; case OPT_pg: case OPT_p: saw_profile_flag++; break; case OPT_x: seen_x_flag = true; language = arg; break; case OPT_v: verbose = true; break; case OPT_Xlinker: case OPT_Wl_: /* Arguments that go directly to the linker might be .o files, or something, and so might cause libstdc++ to be needed. */ if (library == 0) library = 1; break; case OPT_c: case OPT_r: case OPT_S: case OPT_E: case OPT_M: case OPT_MM: case OPT_fsyntax_only: /* Don't specify libraries if we won't link, since that would cause a warning. */ linking = false; library = -1; break; /* PCH makes no sense here, we do not catch -output-pch on purpose, that should flag an error. */ case OPT_fpch_deps: case OPT_fpch_preprocess: case OPT_Winvalid_pch: args[i] |= SKIPOPT; break; case OPT_static: static_link = 1; break; case OPT_static_libgcc: shared_libgcc = 0; break; case OPT_static_libstdc__: library = library >= 0 ? 2 : library; #ifdef HAVE_LD_STATIC_DYNAMIC /* Remove -static-libstdc++ from the command only if target supports LD_STATIC_DYNAMIC. When not supported, it is left in so that a back-end target can use outfile substitution. */ args[i] |= SKIPOPT; #endif break; case OPT_static_libgm2: shared_libgm2 = false; #ifdef HAVE_LD_STATIC_DYNAMIC /* Remove -static-libgm2 from the command only if target supports LD_STATIC_DYNAMIC. When not supported, it is left in so that a back-end target can use outfile substitution. */ args[i] |= SKIPOPT; #endif break; case OPT_stdlib_: which_library = (stdcxxlib_kind) decoded_options[i].value; break; default: break; } } if (language != NULL && (strcmp (language, "modula-2") != 0)) return; /* Override the default when the user specifies it. */ if (seen_scaffold_static && scaffold_static && !seen_scaffold_dynamic) scaffold_dynamic = false; /* If both options have been seen and both are true, that means the user tried to set both. */ if (seen_scaffold_dynamic && scaffold_dynamic && seen_scaffold_static && scaffold_static) error ("%qs and %qs cannot both be enabled", "-fscaffold-dynamic", "-fscaffold-static"); if (uselist && gen_module_list) { if (! seen_gen_module_list) gen_module_list = false; if (uselist && gen_module_list) error ("%qs and %qs cannot both be enabled", "-fgen-module-list=", "-fuse-list="); } /* There's no point adding -shared-libgcc if we don't have a shared libgcc. */ #ifndef ENABLE_SHARED_LIBGCC shared_libgcc = 0; #endif /* Second pass through arglist, transforming arguments as appropriate. */ append_arg (&decoded_options[0]); /* Start with command name, of course. */ for (i = 1; i < argc; ++i) { #if defined(DEBUG_ARG) printf ("2nd pass: %s\n", decoded_options[i].orig_option_with_args_text); #endif if ((args[i] & SKIPOPT) == 0) { append_arg (&decoded_options[i]); /* Make sure -lstdc++ is before the math library, since libstdc++ itself uses those math routines. */ if (!saw_math && (args[i] & MATHLIB) && library > 0) saw_math = &decoded_options[i]; if (!saw_libc && (args[i] & WITHLIBC) && library > 0) saw_libc = &decoded_options[i]; } #if defined(DEBUG_ARG) else printf ("skipping: %s\n", decoded_options[i].orig_option_with_args_text); #endif } /* We now add in extra arguments to facilitate a successful link. Note that the libraries are added to the end of the link here and also placed earlier into the link by lang-specs.h. Possibly this is needed because the m2pim,m2iso libraries are cross linked (--fixme-- combine all the m2 libraries into a single archive). We also add default scaffold linking options. */ /* If we have not seen either uselist or gen_module_list and we need to link or compile a module list then we turn on -fgen_module_list=- as the default. */ if (!seen_uselist && !seen_gen_module_list && (linking || scaffold_main)) append_option (OPT_fgen_module_list_, "-", 1); /* We checked that they were not both enabled above, if there was a set value (even iff that is 'off'), pass that to the FE. */ if (seen_scaffold_dynamic || scaffold_dynamic) append_option (OPT_fscaffold_dynamic, NULL, scaffold_dynamic); if (seen_scaffold_static) append_option (OPT_fscaffold_static, NULL, scaffold_static); /* If the user has set fscaffold-main specifically, use that. Otherwise, if we are linking then set it so that we generate the relevant code for the main module. */ if (seen_scaffold_main) append_option (OPT_fscaffold_main, NULL, scaffold_main); else if (linking) append_option (OPT_fscaffold_main, NULL, true); if (allow_libraries) { /* If the libraries have not been specified by the user, select the appropriate libraries for the active dialect. */ if (libraries == NULL) { if (strcmp (dialect, "iso") == 0) libraries = xstrdup ("m2iso,m2cor,m2pim,m2log"); else /* Default to pim libraries otherwise. */ libraries = xstrdup ("m2pim,m2iso,m2cor,m2log"); } libraries = convert_abbreviations (libraries); append_option (OPT_flibs_, xstrdup (libraries), 1); } else append_option (OPT_flibs_, xstrdup ("-"), 0); /* no system libs. */ if ((! seen_x_flag) && seen_module_extension) append_option (OPT_x, "modula-2", 1); if (need_plugin) append_option (OPT_fplugin_, "m2rte", 1); if (linking) { if (allow_libraries) { #ifdef HAVE_LD_STATIC_DYNAMIC if (!shared_libgm2) append_option (OPT_Wl_, LD_STATIC_OPTION, 1); #endif added_libraries += add_default_libs (libraries); #ifdef HAVE_LD_STATIC_DYNAMIC if (!shared_libgm2) append_option (OPT_Wl_, LD_DYNAMIC_OPTION, 1); #endif } /* Add `-lstdc++' if we haven't already done so. */ #ifdef HAVE_LD_STATIC_DYNAMIC if (library > 1 && !static_link) append_option (OPT_Wl_, LD_STATIC_OPTION, 1); #endif if (which_library == USE_LIBCXX) { append_option (OPT_l, saw_profile_flag ? LIBCXX_PROFILE : LIBCXX, 1); added_libraries++; if (LIBCXXABI != NULL) { append_option (OPT_l, saw_profile_flag ? LIBCXXABI_PROFILE : LIBCXXABI, 1); added_libraries++; } } else { append_option (OPT_l, saw_profile_flag ? LIBSTDCXX_PROFILE : LIBSTDCXX, 1); added_libraries++; } /* Add target-dependent static library, if necessary. */ if ((static_link || library > 1) && LIBSTDCXX_STATIC != NULL) { append_option (OPT_l, LIBSTDCXX_STATIC, 1); added_libraries++; } #ifdef HAVE_LD_STATIC_DYNAMIC if (library > 1 && !static_link) append_option (OPT_Wl_, LD_DYNAMIC_OPTION, 1); #endif } if (need_math) { append_option (OPT_l, saw_profile_flag ? MATH_LIBRARY_PROFILE : MATH_LIBRARY, 1); added_libraries++; } if (need_pthread) { append_option (OPT_l, "pthread", 1); added_libraries++; } if (shared_libgcc && !static_link) append_option (OPT_shared_libgcc, NULL, 1); if (verbose && gm2_new_decoded_options != gm2_x_decoded_options) { fprintf (stderr, _("Driving:")); for (i = 0; i < gm2_newargc; i++) fprintf (stderr, " %s", gm2_new_decoded_options[i].orig_option_with_args_text); fprintf (stderr, "\n"); fprintf (stderr, "new argc = %d, added_libraries = %d\n", gm2_newargc, added_libraries); } *in_decoded_options_count = gm2_newargc; *in_decoded_options = gm2_new_decoded_options; *in_added_libraries = added_libraries; } /* Called before linking. Returns 0 on success and -1 on failure. */ int lang_specific_pre_link (void) /* Not used for M2. */ { return 0; } /* Number of extra output files that lang_specific_pre_link may generate. */ int lang_specific_extra_outfiles = 0;