2 deptrack.c -- high-level routines for gendepend.
4 (c) Han-Wen Nienhuys <hanwen@cs.uu.nl> 1998
5 (c) Michael Roitzsch <mroi@os.inf.tu-dresden.de> 2007
17 #if defined(__APPLE__) || defined(__FreeBSD__)
18 #include <sys/types.h>
19 #include <sys/sysctl.h>
25 #define min(a,b) ((a)<(b)?(a):(b))
38 static char * executable_name;
39 static char * wanted_executable_name;
41 static char * depfile_name;
43 /* Special mode: Detection mode. In this mode, we watch for a binary opening
44 a file specified in the environment variable GENDEP_SOURCE (stored in
45 target). The binary name is written into $(GENDEP_OUTPUT) (depfile_name)
47 static int detection_mode;
48 static int detection_done;
50 /* List of dependencies. We first collect all, then write them out. */
51 static struct strlist{
62 write_word (char const * word)
65 int l = strlen (word);
66 if (l + column > 75 && column>1)
68 fputs ("\\\n\t",output);
83 perror ("libgendep.so");
96 xrealloc (void *s, int sz)
98 void *c = realloc (s, sz);
112 /*!\brief Trim the filename
114 * Remove redundant "./" at the beginning of a filename.
116 * We are not very minimal about memory usage, because this would require
117 * scanning the filename twice, instead of once.
119 static char* trim (const char*fn){
122 /* remove trailing "./" */
123 while(fn[0]=='.' && fn[1]=='/') fn+=2;
124 name = xmalloc(strlen(fn)+1);
125 if(name==0) return 0;
131 /*!\brief add a dependency
133 * Add a dependency to the list of dependencies for the current target.
134 * If the name is already registered, it won't be added.
136 static void gendep__adddep(const char*name){
140 if (!(namecopy = trim(name)))
143 /* special detection mode ? */
146 if (!strcmp(namecopy, target))
153 if (!strcmp(target, name))
156 for(dep=dependencies; dep; dep=dep->next){
157 if(!strcmp(dep->name, namecopy)){
162 dep = xmalloc(sizeof(struct strlist));
163 dep->name = namecopy;
164 dep->next = dependencies;
168 /*!\brief Delete a dependency
170 * Delete a dependency from the list of dependencies for the current target.
171 * This is used if an already registered file is removed later.
173 static void gendep__deldep(const char*name){
174 struct strlist *dep, *old;
178 if((namecopy = trim(name))==0) return;
179 for(dep=dependencies; dep; dep=dep->next){
180 if(!strcmp(dep->name, namecopy)){
181 if(old) old->next = dep->next;
182 else dependencies=dep->next;
184 free((char*)dep->name);
194 gendep_getenv (char **dest, char *what)
196 char var[STRLEN]="GENDEP_";
198 *dest = getenv (var);
204 Do hairy stuff with regexps and environment variables.
212 if(gendep_getenv(&target, "SOURCE")){
213 /* we have to watch for the binary that opens our file first */
215 gendep_getenv(&depfile_name, "OUTPUT");
219 /* standard mode of operation: catch dependencies */
220 if (!gendep_getenv (&wanted_executable_name, "BINARY"))
223 if (strcmp (wanted_executable_name, executable_name))
225 /* support two binary names (e.g. for arch-foo-X and X) */
226 if (!gendep_getenv (&wanted_executable_name, "BINARY_ALT1")
227 || strcmp (wanted_executable_name, executable_name))
231 gendep_getenv(&depfile_name, "DEPFILE");
233 if (!gendep_getenv (&target, "TARGET"))
236 if (!gendep_getenv (®exp_val, executable_name))
240 let's hope malloc (0) does the sensible thing (not return NULL, that is)
242 regexps.re_array = xmalloc (0);
243 regexps.invert_array = xmalloc (0);
245 regexp_val = xstrdup (regexp_val);
247 end = regexp_val + strlen (regexp_val);
250 Duh. The strength of C : string handling. (not). Pull apart the whitespace delimited
251 list of regexps, and try to compile them.
252 Be really gnuish with dynamic allocation of arrays.
257 while (*start && *start != '+' && *start != '-')
260 in_out= (*start == '+') ;
262 end_re = strchr (start, ' ');
269 int result= regcomp (®ex, start, REG_NOSUB);
273 fprintf (stderr, "libgendep.so: Bad regular expression `%s\'\n", start);
277 if (regexps.no >= regexps.max)
279 /* max_regexps = max_regexps * 2 + 1; */
281 regexps.re_array = xrealloc (regexps.re_array, regexps.max * sizeof (regex_t));
282 regexps.invert_array = xrealloc (regexps.invert_array, regexps.max * sizeof (char));
285 regexps.re_array[regexps.no] = regex;
286 regexps.invert_array[regexps.no++] = in_out;
290 start = end_re ? end_re + 1 : end;
291 } while (start < end);
295 Try to get the name of the binary. Is there a portable way to do this?
297 static void get_executable_name(void)
301 const char * const proc_cmdline = "/proc/self/cmdline";
302 FILE *cmdline = fopen (proc_cmdline, "r");
310 fprintf(stderr, "libgendep.o: cannot open %s\n", proc_cmdline);
314 while ((c = fgetc(cmdline))!=EOF && c && i < STRLEN-1)
318 #elif defined(__APPLE__) || defined(__FreeBSD__)
321 char *procargs, *cmd;
323 /* allocate process argument space */
325 mib[1] = KERN_ARGMAX;
326 size = sizeof(arglen);
327 if (sysctl(mib, 2, &arglen, &size, NULL, 0) == -1)
329 if (!(procargs = xmalloc(arglen)))
332 /* get a copy of the process argument space */
334 #if defined(__APPLE__)
335 mib[1] = KERN_PROCARGS2;
337 size = (size_t)arglen;
338 if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
344 mib[2] = KERN_PROC_ARGS;
346 size = (size_t)arglen;
347 if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
353 /* jump over the argument count */
354 cmd = procargs + sizeof(int);
356 # error "Retrieving the executable name is not implemented for your platform."
359 /* ugh. man 3 basename -> ? */
360 basename_p = strrchr(cmd, '/');
366 executable_name = xstrdup(basename_p);
368 #if defined(__APPLE__) || defined(__FreeBSD__)
373 static void initialize (void) __attribute__ ((constructor));
375 static void initialize (void)
377 get_executable_name ();
380 if (target && !detection_mode)
387 strncpy(fn, depfile_name, STRLEN);
389 slash = strrchr(target, '/');
391 strncat (fn, target, min(STRLEN, slash?slash-target+1:0));
392 strncat (fn, ".", STRLEN);
394 strncat(fn, slash?slash+1:target, STRLEN);
395 strncat (fn, ".d", STRLEN);
399 if((output = fopen (fn, "w"))==0){
400 fprintf(stderr, "libgendep.so: cannot open %s for writing\n", fn);
408 /* Someone is opening a file. If it is opened for reading, and
409 matches the regular expressions, write it to the dep-file.
411 Note that we explicitly ignore accesses to /etc/mtab and /proc/...
412 as these files are inspected by libc for Linux and tend to change.
415 gendep__register_open (char const *fn, int flags)
417 if ((detection_mode || output) && !(flags & (O_WRONLY | O_RDWR)))
421 if (fn == strstr(fn, "/etc/mtab") ||
422 fn == strstr(fn, "/proc/"))
425 for (i = 0; i < regexps.no; i++)
427 int not_matched = regexec (regexps.re_array +i, fn, 0, NULL, 0);
429 if (!(not_matched ^ regexps.invert_array[i]))
438 gendep__register_unlink (char const *fn)
444 static void finish (void) __attribute__ ((destructor));
446 static void finish (void)
452 for(dep=dependencies; dep; dep=dep->next){
453 write_word(dep->name);
455 fprintf (output, "\n");
456 for(dep=dependencies; dep; dep=dep->next){
457 fputs(dep->name, output);
458 fputs(":\n", output);
464 if (detection_mode && detection_done && depfile_name)
469 * libgendep is called in a nested fashion (fork+execve in various
470 * wrappers), so we only write out the most inner occurence, which
471 * means we only write as long as depfile_name is not there
473 if (access(depfile_name, W_OK) == -1 && errno == ENOENT)
475 file = fopen(depfile_name, "w");
478 fwrite(executable_name, strlen(executable_name), 1, file);