Subject: parent directory search for make(1)
To: None <tech-toolchain@netbsd.org>
From: Chuck Cranor <chuck@ece.cmu.edu>
List: tech-toolchain
Date: 01/29/2004 20:53:44
hi-

    i'm working on a project where we want use NetBSD make(1) as
the main build tool.   since this project will run on multiple 
platforms, i'm using Simon Gerraty's portable bmake package (see 
http://www.crufty.net/help/sjg/bmake.html ) which is based on 
NetBSD make.

    one thing we'd like to have is all of make's macros live within
the base of our local CVS source tree rather than outside of it (e.g.
we don't want to use /usr/share/mk).  in order for this to work, our 
build environment needs a way to walk up the directory tree looking 
for sys.mk (giving up if it reaches "/" without finding it).

    there are a couple of ways to add this search in.  currently
i'm using a small patch to make that adds this semantic to MAKESYSPATH.
[patch included below]   basically, it uses a magic path string of
".../" to access the parent directory search.   then the locally
compiled bmake has its default MAKESYSPATH set to ".../mk/sys.mk"
which causes it to look for "mk/sys.mk", then "../mk/sys.mk", then
"../../mk/sys.mk", etc. until it finds it or reaches the root of
the filesystem without finding it.

    in discussing it with Simon, he suggests another approach is
to put the directory search in a wrapper script that dynamically
determines the location of "sys.mk" and invokes make with that
directory passed in (either via "-m" or $MAKESYSPATH).

    it seems like having this feature in NetBSD make itself would
be useful(?).  it isn't clear if it would be better to put it in
make itself (like i did) or add a new make wrapper script to the
NetBSD source tree that would do the search.  anyone have any
helpful thoughts on this?

chuck



diff -rc bmake_DIST/dir.c bmake/dir.c
*** bmake_DIST/dir.c	Fri Sep 12 17:32:17 2003
--- bmake/dir.c	Wed Jan 28 10:02:57 2004
***************
*** 113,118 ****
--- 113,122 ----
   *	    	  	    If it exists, the entire path is returned.
   *	    	  	    Otherwise NULL is returned.
   *
+  *	Dir_FindHereOrAbove Search for a path in the current directory and
+  *			    then all the directories above it in turn until
+  *			    the path is found or we reach the root ("/").
+  * 
   *	Dir_MTime 	    Return the modification time of a node. The file
   *	    	  	    is searched for along the default search path.
   *	    	  	    The path and mtime fields of the node are filled
***************
*** 1331,1336 ****
--- 1335,1419 ----
  #endif /* notdef */
  }
  
+ 
+ /*-
+  *-----------------------------------------------------------------------
+  * Dir_FindHereOrAbove  --
+  *	search for a path starting at a given directory and then working 
+  *	our way up towards the root.
+  *
+  * Input:
+  *	here		starting directory
+  *	search_path	the path we are looking for
+  *	result		the result of a successful search is placed here
+  *	rlen		the length of the result buffer 
+  *			(typically MAXPATHLEN + 1)
+  *
+  * Results:
+  *	0 on failure, 1 on success [in which case the found path is put
+  *	in the result buffer].
+  *
+  * Side Effects:
+  *-----------------------------------------------------------------------
+  */
+ int 
+ Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) {
+ 
+ 	struct stat st;
+ 	char dirbase[MAXPATHLEN + 1], *db_end;
+         char try[MAXPATHLEN + 1], *try_end;
+ 
+ 	/* copy out our starting point */
+ 	snprintf(dirbase, sizeof(dirbase), "%s", here);
+ 	db_end = dirbase + strlen(dirbase);
+ 
+ 	/* loop until we determine a result */
+ 	while (1) {
+ 
+ 		/* try and stat(2) it ... */
+ 		snprintf(try, sizeof(try), "%s/%s", dirbase, search_path);
+ 		if (stat(try, &st) != -1) {
+ 			/*
+ 			 * success!  if we found a file, chop off
+ 			 * the filename so we return a directory.
+ 			 */
+ 			if ((st.st_mode & S_IFMT) != S_IFDIR) {
+ 				try_end = try + strlen(try);
+ 				while (try_end > try && *try_end != '/')
+ 					try_end--;
+ 				if (try_end > try) 
+ 					*try_end = 0;	/* chop! */
+ 			}
+ 
+ 			/*
+ 			 * done!
+ 			 */
+ 			snprintf(result, rlen, "%s", try);
+ 			return(1);
+ 		}
+ 
+ 		/* 
+ 		 * nope, we didn't find it.  if we used up dirbase we've
+ 		 * reached the root and failed.
+ 		 */
+ 		if (db_end == dirbase)
+ 			break;		/* failed! */
+ 
+ 		/*
+ 		 * truncate dirbase from the end to move up a dir
+ 		 */
+ 		while (db_end > dirbase && *db_end != '/')
+ 			db_end--;
+ 		*db_end = 0;		/* chop! */
+ 
+ 	} /* while (1) */
+ 
+ 	/*
+ 	 * we failed... 
+ 	 */
+ 	return(0);
+ }
+ 
  /*-
   *-----------------------------------------------------------------------
   * Dir_MTime  --
diff -rc bmake_DIST/dir.h bmake/dir.h
*** bmake_DIST/dir.h	Tue Sep  9 03:31:10 2003
--- bmake/dir.h	Wed Jan 28 10:03:48 2004
***************
*** 94,99 ****
--- 94,100 ----
  Boolean Dir_HasWildcards(char *);
  void Dir_Expand(const char *, Lst, Lst);
  char *Dir_FindFile(const char *, Lst);
+ int Dir_FindHereOrAbove(char *, char *, char *, int);
  int Dir_MTime(GNode *);
  Path *Dir_AddDir(Lst, const char *);
  char *Dir_MakeFlags(const char *, Lst);
diff -rc bmake_DIST/main.c bmake/main.c
*** bmake_DIST/main.c	Tue Jan  6 03:09:14 2004
--- bmake/main.c	Wed Jan 28 10:24:41 2004
***************
*** 605,610 ****
--- 605,611 ----
  	char *cp = NULL, *start;
  					/* avoid faults on read-only strings */
  	static char defsyspath[] = _PATH_DEFSYSPATH;
+ 	char found_path[MAXPATHLEN + 1];	/* for searching for sys.mk */
  	
  	if ((progname = strrchr(argv[0], '/')) != NULL)
  		progname++;
***************
*** 849,859 ****
  	for (start = syspath; *start != '\0'; start = cp) {
  		for (cp = start; *cp != '\0' && *cp != ':'; cp++)
  			continue;
! 		if (*cp == '\0') {
! 			(void) Dir_AddDir(defIncPath, start);
! 		} else {
  			*cp++ = '\0';
  			(void) Dir_AddDir(defIncPath, start);
  		}
  	}
  	if (syspath != defsyspath)
--- 850,866 ----
  	for (start = syspath; *start != '\0'; start = cp) {
  		for (cp = start; *cp != '\0' && *cp != ':'; cp++)
  			continue;
! 		if (*cp == ':') {
  			*cp++ = '\0';
+ 		}
+ 		/* look for magic parent directory search string */
+ 		if (strncmp(".../", start, 4) != 0) {
  			(void) Dir_AddDir(defIncPath, start);
+ 		} else {
+ 			if (Dir_FindHereOrAbove(curdir, start+4, 
+ 			    found_path, sizeof(found_path))) {
+ 				(void) Dir_AddDir(defIncPath, found_path);
+ 			}
  		}
  	}
  	if (syspath != defsyspath)