/* gm2spec.cc specific flags and argument handling within GNU Modula-2. Copyright (C) 2007-2022 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 scaffold_static = false; static bool scaffold_dynamic = true; // Default uses -fscaffold-dynamic. 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 = "-"; static const char *multilib_dir = NULL; /* 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; /* 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); } /* build_archive_path returns a string containing the path to the archive defined by libpath and dialectLib. */ static const char * build_archive_path (const char *libpath, const char *library) { if (library != NULL) { const char *libdir = (const char *)library; if (libdir != NULL) { int machine_length = 0; char dir_sep[2]; dir_sep[0] = DIR_SEPARATOR; dir_sep[1] = (char)0; if (multilib_dir != NULL) { machine_length = strlen (multilib_dir); machine_length += strlen (dir_sep); } int l = strlen (libpath) + 1 + strlen ("m2") + 1 + strlen (libdir) + 1 + machine_length + 1; char *s = (char *)xmalloc (l); strcpy (s, libpath); strcat (s, dir_sep); if (machine_length > 0) { strcat (s, multilib_dir); strcat (s, dir_sep); } strcat (s, "m2"); strcat (s, dir_sep); strcat (s, libdir); return s; } } return NULL; } /* safe_strdup safely duplicates a string. */ static char * safe_strdup (const char *s) { if (s != NULL) return xstrdup (s); return NULL; } /* add_default_combination adds the correct link path and then the library name. */ static bool add_default_combination (const char *libpath, const char *library) { if (library != NULL) { append_option (OPT_L, build_archive_path (libpath, library), 1); append_option (OPT_l, safe_strdup (library), 1); return true; } return false; } /* add_default_archives adds the default archives to the end of the current command line. */ static int add_default_archives (const char *libpath, const char *libraries) { const char *l = libraries; const char *e; char *libname; unsigned int libcount = 0; do { e = index (l, ','); if (e == NULL) { libname = xstrdup (l); l = NULL; if (add_default_combination (libpath, libname)) libcount++; free (libname); } else { libname = xstrndup (l, e - l); l = e + 1; if (add_default_combination (libpath, libname)) libcount++; free (libname); } } while ((l != NULL) && (l[0] != (char)0)); return libcount; } /* build_include_path builds the component of the include path referenced by the library. */ static const char * build_include_path (const char *libpath, const char *library) { char dir_sep[2]; char *gm2libs; unsigned int machine_length = 0; dir_sep[0] = DIR_SEPARATOR; dir_sep[1] = (char)0; if (multilib_dir != NULL) { machine_length = strlen (multilib_dir); machine_length += strlen (dir_sep); } gm2libs = (char *)alloca (strlen (libpath) + strlen (dir_sep) + strlen ("m2") + strlen (dir_sep) + strlen (library) + 1 + machine_length + 1); strcpy (gm2libs, libpath); strcat (gm2libs, dir_sep); if (machine_length > 0) { strcat (gm2libs, multilib_dir); strcat (gm2libs, dir_sep); } strcat (gm2libs, "m2"); strcat (gm2libs, dir_sep); strcat (gm2libs, library); return xstrdup (gm2libs); } /* add_include add the correct include path given the libpath and library. The new path is returned. */ static const char * add_include (const char *libpath, const char *library) { if (library == NULL) return NULL; else return build_include_path (libpath, library); } /* add_default_includes add the appropriate default include paths depending upon the style of libraries chosen. */ static void add_default_includes (const char *libpath, const char *libraries) { const char *l = libraries; const char *e; const char *c; const char *path; do { e = index (l, ','); if (e == NULL) { c = xstrdup (l); l = NULL; } else { c = xstrndup (l, e - l); l = e + 1; } path = add_include (libpath, c); append_option (OPT_I, path, 1); } while ((l != NULL) && (l[0] != (char)0)); } /* library_installed returns true if directory library is found under libpath. */ static bool library_installed (const char *libpath, const char *library) { #if defined(HAVE_OPENDIR) && defined(HAVE_DIRENT_H) const char *complete = build_archive_path (libpath, library); DIR *directory = opendir (complete); if (directory == NULL || (errno == ENOENT)) return false; /* Directory exists and therefore the library also exists. */ closedir (directory); return true; #else return false; #endif } /* check_valid check to see that the library is valid. It check the library against the default library set in gm2 and also against any additional libraries installed in the prefix tree. */ static bool check_valid_library (const char *libpath, const char *library) { /* Firstly check against the default libraries (which might not be installed yet). */ for (int i = 0; i < maxlib; i++) if (strcmp (library, library_name[i]) == 0) return true; /* Secondly check whether it is installed (a third party library). */ return library_installed (libpath, library); } /* check_valid_list check to see that the libraries specified are valid. It checks against the default library set in gm2 and also against any additional libraries installed in the libpath tree. */ static bool check_valid_list (const char *libpath, const char *libraries) { const char *start = libraries; const char *end; const char *copy; do { end = index (start, ','); if (end == NULL) { copy = xstrdup (start); start = NULL; } else { copy = xstrndup (start, end - start); start = end + 1; } if (! check_valid_library (libpath, copy)) { error ("library specified %sq is either not installed or does not exist", copy); return false; } } while ((start != NULL) && (start[0] != (char)0)); return true; } /* 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]); /* No abbreviation found therefore assume user specified full library name. */ return add_word (full_libraries, abbreviation); } /* 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 *libraries = NULL; const char *dialect = DEFAULT_DIALECT; const char *libpath = LIBSUBDIR; /* 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; break; case OPT_fmod_: seen_module_extension = true; break; case OPT_fpthread: need_pthread = decoded_options[i].value; 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 break; case OPT_fscaffold_dynamic: seen_scaffold_dynamic = true; scaffold_dynamic = decoded_options[i].value; break; case OPT_fscaffold_static: seen_scaffold_static = true; scaffold_static = decoded_options[i].value; 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; 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: if ((decoded_options[i].orig_option_with_args_text != NULL) && (strncmp (decoded_options[i].orig_option_with_args_text, "-m", 2) == 0)) multilib_dir = xstrdup (decoded_options[i].orig_option_with_args_text + 2); } } if (language != NULL && (strcmp (language, "modula-2") != 0)) return; if (scaffold_static && scaffold_dynamic) { if (! seen_scaffold_dynamic) scaffold_dynamic = false; if (scaffold_dynamic && 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 compile or link. For example include paths for dialect of Modula-2, library paths and default scaffold linking options. */ /* If we have not seen either uselist or gen_module_list and we need to link then we turn on -fgen_module_list=- as the default. */ if ((! (seen_uselist || seen_gen_module_list)) && linking) append_option (OPT_fgen_module_list_, "-", 1); if (allow_libraries) { /* If the libraries have not been specified by the user but the dialect has been specified then select the appropriate libraries. */ if (libraries == NULL) { if (strcmp (dialect, "iso") == 0) libraries = xstrdup ("m2iso,m2pim"); else /* Default to pim libraries if none specified. */ libraries = xstrdup ("m2pim,m2log,m2iso"); } libraries = convert_abbreviations (libraries); if (! check_valid_list (libpath, libraries)) return; add_default_includes (libpath, libraries); } 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) { #ifdef HAVE_LD_STATIC_DYNAMIC if (allow_libraries && !shared_libgm2) append_option (OPT_Wl_, LD_STATIC_OPTION, 1); #endif if (allow_libraries) add_default_archives (libpath, libraries); #ifdef HAVE_LD_STATIC_DYNAMIC if (allow_libraries && !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;