1 /* 2 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 3. Neither the name of The DragonFly Project nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific, prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 /* 38 * Figure out what would have to be built [N]ew, [R]ebuild, [U]pgrade 39 * but do not perform any action. 40 */ 41 #include "dsynth.h" 42 43 static int status_find_leaves(pkg_t *parent, pkg_t *pkg, pkg_t ***build_tailp, 44 int *app, int *hasworkp, int level, int first); 45 static void status_clear_trav(pkg_t *pkg); 46 static void startstatus(pkg_t **build_listp, pkg_t ***build_tailp); 47 48 void 49 DoStatus(pkg_t *pkgs) 50 { 51 pkg_t *build_list = NULL; 52 pkg_t **build_tail = &build_list; 53 pkg_t *scan; 54 int haswork = 1; 55 int first = 1; 56 57 for (scan = pkgs; scan; scan = scan->bnext) 58 ++BuildTotal; 59 60 /* 61 * Nominal bulk build sequence 62 */ 63 while (haswork) { 64 haswork = 0; 65 fflush(stdout); 66 for (scan = pkgs; scan; scan = scan->bnext) { 67 ddprintf(0, "SCANLEAVES %08x %s\n", 68 scan->flags, scan->portdir); 69 scan->flags |= PKGF_BUILDLOOP; 70 /* 71 * NOTE: We must still find dependencies if PACKAGED 72 * to fill in the gaps, as some of them may 73 * need to be rebuilt. 74 */ 75 if (scan->flags & (PKGF_SUCCESS | PKGF_FAILURE | 76 PKGF_ERROR | PKGF_NOBUILD)) { 77 #if 0 78 ddprintf(0, "%s: already built\n", 79 scan->portdir); 80 #endif 81 } else { 82 int ap = 0; 83 status_find_leaves(NULL, scan, &build_tail, 84 &ap, &haswork, 0, first); 85 ddprintf(0, "TOPLEVEL %s %08x\n", 86 scan->portdir, ap); 87 } 88 scan->flags &= ~PKGF_BUILDLOOP; 89 status_clear_trav(scan); 90 } 91 first = 0; 92 fflush(stdout); 93 if (haswork == 0) 94 break; 95 startstatus(&build_list, &build_tail); 96 } 97 printf("Total packages that would be built: %d/%d\n", 98 BuildSuccessCount, BuildTotal); 99 } 100 101 /* 102 * Traverse the packages (pkg) depends on recursively until we find 103 * a leaf to build or report as unbuildable. Calculates and assigns a 104 * dependency count. Returns all parallel-buildable packages. 105 * 106 * (pkg) itself is only added to the list if it is immediately buildable. 107 */ 108 static 109 int 110 status_find_leaves(pkg_t *parent, pkg_t *pkg, pkg_t ***build_tailp, 111 int *app, int *hasworkp, int level, int first) 112 { 113 pkglink_t *link; 114 pkg_t *scan; 115 int idep_count = 0; 116 int apsub; 117 118 /* 119 * Already on build list, possibly in-progress, tell caller that 120 * it is not ready. 121 */ 122 ddprintf(level, "sbuild_find_leaves %d %s %08x {\n", 123 level, pkg->portdir, pkg->flags); 124 if (pkg->flags & PKGF_BUILDLIST) { 125 ddprintf(level, "} (already on build list)\n"); 126 *app |= PKGF_NOTREADY; 127 return (pkg->idep_count); 128 } 129 130 /* 131 * Check dependencies 132 */ 133 ++level; 134 PKGLIST_FOREACH(link, &pkg->idepon_list) { 135 scan = link->pkg; 136 137 if (scan == NULL) 138 continue; 139 ddprintf(level, "check %s %08x\t", scan->portdir, scan->flags); 140 141 /* 142 * When accounting for a successful build, just bump 143 * idep_count by one. scan->idep_count will heavily 144 * overlap packages that we count down multiple branches. 145 * 146 * We must still recurse through PACKAGED packages as 147 * some of their dependencies might be missing. 148 */ 149 if (scan->flags & PKGF_SUCCESS) { 150 ddprintf(0, "SUCCESS - OK\n"); 151 ++idep_count; 152 continue; 153 } 154 if (scan->flags & PKGF_ERROR) { 155 ddprintf(0, "ERROR - OK (propogate failure upward)\n"); 156 *app |= PKGF_NOBUILD_S; 157 continue; 158 } 159 if (scan->flags & PKGF_NOBUILD) { 160 ddprintf(0, "NOBUILD - OK " 161 "(propogate failure upward)\n"); 162 *app |= PKGF_NOBUILD_S; 163 continue; 164 } 165 166 /* 167 * If already on build-list this dependency is not ready. 168 */ 169 if (scan->flags & PKGF_BUILDLIST) { 170 ddprintf(0, " [BUILDLIST]"); 171 *app |= PKGF_NOTREADY; 172 } 173 174 /* 175 * If not packaged this dependency is not ready for 176 * the caller. 177 */ 178 if ((scan->flags & PKGF_PACKAGED) == 0) { 179 ddprintf(0, " [NOT_PACKAGED]"); 180 *app |= PKGF_NOTREADY; 181 } 182 183 /* 184 * Reduce search complexity, if we have already processed 185 * scan in the traversal it will either already be on the 186 * build list or it will not be buildable. Either way 187 * the parent is not buildable. 188 */ 189 if (scan->flags & PKGF_BUILDTRAV) { 190 ddprintf(0, " [BUILDTRAV]\n"); 191 *app |= PKGF_NOTREADY; 192 continue; 193 } 194 195 /* 196 * Assert on dependency loop 197 */ 198 ++idep_count; 199 if (scan->flags & PKGF_BUILDLOOP) { 200 dfatal("pkg dependency loop %s -> %s", 201 parent->portdir, scan->portdir); 202 } 203 scan->flags |= PKGF_BUILDLOOP; 204 apsub = 0; 205 ddprintf(0, " SUBRECURSION {\n"); 206 idep_count += status_find_leaves(pkg, scan, build_tailp, 207 &apsub, hasworkp, 208 level + 1, first); 209 scan->flags &= ~PKGF_BUILDLOOP; 210 *app |= apsub; 211 if (apsub & PKGF_NOBUILD) { 212 ddprintf(level, "} (sub-nobuild)\n"); 213 } else if (apsub & PKGF_ERROR) { 214 ddprintf(level, "} (sub-error)\n"); 215 } else if (apsub & PKGF_NOTREADY) { 216 ddprintf(level, "} (sub-notready)\n"); 217 } else { 218 ddprintf(level, "} (sub-ok)\n"); 219 } 220 } 221 --level; 222 pkg->idep_count = idep_count; 223 pkg->flags |= PKGF_BUILDTRAV; 224 225 /* 226 * Incorporate scan results into pkg state. 227 */ 228 if ((pkg->flags & PKGF_NOBUILD) == 0 && (*app & PKGF_NOBUILD)) { 229 *hasworkp = 1; 230 } else if ((pkg->flags & PKGF_ERROR) == 0 && (*app & PKGF_ERROR)) { 231 *hasworkp = 1; 232 } 233 pkg->flags |= *app & ~PKGF_NOTREADY; 234 235 /* 236 * Clear PACKAGED bit if sub-dependencies aren't clean 237 */ 238 if ((pkg->flags & PKGF_PACKAGED) && 239 (pkg->flags & (PKGF_NOTREADY|PKGF_ERROR|PKGF_NOBUILD))) { 240 pkg->flags &= ~PKGF_PACKAGED; 241 ddassert(pkg->pkgfile); 242 *hasworkp = 1; 243 } 244 245 /* 246 * Handle propagated flags 247 */ 248 if (pkg->flags & PKGF_ERROR) { 249 ddprintf(level, "} (ERROR - %s)\n", pkg->portdir); 250 } else if (pkg->flags & PKGF_NOBUILD) { 251 ddprintf(level, "} (SKIPPED - %s)\n", pkg->portdir); 252 } else if (*app & PKGF_NOTREADY) { 253 /* 254 * We don't set PKGF_NOTREADY in the pkg, it is strictly 255 * a transient flag propagated via build_find_leaves(). 256 * 257 * Just don't add the package to the list. 258 */ 259 ; 260 } else if (pkg->flags & PKGF_SUCCESS) { 261 ddprintf(level, "} (SUCCESS - %s)\n", pkg->portdir); 262 } else if (pkg->flags & PKGF_DUMMY) { 263 ddprintf(level, "} (DUMMY/META - SUCCESS)\n"); 264 pkg->flags |= PKGF_SUCCESS; 265 *hasworkp = 1; 266 if (first) { 267 dlog(DLOG_ALL | DLOG_FILTER, 268 "[XXX] %s META-ALREADY-BUILT\n", 269 pkg->portdir); 270 --BuildTotal; 271 } else { 272 dlog(DLOG_SUCC | DLOG_FILTER, 273 "[XXX] %s meta-node complete\n", 274 pkg->portdir); 275 } 276 } else if (pkg->flags & PKGF_PACKAGED) { 277 /* 278 * We can just mark the pkg successful. If this is 279 * the first pass, we count this as an initial pruning 280 * pass and reduce BuildTotal. 281 */ 282 ddprintf(level, "} (PACKAGED - SUCCESS)\n"); 283 pkg->flags |= PKGF_SUCCESS; 284 *hasworkp = 1; 285 if (first) { 286 dlog(DLOG_ALL | DLOG_FILTER, 287 "[XXX] %s ALREADY-BUILT\n", 288 pkg->portdir); 289 --BuildTotal; 290 } 291 } else { 292 /* 293 * All dependencies are successful, queue new work 294 * and indicate not-ready to the parent (since our 295 * package has to be built). 296 */ 297 *hasworkp = 1; 298 ddprintf(level, "} (ADDLIST - %s)\n", pkg->portdir); 299 pkg->flags |= PKGF_BUILDLIST; 300 **build_tailp = pkg; 301 *build_tailp = &pkg->build_next; 302 *app |= PKGF_NOTREADY; 303 } 304 305 return idep_count; 306 } 307 308 static 309 void 310 status_clear_trav(pkg_t *pkg) 311 { 312 pkglink_t *link; 313 pkg_t *scan; 314 315 pkg->flags &= ~PKGF_BUILDTRAV; 316 PKGLIST_FOREACH(link, &pkg->idepon_list) { 317 scan = link->pkg; 318 if (scan && (scan->flags & PKGF_BUILDTRAV)) 319 status_clear_trav(scan); 320 } 321 } 322 323 /* 324 * This is a fake startbuild() which just marks the build list as built, 325 * allowing us to resolve the build tree. 326 */ 327 static 328 void 329 startstatus(pkg_t **build_listp, pkg_t ***build_tailp) 330 { 331 pkg_t *pkg; 332 333 /* 334 * Nothing to do 335 */ 336 if (*build_listp == NULL) 337 return; 338 339 /* 340 * Sort 341 */ 342 for (pkg = *build_listp; pkg; pkg = pkg->build_next) { 343 if ((pkg->flags & (PKGF_SUCCESS | PKGF_FAILURE | 344 PKGF_ERROR | PKGF_NOBUILD | 345 PKGF_RUNNING)) == 0) { 346 pkg->flags |= PKGF_SUCCESS; 347 ++BuildSuccessCount; 348 ++BuildCount; 349 pkg->flags &= ~PKGF_BUILDLIST; 350 printf(" N => %s\n", pkg->portdir); 351 /* XXX [R]ebuild and [U]pgrade */ 352 } 353 354 } 355 *build_listp = NULL; 356 *build_tailp = build_listp; 357 } 358