[BACK]Return to targequiv.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Annotation of src/usr.bin/make/targequiv.c, Revision 1.10

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