usr.sbin/makefs: Sync with sys/vfs/hammer2
[dragonfly.git] / usr.bin / dsynth / status.c
blobd883040af2c58320e5f5c6260d62761fd380d165
1 /*
2 * Copyright (c) 2019 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * This code uses concepts and configuration based on 'synth', by
8 * John R. Marino <draco@marino.st>, which was written in ada.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
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.
38 * Figure out what would have to be built [N]ew, [R]ebuild, [U]pgrade
39 * but do not perform any action.
41 #include "dsynth.h"
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);
48 void
49 DoStatus(pkg_t *pkgs)
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;
58 * Count up all the packages, do not include dummy packages.
60 for (scan = pkgs; scan; scan = scan->bnext) {
61 if ((scan->flags & PKGF_DUMMY) == 0)
62 ++BuildTotal;
66 * Nominal bulk build sequence
68 while (haswork) {
69 haswork = 0;
70 fflush(stdout);
71 for (scan = pkgs; scan; scan = scan->bnext) {
72 ddprintf(0, "SCANLEAVES %08x %s\n",
73 scan->flags, scan->portdir);
74 scan->flags |= PKGF_BUILDLOOP;
76 * NOTE: We must still find dependencies if PACKAGED
77 * to fill in the gaps, as some of them may
78 * need to be rebuilt.
80 if (scan->flags & (PKGF_SUCCESS | PKGF_FAILURE |
81 PKGF_ERROR | PKGF_NOBUILD)) {
82 #if 0
83 ddprintf(0, "%s: already built\n",
84 scan->portdir);
85 #endif
86 } else {
87 int ap = 0;
88 status_find_leaves(NULL, scan, &build_tail,
89 &ap, &haswork, 0, first);
90 ddprintf(0, "TOPLEVEL %s %08x\n",
91 scan->portdir, ap);
93 scan->flags &= ~PKGF_BUILDLOOP;
94 status_clear_trav(scan);
96 first = 0;
97 fflush(stdout);
98 if (haswork == 0)
99 break;
100 startstatus(&build_list, &build_tail);
102 printf("Total packages that would be built: %d/%d\n",
103 BuildSuccessCount, BuildTotal);
107 * Traverse the packages (pkg) depends on recursively until we find
108 * a leaf to build or report as unbuildable. Calculates and assigns a
109 * dependency count. Returns all parallel-buildable packages.
111 * (pkg) itself is only added to the list if it is immediately buildable.
113 static
115 status_find_leaves(pkg_t *parent, pkg_t *pkg, pkg_t ***build_tailp,
116 int *app, int *hasworkp, int level, int first)
118 pkglink_t *link;
119 pkg_t *scan;
120 int idep_count = 0;
121 int apsub;
124 * Already on build list, possibly in-progress, tell caller that
125 * it is not ready.
127 ddprintf(level, "sbuild_find_leaves %d %s %08x {\n",
128 level, pkg->portdir, pkg->flags);
129 if (pkg->flags & PKGF_BUILDLIST) {
130 ddprintf(level, "} (already on build list)\n");
131 *app |= PKGF_NOTREADY;
132 return (pkg->idep_count);
136 * Check dependencies
138 ++level;
139 PKGLIST_FOREACH(link, &pkg->idepon_list) {
140 scan = link->pkg;
142 if (scan == NULL)
143 continue;
144 ddprintf(level, "check %s %08x\t", scan->portdir, scan->flags);
147 * When accounting for a successful build, just bump
148 * idep_count by one. scan->idep_count will heavily
149 * overlap packages that we count down multiple branches.
151 * We must still recurse through PACKAGED packages as
152 * some of their dependencies might be missing.
154 if (scan->flags & PKGF_SUCCESS) {
155 ddprintf(0, "SUCCESS - OK\n");
156 ++idep_count;
157 continue;
159 if (scan->flags & PKGF_ERROR) {
160 ddprintf(0, "ERROR - OK (propagate failure upward)\n");
161 *app |= PKGF_NOBUILD_S;
162 continue;
164 if (scan->flags & PKGF_NOBUILD) {
165 ddprintf(0, "NOBUILD - OK "
166 "(propagate failure upward)\n");
167 *app |= PKGF_NOBUILD_S;
168 continue;
172 * If already on build-list this dependency is not ready.
174 if (scan->flags & PKGF_BUILDLIST) {
175 ddprintf(0, " [BUILDLIST]");
176 *app |= PKGF_NOTREADY;
180 * If not packaged this dependency is not ready for
181 * the caller.
183 if ((scan->flags & PKGF_PACKAGED) == 0) {
184 ddprintf(0, " [NOT_PACKAGED]");
185 *app |= PKGF_NOTREADY;
189 * Reduce search complexity, if we have already processed
190 * scan in the traversal it will either already be on the
191 * build list or it will not be buildable. Either way
192 * the parent is not buildable.
194 if (scan->flags & PKGF_BUILDTRAV) {
195 ddprintf(0, " [BUILDTRAV]\n");
196 *app |= PKGF_NOTREADY;
197 continue;
201 * Assert on dependency loop
203 ++idep_count;
204 if (scan->flags & PKGF_BUILDLOOP) {
205 dfatal("pkg dependency loop %s -> %s",
206 parent->portdir, scan->portdir);
208 scan->flags |= PKGF_BUILDLOOP;
209 apsub = 0;
210 ddprintf(0, " SUBRECURSION {\n");
211 idep_count += status_find_leaves(pkg, scan, build_tailp,
212 &apsub, hasworkp,
213 level + 1, first);
214 scan->flags &= ~PKGF_BUILDLOOP;
215 *app |= apsub;
216 if (apsub & PKGF_NOBUILD) {
217 ddprintf(level, "} (sub-nobuild)\n");
218 } else if (apsub & PKGF_ERROR) {
219 ddprintf(level, "} (sub-error)\n");
220 } else if (apsub & PKGF_NOTREADY) {
221 ddprintf(level, "} (sub-notready)\n");
222 } else {
223 ddprintf(level, "} (sub-ok)\n");
226 --level;
227 pkg->idep_count = idep_count;
228 pkg->flags |= PKGF_BUILDTRAV;
231 * Incorporate scan results into pkg state.
233 if ((pkg->flags & PKGF_NOBUILD) == 0 && (*app & PKGF_NOBUILD)) {
234 *hasworkp = 1;
235 } else if ((pkg->flags & PKGF_ERROR) == 0 && (*app & PKGF_ERROR)) {
236 *hasworkp = 1;
238 pkg->flags |= *app & ~PKGF_NOTREADY;
241 * Clear PACKAGED bit if sub-dependencies aren't clean
243 if ((pkg->flags & PKGF_PACKAGED) &&
244 (pkg->flags & (PKGF_NOTREADY|PKGF_ERROR|PKGF_NOBUILD))) {
245 pkg->flags &= ~PKGF_PACKAGED;
246 ddassert(pkg->pkgfile);
247 *hasworkp = 1;
251 * Handle propagated flags
253 if (pkg->flags & PKGF_ERROR) {
254 ddprintf(level, "} (ERROR - %s)\n", pkg->portdir);
255 } else if (pkg->flags & PKGF_NOBUILD) {
256 ddprintf(level, "} (SKIPPED - %s)\n", pkg->portdir);
257 } else if (*app & PKGF_NOTREADY) {
259 * We don't set PKGF_NOTREADY in the pkg, it is strictly
260 * a transient flag propagated via build_find_leaves().
262 * Just don't add the package to the list.
265 } else if (pkg->flags & PKGF_SUCCESS) {
266 ddprintf(level, "} (SUCCESS - %s)\n", pkg->portdir);
267 } else if (pkg->flags & PKGF_DUMMY) {
269 * Just mark dummy packages as successful when all of their
270 * sub-depends (flavors) complete successfully. Note that
271 * dummy packages are not counted in the total, so do not
272 * decrement BuildTotal.
274 ddprintf(level, "} (DUMMY/META - SUCCESS)\n");
275 pkg->flags |= PKGF_SUCCESS;
276 *hasworkp = 1;
277 if (first) {
278 dlog(DLOG_ALL | DLOG_FILTER,
279 "[XXX] %s META-ALREADY-BUILT\n",
280 pkg->portdir);
281 } else {
282 dlog(DLOG_SUCC | DLOG_FILTER,
283 "[XXX] %s meta-node complete\n",
284 pkg->portdir);
286 } else if (pkg->flags & PKGF_PACKAGED) {
288 * We can just mark the pkg successful. If this is
289 * the first pass, we count this as an initial pruning
290 * pass and reduce BuildTotal.
292 ddprintf(level, "} (PACKAGED - SUCCESS)\n");
293 pkg->flags |= PKGF_SUCCESS;
294 *hasworkp = 1;
295 if (first) {
296 dlog(DLOG_ALL | DLOG_FILTER,
297 "[XXX] %s ALREADY-BUILT\n",
298 pkg->portdir);
299 --BuildTotal;
301 } else {
303 * All dependencies are successful, queue new work
304 * and indicate not-ready to the parent (since our
305 * package has to be built).
307 *hasworkp = 1;
308 ddprintf(level, "} (ADDLIST - %s)\n", pkg->portdir);
309 pkg->flags |= PKGF_BUILDLIST;
310 **build_tailp = pkg;
311 *build_tailp = &pkg->build_next;
312 *app |= PKGF_NOTREADY;
315 return idep_count;
318 static
319 void
320 status_clear_trav(pkg_t *pkg)
322 pkglink_t *link;
323 pkg_t *scan;
325 pkg->flags &= ~PKGF_BUILDTRAV;
326 PKGLIST_FOREACH(link, &pkg->idepon_list) {
327 scan = link->pkg;
328 if (scan && (scan->flags & PKGF_BUILDTRAV))
329 status_clear_trav(scan);
334 * This is a fake startbuild() which just marks the build list as built,
335 * allowing us to resolve the build tree.
337 static
338 void
339 startstatus(pkg_t **build_listp, pkg_t ***build_tailp)
341 pkg_t *pkg;
344 * Nothing to do
346 if (*build_listp == NULL)
347 return;
350 * Sort
352 for (pkg = *build_listp; pkg; pkg = pkg->build_next) {
353 if ((pkg->flags & (PKGF_SUCCESS | PKGF_FAILURE |
354 PKGF_ERROR | PKGF_NOBUILD |
355 PKGF_RUNNING)) == 0) {
356 pkg->flags |= PKGF_SUCCESS;
357 ++BuildSuccessCount;
358 ++BuildCount;
359 pkg->flags &= ~PKGF_BUILDLIST;
360 printf(" N => %s\n", pkg->portdir);
361 /* XXX [R]ebuild and [U]pgrade */
365 *build_listp = NULL;
366 *build_tailp = build_listp;