aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGaius Mulley <gaiusmod2@gmail.com>2025-08-28 12:08:33 +0100
committerGaius Mulley <gaiusmod2@gmail.com>2025-08-28 12:08:33 +0100
commit69faef01dff124cd2e657b7525ba6cc574626853 (patch)
tree166592b4eaa918875e5a2fe17094832a8c93c7c9
parent098cf067772921764e7eb618e3e0614a7df0a947 (diff)
downloadgcc-69faef01dff124cd2e657b7525ba6cc574626853.zip
gcc-69faef01dff124cd2e657b7525ba6cc574626853.tar.gz
gcc-69faef01dff124cd2e657b7525ba6cc574626853.tar.bz2
PR modula2/121629: adding third party modules
This patch makes it easier to add third party modules. cc1gm2 now appends the search directory prefix/include/m2 to the search path for non dialect specific modules. Prior to this it appends the dialect specific subdirectories {m2pim,m2iso,m2log,m2min} with the appropriate dialect pathname. The patch also includes a new option -fm2-pathname-root=prefix which allow additional prefix/m2 directories to be searched before the default. gcc/ChangeLog: PR modula2/121629 * doc/gm2.texi (Module Search Path): New section. (Compiler options): New option -fm2-pathname-root=. New option -fm2-pathname-rootI. gcc/m2/ChangeLog: PR modula2/121629 * gm2-compiler/PathName.mod: Add copyright notice. * gm2-lang.cc (named_path): Add field lib_root. (push_back_Ipath): Set lib_root false. (push_back_lib_root): New function. (get_dir_sep_size): Ditto. (add_path_component): Ditto. (add_one_import_path): Ditto. (add_non_dialect_specific_path): Ditto. (foreach_lib_gen_import_path): Ditto. (get_module_source_dir): Ditto. (add_default_include_paths): Ditto. (assign_flibs): Ditto. (m2_pathname_root): Ditto. (add_m2_import_paths): Remove function. (gm2_langhook_post_options): Call assign_flibs. Check np.lib_root and call foreach_lib_gen_import_path. Replace call to add_m2_import_paths with a call to add_default_include_paths. (gm2_langhook_handle_option): Add case OPT_fm2_pathname_rootI_. * gm2spec.cc (named_path): Add field lib_root. (push_back_Ipath): Set lib_root false. (push_back_lib_root): New function. (add_m2_I_path): Add OPT_fm2_pathname_rootI_ option if np.lib_root. (lang_specific_driver): Add case OPT_fm2_pathname_root_. * lang.opt (fm2-pathname-root=): New option. (fm2-pathname-rootI=): Ditto. gcc/testsuite/ChangeLog: PR modula2/121629 * gm2/switches/pathnameroot/pass/switches-pathnameroot-pass.exp: New test. * gm2/switches/pathnameroot/pass/test.mod: New test. * gm2/switches/pathnameroot/pass/testlib/m2/foo.def: New test. * gm2/switches/pathnameroot/pass/testlib/m2/foo.mod: New test. Signed-off-by: Gaius Mulley <gaiusmod2@gmail.com>
-rw-r--r--gcc/doc/gm2.texi69
-rw-r--r--gcc/m2/gm2-compiler/PathName.mod21
-rw-r--r--gcc/m2/gm2-lang.cc283
-rw-r--r--gcc/m2/gm2spec.cc31
-rw-r--r--gcc/m2/lang.opt10
-rwxr-xr-xgcc/testsuite/gm2/switches/pathnameroot/pass/switches-pathnameroot-pass.exp48
-rw-r--r--gcc/testsuite/gm2/switches/pathnameroot/pass/test.mod6
-rw-r--r--gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.def7
-rw-r--r--gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.mod3
9 files changed, 384 insertions, 94 deletions
diff --git a/gcc/doc/gm2.texi b/gcc/doc/gm2.texi
index 9bd0f0d..4147a287 100644
--- a/gcc/doc/gm2.texi
+++ b/gcc/doc/gm2.texi
@@ -143,11 +143,7 @@ available and access to assembly programming is achieved using the
same syntax as that used by GCC.
The gm2 driver allows third party libraries to be installed alongside
-gm2 libraries. For example if the user specifies library @code{foo}
-using @code{-flibs=foo} the driver will check the standard GCC install
-directory for a sub directory @code{foo} containing the library
-contents. The library module search path is altered accordingly
-for compile and link.
+gm2 libraries. @xref{Module Search Path}.
@node Development, Features, Why use GNU Modula-2, Overview
@section How to get source code using git
@@ -229,6 +225,7 @@ such as the AVR and the ARM).
* Standard procedures:: Permanently accessible base procedures.
* High procedure function:: Behavior of the high procedure function.
* Dialect:: GNU Modula-2 supported dialects.
+* Module Search Path:: How to add library modules.
* Exceptions:: Exception implementation
* Semantic checking:: How to detect run time problems at compile time.
* Extensions:: GNU Modula-2 language extensions.
@@ -525,6 +522,15 @@ following include paths.
for internal use only: used by the driver to copy the user facing @samp{-I}
option.
+@item -fm2-pathname-root=@file{pathroot}
+add search paths derived from the specified @file{pathroot}.
+@xref{Module Search Path} for examples.
+
+@item -fm2-pathname-rootI
+for internal use only: used by the driver to copy every user
+@samp{-fm2-pathname-root=} facing option in order with all other
+@samp{-I} options.
+
@item -fm2-plugin
insert plugin to identify run time errors at compile time (default on).
@@ -1379,7 +1385,7 @@ Actual parameter | HIGH (a) | a[HIGH (a)] = nul
str3 | 3 | TRUE
@end example
-@node Dialect, Exceptions, High procedure function, Using
+@node Dialect, Module Search Path, High procedure function, Using
@section GNU Modula-2 supported dialects
This section describes the dialects understood by GNU Modula-2.
@@ -1444,6 +1450,39 @@ implemented as above, apart from the exception calling in the ISO
dialect. Instead of exception handling the results are the same as the
PIM4 dialect. This is a temporary implementation situation.
+@node Module Search Path, Exceptions, Dialect, Using
+@section Module Search Path
+
+This section describes the default module search path and how this
+might be changed. By default the compiler will search the current
+directory, site wide modules and lastly gcc version specific modules.
+
+The @samp{-I} option option can be used to introduce new directories
+in the module search path and for convenience the options @samp{-flibs=}
+and @samp{-fm2-pathname-root=} are also provided.
+
+The site wide modules are located at @var{prefix}@file{/include/m2}
+whereas the version specific modules are located in
+@var{libsubdir}@file{/m2}. Both of these @file{/m2} directories
+are organized such that the non dialect specific modules are at the
+top and dialect specific modules are in subdirectories.
+
+The @samp{-fm2-pathname-root=} option is equivalent to adding a
+@samp{-I} path for every library dialect. For example if the library
+dialect order is selected by @samp{-flibs=pim,iso,log} and
+@samp{-fm2-pathname-root=foo} is supplied then this is equivalent to
+the following pairs of options:
+
+@example
+-fm2-pathname=m2pim -I@file{foo/m2/m2pim}
+-fm2-pathname=m2iso -I@file{foo/m2/m2iso}
+-fm2-pathname=m2log -I@file{foo/m2/m2log}
+-fm2-pathname=- -I@file{foo/m2}
+@end example
+
+The option @samp{-fsources} will show the source module, path and
+pathname for each module parsed.
+
@node Exceptions, Semantic checking, Dialect, Using
@section Exception implementation
@@ -2023,7 +2062,7 @@ CONST
VAR
head: List ;
-@end group
+@end group
@end example
@example
@@ -2034,13 +2073,13 @@ VAR
BEGIN
p := head^.next ;
printf ("\nunique data\n");
- printf ("===========\n");
+ printf ("===========\n");
WHILE p # NIL DO
printf ("%d\n", p^.value);
p := p^.next
END
END Display ;
-@end group
+@end group
@end example
@example
@@ -2053,7 +2092,7 @@ BEGIN
next := NIL
END
END Add ;
-@end group
+@end group
@end example
@example
@@ -2075,17 +2114,17 @@ EXCEPT
THEN
printf ("list was empty, add sentinal\n");
Add (head, -1) ;
- RETRY (* Jump back to the begin statement. *)
+ RETRY (* Jump back to the begin statement. *)
ELSIF p^.next = NIL
THEN
printf ("growing the list\n");
Add (p^.next, val) ;
RETRY (* Jump back to the begin statement. *)
ELSE
- printf ("should never reach here!\n");
+ printf ("should never reach here!\n");
END
END Unique ;
-@end group
+@end group
@end example
@example
@@ -2104,7 +2143,7 @@ BEGIN
head := NIL ;
unique
END lazyunique.
-@end group
+@end group
@end example
@example
@@ -2127,7 +2166,7 @@ unique data
0
2
1
-@end group
+@end group
@end example
@node Unbounded by reference, Building a shared library, Exception handling, Using
diff --git a/gcc/m2/gm2-compiler/PathName.mod b/gcc/m2/gm2-compiler/PathName.mod
index 6fc7612..0ba9024 100644
--- a/gcc/m2/gm2-compiler/PathName.mod
+++ b/gcc/m2/gm2-compiler/PathName.mod
@@ -1,3 +1,24 @@
+(* M2PathName.mod maintain a dictionary of named paths.
+
+Copyright (C) 2023-2025 Free Software Foundation, Inc.
+Contributed by Gaius Mulley <gaius.mulley@southwales.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/>. *)
+
IMPLEMENTATION MODULE PathName ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
diff --git a/gcc/m2/gm2-lang.cc b/gcc/m2/gm2-lang.cc
index 31a2e46..d378d1b 100644
--- a/gcc/m2/gm2-lang.cc
+++ b/gcc/m2/gm2-lang.cc
@@ -51,6 +51,7 @@ static bool iso = false;
typedef struct named_path_s {
std::vector<const char*>path;
const char *name;
+ bool lib_root;
} named_path;
@@ -372,6 +373,7 @@ push_back_Ipath (const char *arg)
named_path np;
np.path.push_back (arg);
np.name = xstrdup (M2Options_GetM2PathName ());
+ np.lib_root = false;
Ipaths.push_back (np);
}
else
@@ -384,11 +386,205 @@ push_back_Ipath (const char *arg)
named_path np;
np.path.push_back (arg);
np.name = xstrdup (M2Options_GetM2PathName ());
+ np.lib_root = false;
Ipaths.push_back (np);
}
}
}
+/* push_back_lib_root pushes a lib_root onto the Ipaths vector.
+ The ordering of the -fm2_add_lib_root=, -I and named paths
+ must be preserved. */
+
+static void
+push_back_lib_root (const char *arg)
+{
+ named_path np;
+ np.name = arg;
+ np.lib_root = true;
+ Ipaths.push_back (np);
+}
+
+/* get_dir_sep_size return the length of the DIR_SEPARATOR string. */
+
+static size_t
+get_dir_sep_size (void)
+{
+ const char dir_sep[] = {DIR_SEPARATOR, (char)0};
+ size_t dir_sep_size = strlen (dir_sep);
+ return dir_sep_size;
+}
+
+/* add_path_component strcats src into dest and adds a directory seperator
+ if necessary. */
+
+static void
+add_path_component (char *dest, const char *src)
+{
+ size_t len = strlen (dest);
+ const char dir_sep[] = {DIR_SEPARATOR, (char)0};
+ size_t dir_sep_size = strlen (dir_sep);
+
+ if (len > 0)
+ {
+ /* Only add a seperator if dest is not empty and does not end
+ with a seperator. */
+ if (len >= dir_sep_size
+ && (strcmp (&dest[len-dir_sep_size], dir_sep) != 0))
+ strcat (dest, dir_sep);
+ }
+ strcat (dest, src);
+}
+
+/* This prefixes LIBNAME with the current compiler prefix (if it has been
+ relocated) or the LIBSUBDIR, if not. */
+
+static void
+add_one_import_path (const char *libpath, const char *libname)
+{
+ size_t dir_sep_size = get_dir_sep_size ();
+ size_t mlib_len = 0;
+
+ if (imultilib)
+ {
+ mlib_len = strlen (imultilib);
+ mlib_len += dir_sep_size;
+ }
+
+ char *lib = (char *)alloca (strlen (libpath) + dir_sep_size
+ + strlen ("m2") + dir_sep_size
+ + strlen (libname) + 1
+ + mlib_len + 1);
+ strcpy (lib, libpath);
+ if (imultilib)
+ add_path_component (lib, imultilib);
+ add_path_component (lib, "m2");
+ add_path_component (lib, libname);
+ M2Options_SetM2PathName (libname);
+ M2Options_SetSearchPath (lib);
+}
+
+/* add_non_dialect_specific_path add non dialect specific includes
+ given a base libpath. */
+
+static void
+add_non_dialect_specific_path (const char *libpath)
+{
+ char *incpath = (char *)alloca (strlen (libpath)
+ + strlen ("m2")
+ + get_dir_sep_size ()
+ + 1);
+ strcpy (incpath, libpath);
+ add_path_component (incpath, "m2");
+ M2Options_SetM2PathName (""); /* No pathname for non dialect specific libs. */
+ M2Options_SetSearchPath (incpath);
+}
+
+/* For each comma-separated standard library name in LIBLIST, add the
+ corresponding include path. */
+
+static void
+foreach_lib_gen_import_path (const char *liblist, const char *libpath)
+{
+ while (*liblist != 0 && *liblist != '-')
+ {
+ const char *comma = strstr (liblist, ",");
+ size_t len;
+ if (comma)
+ len = comma - liblist;
+ else
+ len = strlen (liblist);
+ char *libname = (char *) alloca (len+1);
+ strncpy (libname, liblist, len);
+ libname[len] = 0;
+ add_one_import_path (libpath, libname);
+ liblist += len;
+ if (*liblist == ',')
+ liblist++;
+ }
+ add_non_dialect_specific_path (libpath);
+}
+
+/* get_module_source_dir return the libpath/{multilib/} as a malloc'd
+ string. */
+
+static const char *
+get_module_source_dir (void)
+{
+ const char *libpath = iprefix ? iprefix : LIBSUBDIR;
+ const char dir_sep[] = {DIR_SEPARATOR, (char)0};
+ size_t dir_sep_size = strlen (dir_sep);
+ unsigned int mlib_len = 0;
+
+ if (imultilib)
+ {
+ mlib_len = strlen (imultilib);
+ mlib_len += strlen (dir_sep);
+ }
+ char *lib = (char *) xmalloc (strlen (libpath)
+ + dir_sep_size
+ + mlib_len + 1);
+ strcpy (lib, libpath);
+ /* iprefix has a trailing dir separator, LIBSUBDIR does not. */
+ if (!iprefix)
+ strcat (lib, dir_sep);
+
+ if (imultilib)
+ {
+ strcat (lib, imultilib);
+ strcat (lib, dir_sep);
+ }
+ return lib;
+}
+
+/* add_default_include_paths add include paths for site wide definition modules
+ and also gcc version specific definition modules. */
+
+static void
+add_default_include_paths (const char *flibs)
+{
+ /* Add the site wide include path. */
+ foreach_lib_gen_import_path (flibs, PREFIX_INCLUDE_DIR);
+ /* Add the gcc version specific include path. */
+ foreach_lib_gen_import_path (flibs,
+ get_module_source_dir ());
+}
+
+/* assign_flibs assign flibs to a default providing that allow_libraries
+ is true and flibs has not been set. */
+
+static void
+assign_flibs (void)
+{
+ if (allow_libraries && (flibs == NULL))
+ {
+ if (iso)
+ flibs = "m2iso,m2cor,m2pim,m2log";
+ else
+ flibs = "m2pim,m2iso,m2cor,m2log";
+ }
+}
+
+/* m2_pathname_root creates a new set of include paths for the
+ subdirectory m2 inside libroot. The ordering of the paths
+ follows the dialect library order. */
+
+static void
+m2_pathname_root (const char *libroot)
+{
+ const char *copy_flibs = flibs;
+
+ if (copy_flibs == NULL)
+ {
+ if (iso)
+ copy_flibs = "m2iso,m2cor,m2pim,m2log";
+ else
+ copy_flibs = "m2pim,m2iso,m2cor,m2log";
+ }
+ foreach_lib_gen_import_path (copy_flibs, libroot);
+}
+
+
/* Handle gm2 specific options. Return 0 if we didn't do anything. */
bool
@@ -435,6 +631,9 @@ gm2_langhook_handle_option (
case OPT_fpositive_mod_floor_div:
M2Options_SetPositiveModFloor (value);
return 1;
+ case OPT_fm2_pathname_rootI_:
+ push_back_lib_root (arg);
+ return 1;
case OPT_flibs_:
allow_libraries = value;
flibs = arg;
@@ -710,66 +909,6 @@ gm2_langhook_handle_option (
return 0;
}
-/* This prefixes LIBNAME with the current compiler prefix (if it has been
- relocated) or the LIBSUBDIR, if not. */
-static void
-add_one_import_path (const char *libname)
-{
- const char *libpath = iprefix ? iprefix : LIBSUBDIR;
- const char dir_sep[] = {DIR_SEPARATOR, (char)0};
- size_t dir_sep_size = strlen (dir_sep);
- unsigned int mlib_len = 0;
-
- if (imultilib)
- {
- mlib_len = strlen (imultilib);
- mlib_len += strlen (dir_sep);
- }
-
- char *lib = (char *)alloca (strlen (libpath) + dir_sep_size
- + strlen ("m2") + dir_sep_size
- + strlen (libname) + 1
- + mlib_len + 1);
- strcpy (lib, libpath);
- /* iprefix has a trailing dir separator, LIBSUBDIR does not. */
- if (!iprefix)
- strcat (lib, dir_sep);
-
- if (imultilib)
- {
- strcat (lib, imultilib);
- strcat (lib, dir_sep);
- }
- strcat (lib, "m2");
- strcat (lib, dir_sep);
- strcat (lib, libname);
- M2Options_SetM2PathName (libname);
- M2Options_SetSearchPath (lib);
-}
-
-/* For each comma-separated standard library name in LIBLIST, add the
- corresponding include path. */
-static void
-add_m2_import_paths (const char *liblist)
-{
- while (*liblist != 0 && *liblist != '-')
- {
- const char *comma = strstr (liblist, ",");
- size_t len;
- if (comma)
- len = comma - liblist;
- else
- len = strlen (liblist);
- char *libname = (char *) alloca (len+1);
- strncpy (libname, liblist, len);
- libname[len] = 0;
- add_one_import_path (libname);
- liblist += len;
- if (*liblist == ',')
- liblist++;
- }
-}
-
/* Run after parsing options. */
static bool
@@ -784,16 +923,7 @@ gm2_langhook_post_options (const char **pfilename)
/* Add the include paths as per the libraries specified.
NOTE: This assumes that the driver has validated the input and makes
no attempt to be defensive of nonsense input in flibs=. */
- if (allow_libraries)
- {
- if (!flibs)
- {
- if (iso)
- flibs = "m2iso,m2cor,m2pim,m2log";
- else
- flibs = "m2pim,m2iso,m2cor,m2log";
- }
- }
+ assign_flibs ();
/* Add search paths.
We are not handling all of the cases yet (e.g idirafter).
@@ -807,9 +937,14 @@ gm2_langhook_post_options (const char **pfilename)
iquote.clear();
for (auto np : Ipaths)
{
- M2Options_SetM2PathName (np.name);
- for (auto *s : np.path)
- M2Options_SetSearchPath (s);
+ if (np.lib_root)
+ foreach_lib_gen_import_path (flibs, np.name);
+ else
+ {
+ M2Options_SetM2PathName (np.name);
+ for (auto *s : np.path)
+ M2Options_SetSearchPath (s);
+ }
}
Ipaths.clear();
for (auto *s : isystem)
@@ -818,7 +953,7 @@ gm2_langhook_post_options (const char **pfilename)
/* FIXME: this is not a good way to suppress the addition of the import
paths. */
if (allow_libraries)
- add_m2_import_paths (flibs);
+ add_default_include_paths (flibs);
/* Returning false means that the backend should be used. */
return M2Options_GetPPOnly ();
diff --git a/gcc/m2/gm2spec.cc b/gcc/m2/gm2spec.cc
index 868e5c5..18d9ce7 100644
--- a/gcc/m2/gm2spec.cc
+++ b/gcc/m2/gm2spec.cc
@@ -158,10 +158,12 @@ static const char *m2_path_name = "";
typedef struct named_path_s {
std::vector<const char*>path;
const char *name;
+ bool lib_root;
} named_path;
static std::vector<named_path>Ipaths;
+/* push_back_Ipath pushes a named path to the Ipaths global variable. */
static void
push_back_Ipath (const char *arg)
@@ -171,6 +173,7 @@ push_back_Ipath (const char *arg)
named_path np;
np.path.push_back (arg);
np.name = m2_path_name;
+ np.lib_root = false;
Ipaths.push_back (np);
}
else
@@ -183,11 +186,25 @@ push_back_Ipath (const char *arg)
named_path np;
np.path.push_back (arg);
np.name = m2_path_name;
+ np.lib_root = false;
Ipaths.push_back (np);
}
}
}
+/* push_back_lib_root pushes a lib_root onto the Ipaths vector.
+ The ordering of the -fm2_add_lib_root=, -I and named paths
+ must be preserved. */
+
+static void
+push_back_lib_root (const char *arg)
+{
+ named_path np;
+ np.name = arg;
+ np.lib_root = true;
+ Ipaths.push_back (np);
+}
+
/* Return whether strings S1 and S2 are both NULL or both the same
string. */
@@ -379,15 +396,18 @@ convert_abbreviations (const char *libraries)
return full_libraries;
}
-/* add_m2_I_path appends -fm2-pathname and -fm2-pathnameI options to
- the command line which are contructed in the saved Ipaths. */
+/* add_m2_I_path appends -fm2-pathname, -fm2-pathnameI and -fm2-add-lib-root
+ options to the command line which are contructed in the saved Ipaths.
+ The order of these options must be maintained. */
static void
add_m2_I_path (void)
{
for (auto np : Ipaths)
{
- if (strcmp (np.name, "") == 0)
+ if (np.lib_root)
+ append_option (OPT_fm2_pathname_rootI_, safe_strdup (np.name), 1);
+ else if (strcmp (np.name, "") == 0)
append_option (OPT_fm2_pathname_, safe_strdup ("-"), 1);
else
append_option (OPT_fm2_pathname_, safe_strdup (np.name), 1);
@@ -576,6 +596,10 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
args[i] |= SKIPOPT; /* We will add the option if it is needed. */
m2_path_name = decoded_options[i].arg;
break;
+ case OPT_fm2_pathname_root_:
+ args[i] |= SKIPOPT; /* We will add the option if it is needed. */
+ push_back_lib_root (decoded_options[i].arg);
+ break;
case OPT__help:
case OPT__help_:
/* Let gcc.cc handle this, as it has a really
@@ -739,7 +763,6 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
"-fgen-module-list=", "-fuse-list=");
}
-
/* There's no point adding -shared-libgcc if we don't have a shared
libgcc. */
#ifndef ENABLE_SHARED_LIBGCC
diff --git a/gcc/m2/lang.opt b/gcc/m2/lang.opt
index 48c2380..2aea4cc 100644
--- a/gcc/m2/lang.opt
+++ b/gcc/m2/lang.opt
@@ -172,7 +172,15 @@ specify the module mangled prefix name for all modules in the following include
fm2-pathnameI
Modula-2 Joined
-; For internal use only: used by the driver to copy the user facing -I option
+; For internal use only: used by the driver to copy the user facing -I option in order
+
+fm2-pathname-root=
+Modula-2 Joined
+add include paths for all the library names in -flibs= to this directory root
+
+fm2-pathname-rootI=
+Modula-2 Joined
+; For internal use only: used by the driver to copy the user facing -I option in order
fm2-plugin
Modula-2
diff --git a/gcc/testsuite/gm2/switches/pathnameroot/pass/switches-pathnameroot-pass.exp b/gcc/testsuite/gm2/switches/pathnameroot/pass/switches-pathnameroot-pass.exp
new file mode 100755
index 0000000..d2f4d87
--- /dev/null
+++ b/gcc/testsuite/gm2/switches/pathnameroot/pass/switches-pathnameroot-pass.exp
@@ -0,0 +1,48 @@
+# Expect driver script for GCC Regression Tests
+# Copyright (C) 2025 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# This file was written by Gaius Mulley (gaius.mulley@southwales.ac.uk)
+# for GNU Modula-2.
+
+load_lib target-supports.exp
+
+global TESTING_IN_BUILD_TREE
+global ENABLE_PLUGIN
+
+# The plugin testcases currently only work when the build tree is available.
+# Also check whether the host supports plugins.
+if { ![info exists TESTING_IN_BUILD_TREE] || ![info exists ENABLE_PLUGIN] } {
+ return
+}
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+# load support procs
+load_lib gm2-torture.exp
+
+gm2_init_pim "${srcdir}/gm2/switches/pathnameroot/pass" -fm2-pathname-root="${srcdir}/gm2/switches/pathnameroot/pass/testlib"
+
+foreach testcase [lsort [glob -nocomplain $srcdir/$subdir/*.mod]] {
+ # If we're only testing specific files and this isn't one of them, skip it.
+ if ![runtest_file_p $runtests $testcase] then {
+ continue
+ }
+
+ gm2-torture-fail $testcase
+}
diff --git a/gcc/testsuite/gm2/switches/pathnameroot/pass/test.mod b/gcc/testsuite/gm2/switches/pathnameroot/pass/test.mod
new file mode 100644
index 0000000..0f9cd6f
--- /dev/null
+++ b/gcc/testsuite/gm2/switches/pathnameroot/pass/test.mod
@@ -0,0 +1,6 @@
+MODULE test ;
+
+FROM foo IMPORT SomeValue ;
+
+BEGIN
+END test.
diff --git a/gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.def b/gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.def
new file mode 100644
index 0000000..9fa4973
--- /dev/null
+++ b/gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.def
@@ -0,0 +1,7 @@
+DEFINITION MODULE foo ;
+
+CONST
+ SomeValue = 123 ;
+
+
+END foo.
diff --git a/gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.mod b/gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.mod
new file mode 100644
index 0000000..fcbcf1e
--- /dev/null
+++ b/gcc/testsuite/gm2/switches/pathnameroot/pass/testlib/m2/foo.mod
@@ -0,0 +1,3 @@
+IMPLEMENTATION MODULE foo ;
+
+END foo.