Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Dec 2001 20:29:25 +0200
From:      Peter Pentchev <roam@ringlet.net>
To:        arch@FreeBSD.org
Subject:   make(1) enhancement - an 'environment processor' option
Message-ID:  <20011225202925.F304@straylight.oblivion.bg>

next in thread | raw e-mail | index | archive | help
Hi,

<BACKGROUND STYLE="shameless plug">
As some of you may have noticed, I maintain a little utility that
keeps environment variables on a per-directory basis - the sysutils/penv
port.  Basically, what it does is remember what variables you want set
for a specific directory and then hand them in the environment of any
program you want to run.  This may be quite useful for ports, especially
those with lots of tweakable knobs, like vpopmail or MySQL, since it won't
let you miss one of those knobs the next time you rebuild the port.

However, there is one little problem with penv - it only sets
the environment based on the data for the current directory.  If a port
has dependencies, and some of them also have knobs, you either have to
set them for the port you want to build, or you have to build
the dependency separately.  For example, I want Ghostscript to build
with an A4 output format, but then I want to build Ghostscript as part
of the textproc/docproj build.  If I set 'A4=yes' in the print/ghostscript-gnu
directory and then I do a 'make' in the textproc/docproj directory,
penv and make(1) will only pick up the textproc/docproj settings, and
this A4=yes will be lost.  If only there was a way to let make(1) read
per-directory information for every single directory it recursed into..
</BACKGROUND>

Here is a little patch to make make(1) do just that - invoke an external
'environment processor', read its output and modify its own environment
before doing anything else.  This way, I can keep the A4=yes in the penv
settings for the print/ghostscript-gnu directory and rest assured that
a docproj build will pick it right up.

This, incidentally, removes the need for the 'penv' invocation to be
visible at all.  Whereas before one had to run 'penv make all install',
now all one has to do is run 'make all install', and make(1) will run
penv all by itself.

For those worried about the overhead of a program invocation, there is
the MAKEENVDIR environment variable, which, if specified, is treated
as an extended regular expression for the directory names to run
the environment processor in.  Thus, if MAKEENVDIR is set to something
like, say, "^(/usr|/home/roam/fbsd)/ports/", then the environment
processor will only be run in the Ports collection and not at all during
a buildworld.

Comments?  Flames?  Instant shootdowns? :)

G'luck,
Peter

-- 
.siht ekil ti gnidaer eb d'uoy ,werbeH ni erew ecnetnes siht fI

Index: src/usr.bin/make/main.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/make/main.c,v
retrieving revision 1.49
diff -u -r1.49 main.c
--- src/usr.bin/make/main.c	25 Apr 2001 14:44:41 -0000	1.49
+++ src/usr.bin/make/main.c	25 Dec 2001 17:37:25 -0000
@@ -88,6 +88,7 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <regex.h>
 #include <stdio.h>
 #include <sysexits.h>
 #ifdef __STDC__
@@ -139,6 +140,8 @@
 static void		MainParseArgs __P((int, char **));
 char *			chdir_verify_path __P((char *, char *));
 static int		ReadMakefile __P((void *, void *));
+static void		ReadEnvProc __P((const char *, const char *,
+			const char *));
 static void		usage __P((void));
 
 static char *curdir;			/* startup directory */
@@ -428,6 +431,81 @@
 	return 0;
 }
 
+/*-
+ * ReadEnvProc --
+ *	Read the output of an environment processor program and
+ *	set or unset environment variables accordingly.
+ *
+ * Results:
+ *	none
+ *
+ * Side effects:
+ *	Executes the program specified by the envproc argument and
+ *	sets or unsets environment variables according to the program output.
+ */
+
+static void
+ReadEnvProc(const char *envproc, const char *envprocdir, const char *curdir) {
+	regex_t regdir;
+	int r;
+	char regerr[128];
+	FILE *envout;
+	char *s, *new;
+	size_t len;
+	
+	/* First check if the env processor needs to run at all here */
+	if ((envprocdir != NULL) && (envprocdir[0] != '\0')) {
+		memset(&regdir, 0, sizeof(regdir));
+		r = regcomp(&regdir, envprocdir, REG_EXTENDED | REG_NOSUB);
+		if (r != 0) {
+			regerror(r, &regdir, regerr, sizeof(regerr));
+			errx(1, "parsing regexp %s: %s", envprocdir, regerr);
+		}
+
+		/* If curdir does not match, return w/o executing anything */
+		r = regexec(&regdir, curdir, 0, NULL, 0);
+		if (r == REG_NOMATCH)
+			return;
+		if (r != 0) {
+			regerror(r, &regdir, regerr, sizeof(regerr));
+			errx(1, "matching regexp %s against %s: %s",
+			    envprocdir, curdir, regerr);
+		}
+	}
+
+	if ((envout = popen(envproc, "r")) == NULL)
+		err(1, "invoking environment processor '%s'", envproc);
+
+	while ((s = fgetln(envout, &len)) != NULL) {
+		/* Null-terminate as needed */
+		if (s[len - 1] == '\n') {
+			s[len - 1] = '\0';
+			new = NULL;
+		} else {
+			if ((new = realloc(s, len + 1)) == NULL)
+				err(1, "reading env processor output");
+			memcpy(new, s, len);
+			new[len] = '\0';
+			s = new;
+		}
+		
+		/*
+		 * A 'var=value' specification sets the variable, while
+		 * a mere variable name unsets it.
+		 */
+		if (strchr(s, '=') != NULL)
+			putenv(s);
+		else
+			unsetenv(s);
+		
+		if (new != NULL)
+			free(new);
+	}
+	if (ferror(envout))
+		err(1, "reading env processor output");
+
+	pclose(envout);
+}
 
 /*-
  * main --
@@ -465,6 +543,8 @@
 	char *cp = NULL, *start;
 					/* avoid faults on read-only strings */
 	static char syspath[] = _PATH_DEFSYSPATH;
