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
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
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
38 * Figure out what would have to be built [N]ew, [R]ebuild, [U]pgrade
39 * but do not perform any action.
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
);
51 pkg_t
*build_list
= NULL
;
52 pkg_t
**build_tail
= &build_list
;
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)
66 * Nominal bulk build sequence
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
80 if (scan
->flags
& (PKGF_SUCCESS
| PKGF_FAILURE
|
81 PKGF_ERROR
| PKGF_NOBUILD
)) {
83 ddprintf(0, "%s: already built\n",
88 status_find_leaves(NULL
, scan
, &build_tail
,
89 &ap
, &haswork
, 0, first
);
90 ddprintf(0, "TOPLEVEL %s %08x\n",
93 scan
->flags
&= ~PKGF_BUILDLOOP
;
94 status_clear_trav(scan
);
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.
115 status_find_leaves(pkg_t
*parent
, pkg_t
*pkg
, pkg_t
***build_tailp
,
116 int *app
, int *hasworkp
, int level
, int first
)
124 * Already on build list, possibly in-progress, tell caller that
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
);
139 PKGLIST_FOREACH(link
, &pkg
->idepon_list
) {
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");
159 if (scan
->flags
& PKGF_ERROR
) {
160 ddprintf(0, "ERROR - OK (propagate failure upward)\n");
161 *app
|= PKGF_NOBUILD_S
;
164 if (scan
->flags
& PKGF_NOBUILD
) {
165 ddprintf(0, "NOBUILD - OK "
166 "(propagate failure upward)\n");
167 *app
|= PKGF_NOBUILD_S
;
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
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
;
201 * Assert on dependency loop
204 if (scan
->flags
& PKGF_BUILDLOOP
) {
205 dfatal("pkg dependency loop %s -> %s",
206 parent
->portdir
, scan
->portdir
);
208 scan
->flags
|= PKGF_BUILDLOOP
;
210 ddprintf(0, " SUBRECURSION {\n");
211 idep_count
+= status_find_leaves(pkg
, scan
, build_tailp
,
214 scan
->flags
&= ~PKGF_BUILDLOOP
;
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");
223 ddprintf(level
, "} (sub-ok)\n");
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
)) {
235 } else if ((pkg
->flags
& PKGF_ERROR
) == 0 && (*app
& PKGF_ERROR
)) {
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
);
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
;
278 dlog(DLOG_ALL
| DLOG_FILTER
,
279 "[XXX] %s META-ALREADY-BUILT\n",
282 dlog(DLOG_SUCC
| DLOG_FILTER
,
283 "[XXX] %s meta-node complete\n",
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
;
296 dlog(DLOG_ALL
| DLOG_FILTER
,
297 "[XXX] %s ALREADY-BUILT\n",
303 * All dependencies are successful, queue new work
304 * and indicate not-ready to the parent (since our
305 * package has to be built).
308 ddprintf(level
, "} (ADDLIST - %s)\n", pkg
->portdir
);
309 pkg
->flags
|= PKGF_BUILDLIST
;
311 *build_tailp
= &pkg
->build_next
;
312 *app
|= PKGF_NOTREADY
;
320 status_clear_trav(pkg_t
*pkg
)
325 pkg
->flags
&= ~PKGF_BUILDTRAV
;
326 PKGLIST_FOREACH(link
, &pkg
->idepon_list
) {
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.
339 startstatus(pkg_t
**build_listp
, pkg_t
***build_tailp
)
346 if (*build_listp
== NULL
)
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
;
359 pkg
->flags
&= ~PKGF_BUILDLIST
;
360 printf(" N => %s\n", pkg
->portdir
);
361 /* XXX [R]ebuild and [U]pgrade */
366 *build_tailp
= build_listp
;