summaryrefslogtreecommitdiff
path: root/load.c
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2012-10-29 07:05:21 +0000
committerPaul Smith <psmith@gnu.org>2012-10-29 07:05:21 +0000
commit7670c84f7732db29f5a9d9c145c2327f4b575f91 (patch)
tree1fafd8949c867b89444ef57c6d068e7b8fb1a70e /load.c
parent2efd6b47bf59c3202ccc6218b42ba360136d3789 (diff)
downloadgunmake-7670c84f7732db29f5a9d9c145c2327f4b575f91.tar.gz
Implement new "load" directive.
Provides support for dynamically loadable objects in GNU make, as a "technology preview".
Diffstat (limited to 'load.c')
-rw-r--r--load.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/load.c b/load.c
new file mode 100644
index 0000000..076dd30
--- /dev/null
+++ b/load.c
@@ -0,0 +1,157 @@
+/* Loading dynamic objects for GNU Make.
+Copyright (C) 2012 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make 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.
+
+GNU Make 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
+this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "make.h"
+
+#if MAKE_LOAD
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+#define SYMBOL_EXTENSION "_gmake_setup"
+
+static void *global_dl = NULL;
+
+#include "debug.h"
+#include "filedef.h"
+#include "variable.h"
+
+static int
+init_symbol (const struct floc *flocp, const char *ldname, load_func_t symp)
+{
+ int r;
+ const char *p;
+ int nmlen = strlen (ldname);
+ char *loaded = allocated_variable_expand("$(.LOADED)");
+
+ /* If it's already been loaded don't do it again. */
+ p = strstr (loaded, ldname);
+ r = p && (p==loaded || p[-1]==' ') && (p[nmlen]=='\0' || p[nmlen]==' ');
+ free (loaded);
+ if (r)
+ return 1;
+
+ /* Now invoke the symbol. */
+ r = (*symp) (flocp);
+
+ /* If it succeeded, add the symbol to the loaded variable. */
+ if (r > 0)
+ do_variable_definition (flocp, ".LOADED", ldname, o_default, f_append, 0);
+
+ return r;
+}
+
+int
+load_file (const struct floc *flocp, const char *ldname, int noerror)
+{
+ load_func_t symp;
+ const char *fp;
+ char *symname = NULL;
+ char *new = alloca (strlen (ldname) + CSTRLEN (SYMBOL_EXTENSION) + 1);
+
+ if (! global_dl)
+ {
+ global_dl = dlopen (NULL, RTLD_NOW|RTLD_GLOBAL);
+ if (! global_dl)
+ fatal (flocp, _("Failed to open global symbol table: %s"), dlerror());
+ }
+
+ /* If a symbol name was provided, use it. */
+ fp = strchr (ldname, '(');
+ if (fp)
+ {
+ const char *ep;
+
+ /* If there's an open paren, see if there's a close paren: if so use
+ that as the symbol name. We can't have whitespace: it would have
+ been chopped up before this function is called. */
+ ep = strchr (fp+1, ')');
+ if (ep && ep[1] == '\0')
+ {
+ int l = fp - ldname;;
+
+ ++fp;
+ if (fp == ep)
+ fatal (flocp, _("Empty symbol name for load: %s"), ldname);
+
+ /* Make a copy of the ldname part. */
+ memcpy (new, ldname, l);
+ new[l] = '\0';
+ ldname = new;
+
+ /* Make a copy of the symbol name part. */
+ symname = new + l + 1;
+ memcpy (symname, fp, ep - fp);
+ symname[ep - fp] = '\0';
+ }
+ }
+
+ /* If we didn't find a symbol name yet, construct it from the ldname. */
+ if (! symname)
+ {
+ char *p = new;
+
+ fp = strrchr (ldname, '/');
+ if (!fp)
+ fp = ldname;
+ else
+ ++fp;
+ while (isalnum (*fp) || *fp == '_')
+ *(p++) = *(fp++);
+ strcpy (p, SYMBOL_EXTENSION);
+ symname = new;
+ }
+
+ DB (DB_VERBOSE, (_("Loading symbol %s from %s\n"), symname, ldname));
+
+ /* See if it's already defined. */
+ symp = (load_func_t) dlsym (global_dl, symname);
+ if (! symp) {
+ void *dlp = dlopen (ldname, RTLD_LAZY|RTLD_GLOBAL);
+ if (! dlp)
+ {
+ if (noerror)
+ DB (DB_BASIC, ("%s", dlerror()));
+ else
+ error (flocp, "%s", dlerror());
+ return 0;
+ }
+
+ symp = dlsym (dlp, symname);
+ if (! symp)
+ fatal (flocp, _("Failed to load symbol %s from %s: %s"),
+ symname, ldname, dlerror());
+ }
+
+ /* Invoke the symbol to initialize the loaded object. */
+ return init_symbol(flocp, ldname, symp);
+}
+
+#else
+
+int
+load_file (const struct floc *flocp, const char *ldname, int noerror)
+{
+ if (! noerror)
+ fatal (flocp, _("The 'load' operation is not supported on this platform."));
+
+ return 0;
+}
+
+#endif /* MAKE_LOAD */