blob: bc93133243c427521fdeb6ad3fd2575c7af8e353 [file] [log] [blame]
/* gm2spec.cc specific flags and argument handling within GNU Modula-2.
Copyright (C) 2007-2023 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius@glam.ac.uk>.
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
<http://www.gnu.org/licenses/>. */
#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 <dirent.h>
#else
#ifdef HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#ifdef HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#ifdef HAVE_NDIR_H
#include <ndir.h>
#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;