Subject: Re: fix for MAKEOBJDIRPREFIX=/obj make build
To: None <tech-toolchain@netbsd.org>
From: Simon J. Gerraty <sjg@quick.com.au>
List: tech-toolchain
Date: 03/30/2000 17:19:17
Sorry for letting this slide, below are the diffs for the version I
settled on. It works with and without -jN. I've happily done build
world using MAKEOBJDIRPREFIX and a make with this patch.
Please note that this change does _nothing_ if MAKEOBJDIRPREFIX and
MAKEOBJDIR are not set and defining NOCHECKMAKECHDIR turns it off
regardless.
Also note that this feature does NOT break situations where a
configure script builds a makefile in the objdir.
Original description:
> Attempting to do a make build with MAKEOBJDIRPREFIX set (so that the
> src tree can remain read-only) fails utterly when it encounters:
>
> ${MAKE} cleandir
> ${MAKE} includes
>
> etc. This is due to the fact that unlike ./obj* (links or not), even
> if a directory says NOOBJ, if any of its children don't then
> MAKEOBJDIRPREFX`pwd` will exist and make(1) will have cd'd to it.
> This means that the ${MAKE} includes is run in a directory with no
> Makefile.
>
> Changing the above to:
>
> cd ${.CURDIR} && ${MAKE} cleandir
>
> works fine, but its bogus to expect everyone to fix their makefiles.
>
> The fix below, only applies if MAKEOBJDIRPREFIX or MAKEOBJDIR is set.
> What it does is spot if ${.MAKE} or ${.MAKE:T} are invoked
> without a preceeding 'cd' and if so does a chdir(${.CURDIR}). This
> only happens in the child just before exec(2), so does not affect the
> parent.
Anyway here's the patch. I've been running this on 1.3.2, 1.4.x and
-current for some weeks now without any problems. And done several
make builds of -current with MAKEOBJDIRPREFIX set.
--sjg
Index: main.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/make/main.c,v
retrieving revision 1.51
diff -u -p -r1.51 main.c
--- main.c 2000/02/08 12:43:25 1.51
+++ main.c 2000/03/30 07:02:39
@@ -140,6 +140,7 @@ Boolean checkEnvFirst; /* -e flag */
Boolean mkIncPath; /* -m flag */
static Boolean jobsRunning; /* TRUE if the jobs might be running */
+static char * Check_Cwd_av __P((int, char **, int));
static void MainParseArgs __P((int, char **));
char * chdir_verify_path __P((char *, char *));
static int ReadMakefile __P((ClientData, ClientData));
@@ -751,6 +752,9 @@ main(argc, argv)
if (p1)
free(p1);
+ Check_Cwd_av(0, NULL, 0); /* initialize it */
+
+
/*
* For compatibility, look at the directories in the VPATH variable
* and add them to the search path, if the variable is defined. The
@@ -930,6 +934,181 @@ found: Var_Set("MAKEFILE", fname, VAR_G
}
free(path);
return(TRUE);
+}
+
+
+/*
+ * If MAKEOBJDIRPREFIX is in use, make ends up not in .CURDIR
+ * in situations that would not arrise with ./obj (links or not).
+ * This tends to break things like:
+ *
+ * build:
+ * ${MAKE} includes
+ *
+ * This function spots when ${.MAKE:T} or ${.MAKE} is a command (as
+ * opposed to an argument) in a command line and if so returns
+ * ${.CURDIR} so caller can chdir() so that the assumptions made by
+ * the Makefile hold true.
+ *
+ * If ${.MAKE} does not contain any '/', then ${.MAKE:T} is skipped.
+ *
+ * The chdir() only happens in the child process, and does nothing if
+ * MAKEOBJDIRPREFIX and MAKEOBJDIR are not in the environment so it
+ * should not break anything. Also if NOCHECKMAKECHDIR is set we
+ * do nothing - to ensure historic semantics can be retained.
+ */
+static int Check_Cwd_Off = 0;
+
+static char *
+Check_Cwd_av(ac, av, copy)
+ int ac;
+ char **av;
+ int copy;
+{
+ static char *make[4];
+ static char *curdir = NULL;
+ char *cp, **mp;
+ int is_cmd, next_cmd;
+ int i;
+ int n;
+
+ if (Check_Cwd_Off)
+ return NULL;
+
+ if (make[0] == NULL) {
+ if (Var_Exists("NOCHECKMAKECHDIR", VAR_GLOBAL)) {
+ Check_Cwd_Off = 1;
+ return NULL;
+ }
+
+ make[1] = Var_Value(".MAKE", VAR_GLOBAL, &cp);
+ if ((make[0] = strrchr(make[1], '/')) == NULL) {
+ make[0] = make[1];
+ make[1] = NULL;
+ } else
+ ++make[0];
+ make[2] = NULL;
+ curdir = Var_Value(".CURDIR", VAR_GLOBAL, &cp);
+ }
+ if (ac == 0 || av == NULL)
+ return NULL; /* initialization only */
+
+ if (getenv("MAKEOBJDIR") == NULL &&
+ getenv("MAKEOBJDIRPREFIX") == NULL)
+ return NULL;
+
+
+ next_cmd = 1;
+ for (i = 0; i < ac; ++i) {
+ is_cmd = next_cmd;
+
+ n = strlen(av[i]);
+ cp = &(av[i])[n - 1];
+ if (strspn(av[i], "|&;") == n) {
+ next_cmd = 1;
+ continue;
+ } else if (*cp == ';' || *cp == '&' || *cp == '|' || *cp == ')') {
+ next_cmd = 1;
+ if (copy) {
+ do {
+ *cp-- = '\0';
+ } while (*cp == ';' || *cp == '&' || *cp == '|' ||
+ *cp == ')' || *cp == '}') ;
+ } else {
+ /*
+ * XXX this should not happen.
+ */
+ fprintf(stderr, "WARNING: raw arg ends in shell meta '%s'\n",
+ av[i]);
+ }
+ } else
+ next_cmd = 0;
+
+ cp = av[i];
+ if (*cp == ';' || *cp == '&' || *cp == '|')
+ is_cmd = 1;
+
+#ifdef check_cwd_debug
+ fprintf(stderr, "av[%d] == %s '%s'",
+ i, (is_cmd) ? "cmd" : "arg", av[i]);
+#endif
+ if (is_cmd != 0) {
+ while (*cp == '(' || *cp == '{' ||
+ *cp == ';' || *cp == '&' || *cp == '|')
+ ++cp;
+ if (strcmp(cp, "cd") == 0 || strcmp(cp, "chdir") == 0) {
+#ifdef check_cwd_debug
+ fprintf(stderr, " == cd, done.\n");
+#endif
+ return NULL;
+ }
+ for (mp = make; *mp != NULL; ++mp) {
+ n = strlen(*mp);
+ if (strcmp(cp, *mp) == 0) {
+#ifdef check_cwd_debug
+ fprintf(stderr, " %s == '%s', chdir(%s)\n",
+ cp, *mp, curdir);
+#endif
+ return curdir;
+ }
+ }
+ }
+#ifdef check_cwd_debug
+ fprintf(stderr, "\n");
+#endif
+ }
+ return NULL;
+}
+
+char *
+Check_Cwd_Cmd(cmd)
+ char *cmd;
+{
+ char *cp, *bp, **av;
+ int ac;
+
+ if (Check_Cwd_Off)
+ return NULL;
+
+ if (cmd) {
+ av = brk_string(cmd, &ac, TRUE, &bp);
+#ifdef check_cwd_debug
+ fprintf(stderr, "splitting: '%s' -> %d words\n",
+ cmd, ac);
+#endif
+ } else {
+ ac = 0;
+ av = NULL;
+ bp = NULL;
+ }
+ cp = Check_Cwd_av(ac, av, 1);
+ if (bp) {
+ free(av);
+ free(bp);
+ }
+ return cp;
+}
+
+void
+Check_Cwd(argv)
+ char **argv;
+{
+ char *cp;
+ int ac;
+
+ if (Check_Cwd_Off)
+ return;
+
+ for (ac = 0; argv[ac] != NULL; ++ac)
+ /* NOTHING */;
+ if (ac == 3 && *argv[1] == '-') {
+ cp = Check_Cwd_Cmd(argv[2]);
+ } else {
+ cp = Check_Cwd_av(ac, argv, 0);
+ }
+ if (cp) {
+ chdir(cp);
+ }
}
/*-
Index: make.h
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/make/make.h,v
retrieving revision 1.23
diff -u -p -r1.23 make.h
--- make.h 1999/09/16 19:57:54 1.23
+++ make.h 2000/03/30 07:02:43
@@ -395,5 +395,7 @@ int Make_HandleUse __P((GNode *, GNode *
void Make_Update __P((GNode *));
void Make_DoAllVar __P((GNode *));
Boolean Make_Run __P((Lst));
+char * Check_Cwd_Cmd __P((char *));
+void Check_Cwd __P((char **));
#endif /* _MAKE_H_ */
Index: compat.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/make/compat.c,v
retrieving revision 1.29
diff -u -p -r1.29 compat.c
--- compat.c 2000/01/21 17:08:35 1.29
+++ compat.c 2000/03/30 07:02:25
@@ -281,6 +281,7 @@ CompatRunCommand (cmdp, gnp)
Fatal("Could not fork");
}
if (cpid == 0) {
+ Check_Cwd(av);
if (local) {
execvp(av[0], av);
(void) write (2, av[0], strlen (av[0]));
Index: job.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/make/job.c,v
retrieving revision 1.32
diff -u -p -r1.32 job.c
--- job.c 2000/01/21 17:08:35 1.32
+++ job.c 2000/03/30 07:02:34
@@ -532,7 +532,8 @@ JobPrintCommand(cmdp, jobp)
LstNode cmdNode; /* Node for replacing the command */
char *cmd = (char *) cmdp;
Job *job = (Job *) jobp;
-
+ char *cp;
+
noSpecials = noExecute && !(job->node->type & OP_MAKE);
if (strcmp(cmd, "...") == 0) {
@@ -640,6 +641,9 @@ JobPrintCommand(cmdp, jobp)
}
}
+ if ((cp = Check_Cwd_Cmd(cmd)) != NULL) {
+ DBPRINTF("cd %s; ", cp);
+ }
DBPRINTF(cmdTemplate, cmd);
free(cmdStart);