/* * interdep.c - inter-package analysis and optimizations based on * strict dependencies between subpackages (Requires: N = [E:]V-R). * * Written by Alexey Tourbin . * License: GPLv2+. */ #include "system.h" #include "psm.h" // TFI_t #include "rpmbuild.h" #include "interdep.h" static const char *pkgName(Package pkg) { TFI_t fi = pkg->cpioList; if (fi) return fi->name; const char *name; headerNVR(pkg->header, &name, NULL, NULL); return name; } static const char *skipPrefixDash(const char *str, const char *prefix) { int len = strlen(prefix); if (strncmp(str, prefix, len)) return NULL; if (str[len] != '-') return NULL; return str + len + 1; } struct Req { int c; struct Pair { Package pkg1; Package pkg2; } *v; }; static int Requires(struct Req *r, Package pkg1, Package pkg2) { int i; for (i = 0; i < r->c; i++) if (pkg1 == r->v[i].pkg1 && pkg2 == r->v[i].pkg2) return 1; return 0; } static void addRequires(struct Req *r, Package pkg1, Package pkg2) { if (pkg1 == pkg2) return; if (Requires(r, pkg1, pkg2)) return; AUTO_REALLOC(r->v, r->c, 8); r->v[r->c++] = (struct Pair) { pkg1, pkg2 }; } static void makeReq1(struct Req *r, Package pkg1, Package pkg2, int warn) { int c = 0; const char **reqNv = NULL; const char **reqVv = NULL; const int *reqFv = NULL; const HGE_t hge = (HGE_t)headerGetEntryMinMemory; int ok = hge(pkg1->header, RPMTAG_REQUIRENAME, NULL, (void **) &reqNv, &c) && hge(pkg1->header, RPMTAG_REQUIREVERSION, NULL, (void **) &reqVv, NULL) && hge(pkg1->header, RPMTAG_REQUIREFLAGS, NULL, (void **) &reqFv, NULL); if (!ok) return; const char *provN, *provV, *provR; headerNVR(pkg2->header, &provN, &provV, &provR); int i; for (i = 0; i < c; i++) { if (strcmp(reqNv[i], provN)) continue; if ((reqFv[i] & RPMSENSE_SENSEMASK) != RPMSENSE_EQUAL) continue; const char *reqVR = reqVv[i]; if (*reqVR == '\0') continue; const char *colon = NULL; const char *reqR = skipPrefixDash(reqVR, provV); if (reqR == NULL && xisdigit(*reqVR)) { colon = strchr(reqVR, ':'); if (colon) reqR = skipPrefixDash(colon + 1, provV); } if (reqR == NULL) continue; if (strcmp(reqR, provR)) continue; addRequires(r, pkg1, pkg2); if (warn && colon == NULL && headerIsEntry(pkg2->header, RPMTAG_EPOCH)) fprintf(stderr, "warning: %s: dependency on %s needs Epoch\n", pkgName(pkg1), pkgName(pkg2)); break; } const HFD_t hfd = (HFD_t) headerFreeData; reqNv = hfd(reqNv, RPM_STRING_ARRAY_TYPE); reqVv = hfd(reqVv, RPM_STRING_ARRAY_TYPE); } #include "al.h" // check if pkg1 has (possibly non-strict) dependency on pkg2 static int depRequires(Package pkg1, Package pkg2) { int reqc = 0; const char **reqNv = NULL; const char **reqVv = NULL; const int *reqFv = NULL; const HGE_t hge = (HGE_t) headerGetEntryMinMemory; int ok = hge(pkg1->header, RPMTAG_REQUIRENAME, NULL, (void **) &reqNv, &reqc) && hge(pkg1->header, RPMTAG_REQUIREVERSION, NULL, (void **) &reqVv, NULL) && hge(pkg1->header, RPMTAG_REQUIREFLAGS, NULL, (void **) &reqFv, NULL); if (!ok) return 0; availableList proval = alloca(sizeof proval); alCreate(proval); alAddPackage(proval, pkg2->header, NULL, NULL, NULL); int i; struct availablePackage *ap = NULL; for (i = 0; i < reqc; i++) { ap = alSatisfiesDepend(proval, reqNv[i], reqVv[i], reqFv[i]); if (ap) break; } const HFD_t hfd = (HFD_t) headerFreeData; reqNv = hfd(reqNv, RPM_STRING_ARRAY_TYPE); reqVv = hfd(reqVv, RPM_STRING_ARRAY_TYPE); alFree(proval); return ap ? 1 : 0; } static struct Req *makeRequires(Spec spec, int warn) { struct Req *r = xmalloc(sizeof *r); r->c = 0; r->v = NULL; Package pkg1, pkg2; for (pkg1 = spec->packages; pkg1; pkg1 = pkg1->next) for (pkg2 = pkg1->next; pkg2; pkg2 = pkg2->next) { makeReq1(r, pkg1, pkg2, warn & 1); makeReq1(r, pkg2, pkg1, warn & 1); } int propagated; do { propagated = 0; int i1, i2; for (i1 = 0; i1 < r->c; i1++) for (i2 = i1; i2 < r->c; i2++) { struct Pair r1 = r->v[i1]; struct Pair r2 = r->v[i2]; if (r1.pkg2 == r2.pkg1 && r1.pkg1 != r2.pkg2 && !Requires(r, r1.pkg1, r2.pkg2)) { addRequires(r, r1.pkg1, r2.pkg2); propagated++; } if (r2.pkg2 == r1.pkg1 && r2.pkg1 != r1.pkg2 && !Requires(r, r2.pkg1, r1.pkg2)) { addRequires(r, r2.pkg1, r1.pkg2); propagated++; } } } while (propagated); if ((warn & 2) == 0) return r; for (pkg1 = spec->packages; pkg1; pkg1 = pkg1->next) for (pkg2 = pkg1->next; pkg2; pkg2 = pkg2->next) { if (!Requires(r, pkg1, pkg2) && depRequires(pkg1, pkg2)) fprintf(stderr, "warning: %s: non-strict dependency on %s\n", pkgName(pkg1), pkgName(pkg2)); if (!Requires(r, pkg2, pkg1) && depRequires(pkg2, pkg1)) fprintf(stderr, "warning: %s: non-strict dependency on %s\n", pkgName(pkg2), pkgName(pkg1)); } return r; } static struct Req *freeRequires(struct Req *r) { r->v = _free(r->v); return _free(r); } #include "checkFiles.h" // fiIntersect static void fiPrune(TFI_t fi, char pruned[]) { int *dil_; const char **bnl, **dnl; int bnc, dnc, dic; int ok = fi->hge(fi->h, RPMTAG_BASENAMES, NULL, (void **) &bnl, &bnc) && fi->hge(fi->h, RPMTAG_DIRNAMES, NULL, (void **) &dnl, &dnc) && fi->hge(fi->h, RPMTAG_DIRINDEXES, NULL, (void **) &dil_, &dic); assert(ok); assert(fi->fc == bnc); assert(bnc == dic); int i, j, k; // dil must be copied, cf. relocateFileList int dil[dic]; for (i = 0; i < dic; i++) dil[i] = dil_[i]; // mark used dirnames int dirused[dnc]; bzero(dirused, dnc * sizeof *dirused); for (i = 0; i < bnc; i++) if (!pruned[i]) dirused[dil[i]]++; int propagated; do { propagated = 0; // for each unused dirname for (i = 0; i < dnc; i++) { if (dirused[i]) continue; // find its corresponding parent_dir+name entry for (j = 0; j < bnc; j++) { if (pruned[j]) continue; const char *D = dnl[i]; const char *d = dnl[dil[j]]; int dlen = strlen(d); if (strncmp(D, d, dlen)) continue; const char *b = bnl[j]; int blen = strlen(b); if (strncmp(D + dlen, b, blen)) continue; if (strncmp(D + dlen + blen, "/", 2)) continue; // makr parent_dir+name for removal fprintf(stderr, "also prunning dir %s%s\n", d, b); pruned[j] = 1; // decrement parent_dir usage if (--dirused[dil[j]] == 0) propagated++; } } } while (propagated); // new count for bnc-like values int oldc = bnc; int newc = 0; for (i = 0; i < oldc; i++) if (!pruned[i]) newc++; // establish new dirnames and dirindexes for (i = 0, j = 0; i < dnc; i++) { if (!dirused[i]) continue; if (i == j) goto skip; dnl[j] = dnl[i]; for (k = 0; k < dic; k++) if (dil[k] == i) dil[k] = j; skip: j++; } dnc = j; // handle bnl, dnl and dil #define PruneV(v) \ for (i = 0, j = 0; i < oldc; i++) \ if (!pruned[i]) \ v[j++] = v[i] PruneV(bnl); PruneV(dil); PruneV(fi->bnl); PruneV(fi->dil); fi->hme(fi->h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE, bnl, newc); fi->hme(fi->h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE, dnl, dnc); fi->hme(fi->h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dil, newc); bnl = fi->hfd(bnl, RPM_STRING_ARRAY_TYPE); dnl = fi->hfd(dnl, RPM_STRING_ARRAY_TYPE); // prune header tags rpmTagType tagt; int tagc; const char **strv; #define PruneStrTag(tag) \ if (fi->hge(fi->h, tag, &tagt, (void **) &strv, &tagc)) { \ assert(tagt == RPM_STRING_ARRAY_TYPE); \ assert(tagc == oldc); \ PruneV(strv); \ fi->hme(fi->h, tag, RPM_STRING_ARRAY_TYPE, strv, newc); \ fi->hfd(strv, RPM_STRING_ARRAY_TYPE); \ } short *INT16p, INT16v[oldc]; int *INT32p, INT32v[oldc]; #define PruneIntTag(INT, tag) \ if (fi->hge(fi->h, tag, &tagt, (void **) &INT ## p, &tagc)) { \ assert(tagt == RPM_ ## INT ## _TYPE); \ assert(tagc == oldc); \ for (i = 0; i < oldc; i++) \ INT ## v[i] = INT ## p[i]; \ PruneV(INT ## v); \ fi->hme(fi->h, tag, RPM_ ## INT ##_TYPE, INT ## v, newc); \ } #define PruneI16Tag(tag) PruneIntTag(INT16, tag) #define PruneI32Tag(tag) PruneIntTag(INT32, tag) PruneI32Tag(RPMTAG_FILESIZES); PruneStrTag(RPMTAG_FILEUSERNAME); PruneStrTag(RPMTAG_FILEGROUPNAME); PruneI32Tag(RPMTAG_FILEMTIMES); PruneI16Tag(RPMTAG_FILEMODES); PruneI16Tag(RPMTAG_FILERDEVS); PruneI32Tag(RPMTAG_FILEDEVICES); PruneI32Tag(RPMTAG_FILEINODES); PruneStrTag(RPMTAG_FILELANGS); PruneStrTag(RPMTAG_FILEMD5S); PruneStrTag(RPMTAG_FILELINKTOS); PruneI32Tag(RPMTAG_FILEVERIFYFLAGS); PruneI32Tag(RPMTAG_FILEFLAGS); // update fi, cf. genCpioListAndHeader PruneV(fi->apath); PruneV(fi->actions); PruneV(fi->fmapflags); PruneV(fi->fuids); PruneV(fi->fgids); PruneV(fi->fsts); struct transactionFileInfo_s save_fi; #define MV(a) save_fi.a = fi->a; fi->a = NULL MV(bnl); MV(dnl); MV(dil); MV(apath); MV(actions); MV(fmapflags); MV(fuids); MV(fgids); MV(fsts); save_fi.h = fi->h; save_fi.astriplen = fi->astriplen; freeFi(fi); bzero(fi, sizeof *fi); fi->type = TR_ADDED; loadFi(save_fi.h, fi); assert(fi->fc == newc); fi->dnl = _free(fi->dnl); fi->bnl = _free(fi->bnl); #undef MV #define MV(a) fi->a = save_fi.a MV(bnl); MV(dnl); MV(dil); MV(apath); MV(actions); MV(fmapflags); MV(fuids); MV(fgids); MV(fsts); fi->astriplen = save_fi.astriplen; } // prune src dups from pkg1 and add dependency on pkg2 static void pruneSrc1(Package pkg1, Package pkg2) { TFI_t fi1 = pkg1->cpioList; TFI_t fi2 = pkg2->cpioList; if (fi1 == NULL) return; if (fi2 == NULL) return; int npruned = 0; char pruned[fi1->fc]; bzero(pruned, fi1->fc); void cb(char *f, int i1, int i2) { (void) i2; if (S_ISDIR(fi1->fmodes[i1])) return; const char src[] = "/usr/src/debug/"; if (strncmp(f, src, sizeof(src) - 1)) return; pruned[i1] = 1; npruned++; } fiIntersect(fi1, fi2, cb); if (npruned == 0) return; fprintf(stderr, "removing %d sources from %s and adding dependency on %s\n", npruned, pkgName(pkg1), pkgName(pkg2)); fiPrune(fi1, pruned); const char *name = pkgName(pkg2); const char *evr = headerSprintf(pkg2->header, "%|epoch?{%{epoch}:}|%{version}-%{release}", rpmTagTable, rpmHeaderFormats, NULL); assert(evr); int flags = RPMSENSE_EQUAL | RPMSENSE_FIND_REQUIRES; assert(pkg1->header == fi1->h); headerAddOrAppendEntry(fi1->h, RPMTAG_REQUIRENAME, RPM_STRING_ARRAY_TYPE, &name, 1); headerAddOrAppendEntry(fi1->h, RPMTAG_REQUIREVERSION, RPM_STRING_ARRAY_TYPE, &evr, 1); headerAddOrAppendEntry(fi1->h, RPMTAG_REQUIREFLAGS, RPM_INT32_TYPE, &flags, 1); evr = _free(evr); } static void processDependentDebuginfo(struct Req *r, Spec spec, void (*cb)(Package pkg1, Package pkg2), int mutual) { int i1, i2; struct Pair r1, r2; const char *Nd, *Np; const char *suf; // r1 = { pkg1-debuginfo, pkg1 } for (i1 = 0; i1 < r->c; i1++) { r1 = r->v[i1]; Nd = pkgName(r1.pkg1); Np = pkgName(r1.pkg2); suf = skipPrefixDash(Nd, Np); if (suf == NULL || strcmp(suf, "debuginfo")) continue; // r2 = { pkg2-debuginfo, pkg2 } for (i2 = i1; i2 < r->c; i2++) { r2 = r->v[i2]; Nd = pkgName(r2.pkg1); Np = pkgName(r2.pkg2); suf = skipPrefixDash(Nd, Np); if (suf == NULL || strcmp(suf, "debuginfo")) continue; // (pkg1 <-> pkg2) => (pkg1-debuginfo <-> pkg2-debuginfo) if (Requires(r, r1.pkg2, r2.pkg2)) { cb(r1.pkg1, r2.pkg1); if (!mutual) continue; } if (Requires(r, r2.pkg2, r1.pkg2)) cb(r2.pkg1, r1.pkg1); } } } static void pruneDebuginfoSrc(struct Req *r, Spec spec) { processDependentDebuginfo(r, spec, pruneSrc1, 0); } // if pkg1 implicitly requires pkg2, add strict dependency static void liftDeps1(Package pkg1, Package pkg2) { if (!depRequires(pkg1, pkg2)) return; const char *name = pkgName(pkg2); const char *evr = headerSprintf(pkg2->header, "%|epoch?{%{epoch}:}|%{version}-%{release}", rpmTagTable, rpmHeaderFormats, NULL); assert(evr); int flags = RPMSENSE_EQUAL | RPMSENSE_FIND_REQUIRES; if (addReqProv(NULL, pkg1->header, flags, name, evr, 0) == 0) fprintf(stderr, "%s: adding strict dependency on %s\n", pkgName(pkg1), pkgName(pkg2)); evr = _free(evr); } static void liftDebuginfoDeps(struct Req *r, Spec spec) { processDependentDebuginfo(r, spec, liftDeps1, 1); } // assuming pkg1 has strict dependency on pkg2, prune deps provided by pkg2 static void pruneDeps1(Package pkg1, Package pkg2) { TFI_t fi = pkg1->cpioList; if (fi == NULL) return; int reqc = 0; const char **reqNv = NULL; const char **reqVv = NULL; const int *reqFv_ = NULL; int ok = fi->hge(pkg1->header, RPMTAG_REQUIRENAME, NULL, (void **) &reqNv, &reqc) && fi->hge(pkg1->header, RPMTAG_REQUIREVERSION, NULL, (void **) &reqVv, NULL) && fi->hge(pkg1->header, RPMTAG_REQUIREFLAGS, NULL, (void **) &reqFv_, NULL); if (!ok) return; assert(reqc > 0); int i, j; int reqFv[reqc]; for (i = 0; i < reqc; i++) reqFv[i] = reqFv_[i]; availableList proval = alloca(sizeof proval); alCreate(proval); alAddPackage(proval, pkg2->header, NULL, NULL, NULL); int flags = 0, *flagsp = NULL; struct availablePackage *ap = NULL; int rpmlibSetVersions = -1; int hasSetVersions = 0; int npruned = 0; char pruned[reqc]; bzero(pruned, reqc); for (i = 0; i < reqc; i++) { if ((reqFv[i] & RPMSENSE_SENSEMASK) == RPMSENSE_EQUAL && strcmp(reqNv[i], pkgName(pkg2)) == 0) { if (flagsp == NULL) flagsp = &reqFv[i]; continue; } if ((reqFv[i] & RPMSENSE_RPMLIB) && strcmp(reqNv[i], "rpmlib(SetVersions)") == 0) { if (rpmlibSetVersions == -1) rpmlibSetVersions = i; continue; } ap = alSatisfiesDepend(proval, reqNv[i], reqVv[i], reqFv[i]); if (ap == NULL) { if ((reqFv[i] & RPMSENSE_SENSEMASK)) if (strncmp(reqVv[i], "set:", 4) == 0) hasSetVersions++; continue; } pruned[i] = 1; npruned++; flags |= reqFv[i]; } alFree(proval); if (npruned == 0) { reqNv = fi->hfd(reqNv, RPM_STRING_ARRAY_TYPE); reqVv = fi->hfd(reqVv, RPM_STRING_ARRAY_TYPE); return; } if (hasSetVersions == 0 && rpmlibSetVersions >= 0) { pruned[rpmlibSetVersions] = 1; npruned++; } fprintf(stderr, "removing %d extra deps from %s due to dependency on %s\n", npruned, pkgName(pkg1), pkgName(pkg2)); if (flagsp) *flagsp |= (flags & RPMSENSE_PREREQ); for (i = 0, j = 0; i < reqc; i++) { if (pruned[i]) continue; if (i == j) goto skip; reqNv[j] = reqNv[i]; reqVv[j] = reqVv[i]; reqFv[j] = reqFv[i]; skip: j++; } reqc = j; fi->hme(fi->h, RPMTAG_REQUIRENAME, RPM_STRING_ARRAY_TYPE, reqNv, reqc); fi->hme(fi->h, RPMTAG_REQUIREVERSION, RPM_STRING_ARRAY_TYPE, reqVv, reqc); fi->hme(fi->h, RPMTAG_REQUIREFLAGS, RPM_INT32_TYPE, reqFv, reqc); reqNv = fi->hfd(reqNv, RPM_STRING_ARRAY_TYPE); reqVv = fi->hfd(reqVv, RPM_STRING_ARRAY_TYPE); } static void pruneExtraDeps(struct Req *r, Spec spec) { int i; for (i = 0; i < r->c; i++) pruneDeps1(r->v[i].pkg1, r->v[i].pkg2); } // assuming pkg1 has strict dependency on pkg2, prune deps required by pkg2 static void pruneRDeps1(struct Req *r, Spec spec, Package pkg1, Package pkg2) { TFI_t fi = pkg1->cpioList; if (fi == NULL) return; int reqc = 0; const char **reqNv = NULL; const char **reqVv = NULL; const int *reqFv_ = NULL; int ok = fi->hge(fi->h, RPMTAG_REQUIRENAME, NULL, (void **) &reqNv, &reqc) && fi->hge(fi->h, RPMTAG_REQUIREVERSION, NULL, (void **) &reqVv, NULL) && fi->hge(fi->h, RPMTAG_REQUIREFLAGS, NULL, (void **) &reqFv_, NULL); if (!ok) return; int i, j; int reqFv[reqc]; for (i = 0; i < reqc; i++) reqFv[i] = reqFv_[i]; int provc = 0; const char **provNv = NULL; const char **provVv = NULL; const int *provFv = NULL; const HGE_t hge = (HGE_t) headerGetEntryMinMemory; const HFD_t hfd = (HFD_t) headerFreeData; ok = hge(pkg2->header, RPMTAG_REQUIRENAME, NULL, (void **) &provNv, &provc) && hge(pkg2->header, RPMTAG_REQUIREVERSION, NULL, (void **) &provVv, NULL) && hge(pkg2->header, RPMTAG_REQUIREFLAGS, NULL, (void **) &provFv, NULL); if (!ok) { reqNv = fi->hfd(reqNv, RPM_STRING_ARRAY_TYPE); reqVv = fi->hfd(reqVv, RPM_STRING_ARRAY_TYPE); return; } int npruned = 0; char pruned[reqc]; bzero(pruned, reqc); // check if pkg2 is part of a cycle int cycle = 0; Package pkg3; for (pkg3 = spec->packages; pkg3; pkg3 = pkg3->next) { if (pkg3 == pkg1 || pkg3 == pkg2) continue; if (Requires(r, pkg2, pkg3) && Requires(r, pkg3, pkg2)) { cycle = 1; break; } } void prune(int i, int j) { if (strcmp(reqNv[i], provNv[j])) return; if (cycle && (reqFv[i] & RPMSENSE_SENSEMASK) == RPMSENSE_EQUAL) return; dep_compare_t cmp = compare_deps(RPMTAG_REQUIRENAME, provVv[j], provFv[j], reqVv[i], reqFv[i]); if (!(cmp == DEP_ST || cmp == DEP_EQ)) return; pruned[i] = 1; npruned++; } for (i = 0; i < reqc; i++) for (j = 0; j < provc; j++) prune(i, j); if (npruned == 0) goto free; fprintf(stderr, "removing %d extra deps from %s due to repentancy on %s\n", npruned, pkgName(pkg1), pkgName(pkg2)); for (i = 0, j = 0; i < reqc; i++) { if (pruned[i]) continue; if (i == j) goto skip; reqNv[j] = reqNv[i]; reqVv[j] = reqVv[i]; reqFv[j] = reqFv[i]; skip: j++; } reqc = j; fi->hme(fi->h, RPMTAG_REQUIRENAME, RPM_STRING_ARRAY_TYPE, reqNv, reqc); fi->hme(fi->h, RPMTAG_REQUIREVERSION, RPM_STRING_ARRAY_TYPE, reqVv, reqc); fi->hme(fi->h, RPMTAG_REQUIREFLAGS, RPM_INT32_TYPE, reqFv, reqc); free: reqNv = fi->hfd(reqNv, RPM_STRING_ARRAY_TYPE); reqVv = fi->hfd(reqVv, RPM_STRING_ARRAY_TYPE); provNv = hfd(provNv, RPM_STRING_ARRAY_TYPE); provVv = hfd(provVv, RPM_STRING_ARRAY_TYPE); } static void pruneExtraRDeps(struct Req *r, Spec spec) { Package pkg1, pkg2; for (pkg1 = spec->packages; pkg1; pkg1 = pkg1->next) for (pkg2 = pkg1->next; pkg2; pkg2 = pkg2->next) if (Requires(r, pkg1, pkg2)) pruneRDeps1(r, spec, pkg1, pkg2); // "else" guards against mutual deletions else if (Requires(r, pkg2, pkg1)) pruneRDeps1(r, spec, pkg2, pkg1); } int processInterdep(Spec spec) { struct Req *r = makeRequires(spec, 1); pruneDebuginfoSrc(r, spec); liftDebuginfoDeps(r, spec); r = freeRequires(r); int optlevel = rpmExpandNumeric("%{?_deps_optimization}%{?!_deps_optimization:2}"); if (optlevel < 2) return 0; r = makeRequires(spec, 2); pruneExtraDeps(r, spec); pruneExtraRDeps(r, spec); r = freeRequires(r); return 0; } // ex: set ts=8 sts=4 sw=4 noet: