Annotation of src/usr.bin/make/targequiv.c, Revision 1.8
1.8 ! espie 1: /* $OpenBSD: targequiv.c,v 1.7 2015/01/16 15:36:30 deraadt Exp $ */
1.1 espie 2: /*
3: * Copyright (c) 2007-2008 Marc Espie.
4: *
5: * Extensive code changes for the OpenBSD project.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
17: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
20: * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27: */
28:
29: /* Compute equivalence tables of targets, helpful for VPATH and parallel
30: * make.
31: */
32:
1.4 espie 33: #include <stddef.h>
1.1 espie 34: #include <stdint.h>
35: #include <stdio.h>
1.4 espie 36: #include <stdlib.h>
1.1 espie 37: #include <string.h>
1.4 espie 38: #include <ohash.h>
1.7 deraadt 39: #include <limits.h>
1.1 espie 40: #include "config.h"
41: #include "defines.h"
42: #include "memory.h"
43: #include "gnode.h"
44: #include "lst.h"
45: #include "suff.h"
46: #include "dir.h"
47: #include "targ.h"
48: #include "targequiv.h"
49:
50: struct equiv_list {
51: GNode *first, *last;
52: char name[1];
53: };
54:
55: static struct ohash_info equiv_info = {
1.5 espie 56: offsetof(struct equiv_list, name), NULL, hash_calloc, hash_free,
1.1 espie 57: element_alloc
58: };
59:
60: static void attach_node(GNode *, GNode *);
61: static void build_equivalence(void);
62: static void add_to_equiv_list(struct ohash *, GNode *);
63: static char *names_match(GNode *, GNode *);
1.2 espie 64: static char *names_match_with_dir(const char *, const char *, char *,
1.1 espie 65: char *, const char *);
1.2 espie 66: static char *names_match_with_dirs(const char *, const char *, char *,
1.1 espie 67: char *, const char *, const char *);
68: static char *relative_reduce(const char *, const char *);
69: static char *relative_reduce2(const char *, const char *, const char *);
70: static char *absolute_reduce(const char *);
71: static size_t parse_reduce(size_t, const char *);
72: static void find_siblings(GNode *);
73:
74: /* These functions build `equivalence lists' of targets with the same
75: * basename, as circular lists. They use an intermediate ohash as scaffold,
76: * to insert same-basename targets in a simply linked list. Then they make
77: * those lists circular, to the exception of lone elements, since they can't
78: * alias to anything.
1.2 espie 79: *
1.1 espie 80: * This structure is used to simplify alias-lookup to a great extent: two
81: * nodes can only alias each other if they're part of the same equivalence
82: * set. Most nodes either don't belong to an alias set, or to a very simple
83: * alias set, thus removing most possibilities.
84: */
85: static void
86: add_to_equiv_list(struct ohash *equiv, GNode *gn)
87: {
88: const char *end = NULL;
89: struct equiv_list *e;
90: unsigned int slot;
91:
92: if (!should_have_file(gn))
93: return;
94:
95: gn->basename = strrchr(gn->name, '/');
96: if (gn->basename == NULL)
97: gn->basename = gn->name;
98: else
99: gn->basename++;
100: slot = ohash_qlookupi(equiv, gn->basename, &end);
101: e = ohash_find(equiv, slot);
102: if (e == NULL) {
103: e = ohash_create_entry(&equiv_info, gn->basename, &end);
104: e->first = NULL;
105: e->last = gn;
106: ohash_insert(equiv, slot, e);
107: }
108: gn->next = e->first;
109: e->first = gn;
110: }
111:
112: static void
113: build_equivalence()
114: {
115: unsigned int i;
116: GNode *gn;
117: struct equiv_list *e;
118: struct ohash equiv;
119: struct ohash *t = targets_hash();
120:
121:
122: ohash_init(&equiv, 10, &equiv_info);
123:
124: for (gn = ohash_first(t, &i); gn != NULL; gn = ohash_next(t, &i))
125: add_to_equiv_list(&equiv, gn);
126:
127: /* finish making the lists circular */
128: for (e = ohash_first(&equiv, &i); e != NULL;
129: e = ohash_next(&equiv, &i)) {
130: if (e->last != e->first)
131: e->last->next = e->first;
132: free(e);
133: }
134: ohash_delete(&equiv);
135: }
136:
137: static const char *curdir, *objdir;
138: static char *kobjdir;
139: static size_t objdir_len;
140:
141: void
142: Targ_setdirs(const char *c, const char *o)
143: {
144: curdir = c;
145: objdir = o;
146:
147: objdir_len = strlen(o);
148: kobjdir = emalloc(objdir_len+2);
149: memcpy(kobjdir, o, objdir_len);
150: kobjdir[objdir_len++] = '/';
151: kobjdir[objdir_len] = 0;
152: }
153:
154:
155: void
156: kludge_look_harder_for_target(GNode *gn)
157: {
158: GNode *extra, *cgn;
159: LstNode ln;
160:
161: if (strncmp(gn->name, kobjdir, objdir_len) == 0) {
162: extra = Targ_FindNode(gn->name + objdir_len, TARG_NOCREATE);
163: if (extra != NULL) {
164: if (Lst_IsEmpty(&gn->commands))
165: Lst_Concat(&gn->commands, &extra->commands);
166: for (ln = Lst_First(&extra->children); ln != NULL;
167: ln = Lst_Adv(ln)) {
1.8 ! espie 168: cgn = Lst_Datum(ln);
1.1 espie 169:
170: if (Lst_AddNew(&gn->children, cgn)) {
171: Lst_AtEnd(&cgn->parents, gn);
172: gn->unmade++;
173: }
174: }
175: }
176: }
177: }
178:
179: static void
180: attach_node(GNode *gn, GNode *extra)
181: {
182: /* XXX normally extra->sibling == extra, but this is not
1.2 espie 183: * always the case yet, so we merge the two lists
1.1 espie 184: */
185: GNode *tmp;
186:
187: tmp = gn->sibling;
188: gn->sibling = extra->sibling;
189: extra->sibling = tmp;
190: }
191:
192: static char *buffer = NULL;
1.7 deraadt 193: static size_t bufsize = PATH_MAX;
1.1 espie 194:
195: static size_t
196: parse_reduce(size_t i, const char *src)
197: {
198: while (src[0] != 0) {
199: while (src[0] == '/')
200: src++;
201: /* special cases */
202: if (src[0] == '.') {
203: if (src[1] == '/') {
204: src += 2;
205: continue;
206: }
207: if (src[1] == '.' && src[2] == '/') {
208: src += 3;
209: i--;
210: while (i > 0 && buffer[i-1] != '/')
211: i--;
212: if (i == 0)
213: i = 1;
214: continue;
215: }
216: }
217: while (src[0] != '/' && src[0] != 0) {
218: if (i > bufsize - 2) {
219: bufsize *= 2;
220: buffer = erealloc(buffer, bufsize);
221: }
222: buffer[i++] = *src++;
223: }
1.6 espie 224: if (src[0] == '/')
225: buffer[i++] = *src++;
1.1 espie 226: }
1.6 espie 227: buffer[i++] = 0;
1.1 espie 228: return i;
229: }
230:
231: static char *
232: absolute_reduce(const char *src)
233: {
234: size_t i = 0;
235:
236: if (buffer == NULL)
237: buffer = emalloc(bufsize);
238:
239: buffer[i++] = '/';
240: i = parse_reduce(i, src);
1.2 espie 241: return estrdup(buffer);
1.1 espie 242: }
243:
244: static char *
245: relative_reduce(const char *dir, const char *src)
246: {
247: size_t i = 0;
1.2 espie 248:
1.1 espie 249: if (buffer == NULL)
250: buffer = emalloc(bufsize);
251:
252: buffer[i++] = '/';
253: i = parse_reduce(i, dir);
254: i--;
255:
256: if (buffer[i-1] != '/')
257: buffer[i++] = '/';
258: i = parse_reduce(i, src);
1.2 espie 259: return estrdup(buffer);
1.1 espie 260: }
261:
262: static char *
263: relative_reduce2(const char *dir1, const char *dir2, const char *src)
264: {
265: size_t i = 0;
1.2 espie 266:
1.1 espie 267: if (buffer == NULL)
268: buffer = emalloc(bufsize);
269:
270: buffer[i++] = '/';
271: i = parse_reduce(i, dir1);
272: i--;
273: if (buffer[i-1] != '/')
274: buffer[i++] = '/';
275:
276: i = parse_reduce(i, dir2);
277: i--;
278: if (buffer[i-1] != '/')
279: buffer[i++] = '/';
280:
281: i = parse_reduce(i, src);
1.2 espie 282: return estrdup(buffer);
1.1 espie 283: }
284:
285: static char *
1.2 espie 286: names_match_with_dir(const char *a, const char *b, char *ra,
1.1 espie 287: char *rb, const char *dir)
288: {
289: bool r;
290: bool free_a, free_b;
1.2 espie 291:
1.1 espie 292: if (ra == NULL) {
293: ra = relative_reduce(dir, a);
294: free_a = true;
295: } else {
296: free_a = false;
297: }
298:
299: if (rb == NULL) {
300: rb = relative_reduce(dir, b);
301: free_b = true;
302: } else {
303: free_b = false;
304: }
305: r = strcmp(ra, rb) == 0;
306: if (free_a)
307: free(ra);
308: if (r)
309: return rb;
310: else {
311: if (free_b)
312: free(rb);
313: return NULL;
314: }
315: }
316:
317: static char *
1.2 espie 318: names_match_with_dirs(const char *a, const char *b, char *ra,
1.1 espie 319: char *rb, const char *dir1, const char *dir2)
320: {
321: bool r;
322: bool free_a, free_b;
1.2 espie 323:
1.1 espie 324: if (ra == NULL) {
325: ra = relative_reduce2(dir1, dir2, a);
326: free_a = true;
327: } else {
328: free_a = false;
329: }
330:
331: if (rb == NULL) {
332: rb = relative_reduce2(dir1, dir2, b);
333: free_b = true;
334: } else {
335: free_b = false;
336: }
337: r = strcmp(ra, rb) == 0;
338: if (free_a)
339: free(ra);
340: if (r)
341: return rb;
342: else {
343: if (free_b)
344: free(rb);
345: return NULL;
346: }
347: }
348:
349: static char *
350: names_match(GNode *a, GNode *b)
351: {
352: char *ra = NULL , *rb = NULL;
353: char *r;
354:
355: if (a->name[0] == '/')
356: ra = absolute_reduce(a->name);
357: if (b->name[0] == '/')
358: rb = absolute_reduce(b->name);
359: if (ra && rb) {
360: if (strcmp(ra, rb) == 0)
361: r = rb;
362: else
363: r = NULL;
364: } else {
365: r = names_match_with_dir(a->name, b->name, ra, rb, objdir);
366: if (!r)
1.2 espie 367: r = names_match_with_dir(a->name, b->name, ra, rb,
1.1 espie 368: curdir);
369: if (!r) {
370: /* b has necessarily the same one */
371: Lst l = find_suffix_path(a);
372: LstNode ln;
373:
374: for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln)) {
375: const char *p = PathEntry_name(Lst_Datum(ln));
376: if (p[0] == '/') {
1.2 espie 377: r = names_match_with_dir(a->name,
1.1 espie 378: b->name, ra, rb, p);
379: if (r)
380: break;
381: } else {
382: r = names_match_with_dirs(a->name,
383: b->name, ra, rb, p, objdir);
384: if (r)
385: break;
386: r = names_match_with_dirs(a->name,
387: b->name, ra, rb, p, curdir);
388: if (r)
389: break;
390: }
391: }
392: }
393: }
394: free(ra);
395: if (r != rb)
396: free(rb);
397: return r;
398: }
399:
400: static void
401: find_siblings(GNode *gn)
402: {
403: GNode *gn2;
404: char *fullpath;
405:
406: /* not part of an equivalence class: can't alias */
407: if (gn->next == NULL)
408: return;
409: /* already resolved, actually */
410: if (gn->sibling != gn)
411: return;
412: if (DEBUG(NAME_MATCHING))
413: fprintf(stderr, "Matching for %s:", gn->name);
414: /* look through the aliases */
415: for (gn2 = gn->next; gn2 != gn; gn2 = gn2->next) {
416: fullpath = names_match(gn, gn2);
417: if (fullpath) {
418: attach_node(gn, gn2);
419: } else {
420: if (DEBUG(NAME_MATCHING))
421: fputc('!', stderr);
422: }
423: if (DEBUG(NAME_MATCHING))
424: fprintf(stderr, "%s ", gn2->name);
425: }
426: if (DEBUG(NAME_MATCHING))
427: fputc('\n', stderr);
428: }
429:
430: void
431: look_harder_for_target(GNode *gn)
432: {
433: static bool equiv_was_built = false;
434:
435: if (!equiv_was_built) {
436: build_equivalence();
437: equiv_was_built = true;
438: }
439:
440: if (gn->type & (OP_RESOLVED | OP_PHONY))
441: return;
442: gn->type |= OP_RESOLVED;
443: find_siblings(gn);
444: }
445:
446: bool
447: is_sibling(GNode *gn, GNode *gn2)
448: {
449: GNode *sibling;
1.2 espie 450:
1.1 espie 451: sibling = gn;
452: do {
453: if (sibling == gn2)
454: return true;
455: sibling = sibling->sibling;
456: } while (sibling != gn);
457:
458: return false;
459: }