+	char *envproc = getenv("MAKEENVPROC");
+	char *envprocdir = getenv("MAKEENVDIR");
 
 #if DEFSHELL == 2
 	/*
@@ -497,6 +577,12 @@
 
 	if (stat(curdir, &sa) == -1)
 	    err(2, "%s", curdir);
+
+	/*
+	 * Look for an environment processor as early as possible
+	 */
+	if ((envproc != NULL) && (envproc[0] != '\0'))
+		ReadEnvProc(envproc, envprocdir, curdir);
 
 #if defined(__i386__) && defined(__FreeBSD_version) && \
     __FreeBSD_version > 300003
Index: src/usr.bin/make/make.1
===================================================================
RCS file: /home/ncvs/src/usr.bin/make/make.1,v
retrieving revision 1.48
diff -u -r1.48 make.1
--- src/usr.bin/make/make.1	10 Aug 2001 13:45:27 -0000	1.48
+++ src/usr.bin/make/make.1	25 Dec 2001 17:40:39 -0000
@@ -32,7 +32,7 @@
 .\"	from: @(#)make.1	8.4 (Berkeley) 3/19/94
 .\" $FreeBSD$
 .\"
-.Dd March 19, 1994
+.Dd December 25, 2001
 .Dt MAKE 1
 .Os
 .Sh NAME
@@ -373,7 +373,9 @@
 .It Environment variables
 Variables defined as part of
 .Nm Ns 's
-environment.
+environment or in the output of the
+.Ev MAKEENVPROC
+program, if specified (see the ENVIRONMENT PROCESSORS section below).
 .It Global variables
 Variables defined in the makefile or in included makefiles.
 .It Command line variables
@@ -716,6 +718,47 @@
 .It Cm U
 Converts variable to upper-case letters.
 .El
+.Sh ENVIRONMENT PROCESSORS
+External programs, called
+.Dq environment processors ,
+may provide
+additional environment data before any Makefile parsing is done.
+If the
+.Ev MAKEENVPROC
+environment variable is set before
+.Nm
+is invoked, it is interpreted as a command to obtain additional environment
+variables from.
+.Nm
+executes the command using the
+.Xr popen 3
+function and examines each line of its output.
+If the line contains a
+.Sq =
+character, it is treated as a
+.Ar variable Ns No = Ns Ar value
+assignment operator and
+.Nm
+sets the respective variable in its environment to the specified value.
+If the line does not contain a
+.Sq =
+character, it is treated as the name of a variable to be removed from the
+.Nm
+environment.
+.Pp
+If the
+.Ev MAKEENVDIR
+environment variable is also set,
+.Nm
+treats it as an extended regular expression (see
+.Xr re_format 7 )
+and matches the current directory against it.
+If there is no match, the environment processor is not executed at all.
+This allows for running the processor only in certain directory trees, e.g.
+.Pa /usr/ports ,
+without the burden of the additional command execution when running
+.Nm
+in other directories.
 .Sh DIRECTIVES, CONDITIONALS, AND FOR LOOPS
 Directives, conditionals, and for loops reminiscent
 of the C programming language are provided in
@@ -1174,6 +1217,8 @@
 uses the following environment variables, if they exist:
 .Ev MACHINE ,
 .Ev MAKE ,
+.Ev MAKEENVDIR ,
+.Ev MAKEENVPROC ,
 .Ev MAKEFLAGS ,
 .Ev MAKEOBJDIR ,
 and

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20011225202925.F304>