From def2c2dfaf5374f139b310c4f05b0614cb99359e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 28 Mar 2022 15:04:35 +0200 Subject: Implement dependency configuration negotiation For the detailed history see the dep-config and dep-config-neg branches. --- bpkg/auth.cxx | 1 - bpkg/checksum.cxx | 2 +- bpkg/checksum.hxx | 6 +- bpkg/database.cxx | 4 +- bpkg/fetch-pkg.cxx | 10 +- bpkg/manifest-utility.cxx | 1 - bpkg/package-configuration.cxx | 491 ++ bpkg/package-configuration.hxx | 206 + bpkg/package-skeleton.cxx | 2742 +++++- bpkg/package-skeleton.hxx | 275 +- bpkg/package.cxx | 34 +- bpkg/package.hxx | 119 +- bpkg/package.xml | 7 + bpkg/pkg-build.cli | 11 +- bpkg/pkg-build.cxx | 8307 +++++++++++++----- bpkg/pkg-configure.cxx | 196 +- bpkg/pkg-configure.hxx | 15 +- bpkg/pkg-drop.cxx | 12 +- bpkg/pkg-fetch.cxx | 6 +- bpkg/pkg-status.cxx | 2 +- bpkg/rep-create.cxx | 18 +- bpkg/rep-info.cxx | 1 - bpkg/types.hxx | 7 + bpkg/utility.hxx | 4 + .../dependency-alternatives/t11a/bac-1.0.0.tar.gz | Bin 0 -> 472 bytes .../dependency-alternatives/t11a/bar-0.1.0.tar.gz | Bin 0 -> 462 bytes .../dependency-alternatives/t11a/bar-1.0.0.tar.gz | Bin 0 -> 454 bytes .../dependency-alternatives/t11a/bas-1.0.0.tar.gz | Bin 0 -> 465 bytes .../dependency-alternatives/t11a/bat-1.0.0.tar.gz | Bin 0 -> 452 bytes .../dependency-alternatives/t11a/bax-1.0.0.tar.gz | Bin 0 -> 435 bytes .../dependency-alternatives/t11a/baz-0.1.0.tar.gz | Bin 0 -> 421 bytes .../dependency-alternatives/t11a/baz-1.0.0.tar.gz | Bin 0 -> 413 bytes .../dependency-alternatives/t11a/bex-1.0.0.tar.gz | Bin 0 -> 357 bytes .../dependency-alternatives/t11a/bix-1.0.0.tar.gz | Bin 0 -> 414 bytes .../dependency-alternatives/t11a/biz-1.0.0.tar.gz | Bin 0 -> 449 bytes .../dependency-alternatives/t11a/boo-1.0.0.tar.gz | Bin 0 -> 460 bytes .../dependency-alternatives/t11a/box-0.1.0.tar.gz | Bin 0 -> 402 bytes .../dependency-alternatives/t11a/box-1.0.0.tar.gz | Bin 0 -> 415 bytes .../dependency-alternatives/t11a/buc-1.0.0.tar.gz | Bin 0 -> 465 bytes .../dependency-alternatives/t11a/bus-1.0.0.tar.gz | Bin 0 -> 464 bytes .../dependency-alternatives/t11a/bux-1.0.0.tar.gz | Bin 0 -> 454 bytes .../dependency-alternatives/t11a/buz-1.0.0.tar.gz | Bin 0 -> 449 bytes .../dependency-alternatives/t11a/dex-1.0.0.tar.gz | Bin 0 -> 465 bytes .../dependency-alternatives/t11a/dix-1.0.0.tar.gz | Bin 0 -> 472 bytes .../dependency-alternatives/t11a/diz-1.0.0.tar.gz | Bin 0 -> 473 bytes .../dependency-alternatives/t11a/dox-1.0.0.tar.gz | Bin 0 -> 445 bytes .../dependency-alternatives/t11a/fex-1.0.0.tar.gz | Bin 0 -> 455 bytes .../dependency-alternatives/t11a/fix-0.1.0.tar.gz | Bin 0 -> 421 bytes .../dependency-alternatives/t11a/fix-1.0.0.tar.gz | Bin 0 -> 450 bytes .../dependency-alternatives/t11a/foo-0.1.0.tar.gz | Bin 0 -> 454 bytes .../dependency-alternatives/t11a/foo-1.0.0.tar.gz | Bin 0 -> 456 bytes .../dependency-alternatives/t11a/fox-1.0.0.tar.gz | Bin 0 -> 399 bytes .../dependency-alternatives/t11a/fux-1.0.0.tar.gz | Bin 0 -> 419 bytes .../t11a/libbar-0.1.0.tar.gz | Bin 0 -> 409 bytes .../t11a/libbar-1.0.0.tar.gz | Bin 0 -> 406 bytes .../t11a/libbaz-0.1.0.tar.gz | Bin 0 -> 412 bytes .../t11a/libbaz-1.0.0.tar.gz | Bin 0 -> 410 bytes .../t11a/libbox-0.1.0.tar.gz | Bin 0 -> 411 bytes .../t11a/libbox-1.0.0.tar.gz | Bin 0 -> 408 bytes .../t11a/libfoo-0.1.0.tar.gz | Bin 0 -> 411 bytes .../t11a/libfoo-1.0.0.tar.gz | Bin 0 -> 409 bytes .../t11a/repositories.manifest | 1 + .../dependency-alternatives/t11a/tax-1.0.0.tar.gz | Bin 0 -> 462 bytes .../dependency-alternatives/t11a/tex-0.1.0.tar.gz | Bin 0 -> 451 bytes .../dependency-alternatives/t11a/tex-0.2.0.tar.gz | Bin 0 -> 462 bytes .../dependency-alternatives/t11a/tex-0.3.0.tar.gz | Bin 0 -> 466 bytes .../dependency-alternatives/t11a/tex-1.0.0.tar.gz | Bin 0 -> 461 bytes .../dependency-alternatives/t11a/tez-1.0.0.tar.gz | Bin 0 -> 481 bytes .../dependency-alternatives/t11a/tix-0.1.0.tar.gz | Bin 0 -> 407 bytes .../dependency-alternatives/t11a/tix-1.0.0.tar.gz | Bin 0 -> 460 bytes .../dependency-alternatives/t11a/tiz-1.0.0.tar.gz | Bin 0 -> 464 bytes .../dependency-alternatives/t11a/toz-0.1.0.tar.gz | Bin 0 -> 406 bytes .../dependency-alternatives/t11a/toz-0.2.0.tar.gz | Bin 0 -> 464 bytes .../dependency-alternatives/t11a/toz-1.0.0.tar.gz | Bin 0 -> 482 bytes .../dependency-alternatives/t11a/tux-1.0.0.tar.gz | Bin 0 -> 469 bytes .../dependency-alternatives/t13a/bar-1.0.0.tar.gz | Bin 0 -> 530 bytes .../dependency-alternatives/t13a/baz-1.0.0.tar.gz | Bin 0 -> 527 bytes .../dependency-alternatives/t13a/biz-1.0.0.tar.gz | Bin 0 -> 450 bytes .../dependency-alternatives/t13a/liba-1.0.0.tar.gz | Bin 0 -> 456 bytes .../t13a/repositories.manifest | 1 + .../dependency-alternatives/t13b/bar-1.0.0.tar.gz | Bin 0 -> 439 bytes .../dependency-alternatives/t13b/baz-1.0.0.tar.gz | Bin 0 -> 439 bytes .../dependency-alternatives/t13b/biz-1.0.0.tar.gz | Bin 0 -> 399 bytes .../dependency-alternatives/t13b/liba-1.0.0.tar.gz | Bin 0 -> 431 bytes .../t13b/repositories.manifest | 1 + .../dependency-alternatives/t13c/bar-1.0.0.tar.gz | Bin 0 -> 468 bytes .../dependency-alternatives/t13c/baz-1.0.0.tar.gz | Bin 0 -> 403 bytes .../dependency-alternatives/t13c/liba-1.0.0.tar.gz | Bin 0 -> 401 bytes .../t13c/repositories.manifest | 1 + .../dependency-alternatives/t13d/bar-1.0.0.tar.gz | Bin 0 -> 539 bytes .../dependency-alternatives/t13d/baz-1.0.0.tar.gz | Bin 0 -> 407 bytes .../dependency-alternatives/t13d/liba-1.0.0.tar.gz | Bin 0 -> 401 bytes .../dependency-alternatives/t13d/libb-1.0.0.tar.gz | Bin 0 -> 347 bytes .../t13d/repositories.manifest | 1 + .../dependency-alternatives/t13e/bar-1.0.0.tar.gz | Bin 0 -> 443 bytes .../dependency-alternatives/t13e/baz-1.0.0.tar.gz | Bin 0 -> 448 bytes .../dependency-alternatives/t13e/biz-1.0.0.tar.gz | Bin 0 -> 395 bytes .../dependency-alternatives/t13e/liba-1.0.0.tar.gz | Bin 0 -> 422 bytes .../t13e/repositories.manifest | 1 + .../dependency-alternatives/t13f/bar-1.0.0.tar.gz | Bin 0 -> 511 bytes .../dependency-alternatives/t13f/baz-1.0.0.tar.gz | Bin 0 -> 458 bytes .../dependency-alternatives/t13f/liba-1.0.0.tar.gz | Bin 0 -> 421 bytes .../t13f/repositories.manifest | 1 + .../dependency-alternatives/t13g/bar-1.0.0.tar.gz | Bin 0 -> 533 bytes .../dependency-alternatives/t13g/baz-1.0.0.tar.gz | Bin 0 -> 497 bytes .../dependency-alternatives/t13g/biz-1.0.0.tar.gz | Bin 0 -> 538 bytes .../dependency-alternatives/t13g/box-1.0.0.tar.gz | Bin 0 -> 535 bytes .../dependency-alternatives/t13g/liba-1.0.0.tar.gz | Bin 0 -> 402 bytes .../dependency-alternatives/t13g/libb-1.0.0.tar.gz | Bin 0 -> 347 bytes .../t13g/repositories.manifest | 1 + .../dependency-alternatives/t13h/bar-1.0.0.tar.gz | Bin 0 -> 492 bytes .../dependency-alternatives/t13h/baz-1.0.0.tar.gz | Bin 0 -> 416 bytes .../dependency-alternatives/t13h/liba-1.0.0.tar.gz | Bin 0 -> 421 bytes .../t13h/repositories.manifest | 1 + .../dependency-alternatives/t13i/bar-1.0.0.tar.gz | Bin 0 -> 481 bytes .../dependency-alternatives/t13i/baz-1.0.0.tar.gz | Bin 0 -> 501 bytes .../dependency-alternatives/t13i/liba-1.0.0.tar.gz | Bin 0 -> 421 bytes .../t13i/repositories.manifest | 1 + .../dependency-alternatives/t13j/bar-1.0.0.tar.gz | Bin 0 -> 508 bytes .../dependency-alternatives/t13j/baz-1.0.0.tar.gz | Bin 0 -> 398 bytes .../dependency-alternatives/t13j/biz-1.0.0.tar.gz | Bin 0 -> 503 bytes .../dependency-alternatives/t13j/liba-1.0.0.tar.gz | Bin 0 -> 494 bytes .../t13j/repositories.manifest | 1 + .../dependency-alternatives/t13k/bar-1.0.0.tar.gz | Bin 0 -> 505 bytes .../dependency-alternatives/t13k/baz-1.0.0.tar.gz | Bin 0 -> 417 bytes .../dependency-alternatives/t13k/liba-1.0.0.tar.gz | Bin 0 -> 421 bytes .../t13k/repositories.manifest | 1 + .../dependency-alternatives/t13l/bar-1.0.0.tar.gz | Bin 0 -> 433 bytes .../dependency-alternatives/t13l/baz-1.0.0.tar.gz | Bin 0 -> 439 bytes .../dependency-alternatives/t13l/liba-1.0.0.tar.gz | Bin 0 -> 404 bytes .../dependency-alternatives/t13l/libb-1.0.0.tar.gz | Bin 0 -> 354 bytes .../t13l/repositories.manifest | 1 + .../dependency-alternatives/t13m/bar-1.0.0.tar.gz | Bin 0 -> 401 bytes .../dependency-alternatives/t13m/baz-1.0.0.tar.gz | Bin 0 -> 531 bytes .../dependency-alternatives/t13m/bix-1.0.0.tar.gz | Bin 0 -> 407 bytes .../dependency-alternatives/t13m/biz-1.0.0.tar.gz | Bin 0 -> 411 bytes .../dependency-alternatives/t13m/box-1.0.0.tar.gz | Bin 0 -> 414 bytes .../dependency-alternatives/t13m/liba-1.0.0.tar.gz | Bin 0 -> 422 bytes .../t13m/repositories.manifest | 1 + .../dependency-alternatives/t13n/bar-1.0.0.tar.gz | Bin 0 -> 482 bytes .../dependency-alternatives/t13n/liba-1.0.0.tar.gz | Bin 0 -> 351 bytes .../dependency-alternatives/t13n/libb-1.0.0.tar.gz | Bin 0 -> 354 bytes .../t13n/repositories.manifest | 1 + .../dependency-alternatives/t13o/bar-1.0.0.tar.gz | Bin 0 -> 497 bytes .../dependency-alternatives/t13o/baz-1.0.0.tar.gz | Bin 0 -> 398 bytes .../dependency-alternatives/t13o/bix-1.0.0.tar.gz | Bin 0 -> 406 bytes .../dependency-alternatives/t13o/biz-1.0.0.tar.gz | Bin 0 -> 407 bytes .../dependency-alternatives/t13o/liba-1.0.0.tar.gz | Bin 0 -> 351 bytes .../t13o/repositories.manifest | 1 + tests/common/satisfy/t12a/libbar-0.1.0.tar.gz | Bin 0 -> 386 bytes tests/common/satisfy/t12a/libbaz-1.0.0.tar.gz | Bin 0 -> 442 bytes tests/common/satisfy/t12a/repositories.manifest | 1 + tests/common/satisfy/t12b/bar-1.0.0.tar.gz | Bin 0 -> 372 bytes tests/common/satisfy/t12b/baz-0.1.0.tar.gz | Bin 0 -> 359 bytes tests/common/satisfy/t12b/baz-1.0.0.tar.gz | Bin 0 -> 366 bytes tests/common/satisfy/t12b/foo-0.1.0.tar.gz | Bin 0 -> 357 bytes tests/common/satisfy/t12b/foo-1.0.0.tar.gz | Bin 0 -> 368 bytes tests/common/satisfy/t12b/libbar-1.0.0.tar.gz | Bin 0 -> 393 bytes tests/common/satisfy/t12b/libbaz-0.1.0.tar.gz | Bin 0 -> 443 bytes tests/common/satisfy/t12b/repositories.manifest | 4 + tests/pkg-build.testscript | 8997 +++++++++++++++++++- tests/pkg-build/t11a | 1 + tests/pkg-build/t12a | 1 + tests/pkg-build/t12b | 1 + tests/pkg-build/t13a | 1 + tests/pkg-build/t13b | 1 + tests/pkg-build/t13c | 1 + tests/pkg-build/t13d | 1 + tests/pkg-build/t13e | 1 + tests/pkg-build/t13f | 1 + tests/pkg-build/t13g | 1 + tests/pkg-build/t13h | 1 + tests/pkg-build/t13i | 1 + tests/pkg-build/t13j | 1 + tests/pkg-build/t13k | 1 + tests/pkg-build/t13l | 1 + tests/pkg-build/t13m | 1 + tests/pkg-build/t13n | 1 + tests/pkg-build/t13o | 1 + 179 files changed, 18927 insertions(+), 2586 deletions(-) create mode 100644 bpkg/package-configuration.cxx create mode 100644 bpkg/package-configuration.hxx create mode 100644 tests/common/dependency-alternatives/t11a/bac-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bar-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bas-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bat-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bax-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/baz-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bex-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bix-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/boo-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/box-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/box-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/buc-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bus-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/bux-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/buz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/dex-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/dix-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/diz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/dox-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/fex-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/fix-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/fix-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/foo-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/foo-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/fox-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/fux-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libbar-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libbar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libbaz-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libbaz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libbox-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libbox-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libfoo-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/libfoo-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t11a/tax-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tex-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tex-0.2.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tex-0.3.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tex-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tez-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tix-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tix-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tiz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/toz-0.1.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/toz-0.2.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/toz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t11a/tux-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13a/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13a/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13a/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13a/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13a/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13b/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13b/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13b/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13b/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13b/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13c/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13c/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13c/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13c/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13d/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13d/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13d/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13d/libb-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13d/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13e/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13e/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13e/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13e/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13e/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13f/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13f/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13f/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13f/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13g/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13g/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13g/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13g/box-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13g/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13g/libb-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13g/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13h/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13h/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13h/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13h/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13i/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13i/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13i/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13i/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13j/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13j/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13j/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13j/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13j/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13k/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13k/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13k/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13k/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13l/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13l/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13l/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13l/libb-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13l/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13m/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13m/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13m/bix-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13m/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13m/box-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13m/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13m/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13n/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13n/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13n/libb-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13n/repositories.manifest create mode 100644 tests/common/dependency-alternatives/t13o/bar-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13o/baz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13o/bix-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13o/biz-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13o/liba-1.0.0.tar.gz create mode 100644 tests/common/dependency-alternatives/t13o/repositories.manifest create mode 100644 tests/common/satisfy/t12a/libbar-0.1.0.tar.gz create mode 100644 tests/common/satisfy/t12a/libbaz-1.0.0.tar.gz create mode 100644 tests/common/satisfy/t12a/repositories.manifest create mode 100644 tests/common/satisfy/t12b/bar-1.0.0.tar.gz create mode 100644 tests/common/satisfy/t12b/baz-0.1.0.tar.gz create mode 100644 tests/common/satisfy/t12b/baz-1.0.0.tar.gz create mode 100644 tests/common/satisfy/t12b/foo-0.1.0.tar.gz create mode 100644 tests/common/satisfy/t12b/foo-1.0.0.tar.gz create mode 100644 tests/common/satisfy/t12b/libbar-1.0.0.tar.gz create mode 100644 tests/common/satisfy/t12b/libbaz-0.1.0.tar.gz create mode 100644 tests/common/satisfy/t12b/repositories.manifest create mode 120000 tests/pkg-build/t11a create mode 120000 tests/pkg-build/t12a create mode 120000 tests/pkg-build/t12b create mode 120000 tests/pkg-build/t13a create mode 120000 tests/pkg-build/t13b create mode 120000 tests/pkg-build/t13c create mode 120000 tests/pkg-build/t13d create mode 120000 tests/pkg-build/t13e create mode 120000 tests/pkg-build/t13f create mode 120000 tests/pkg-build/t13g create mode 120000 tests/pkg-build/t13h create mode 120000 tests/pkg-build/t13i create mode 120000 tests/pkg-build/t13j create mode 120000 tests/pkg-build/t13k create mode 120000 tests/pkg-build/t13l create mode 120000 tests/pkg-build/t13m create mode 120000 tests/pkg-build/t13n create mode 120000 tests/pkg-build/t13o diff --git a/bpkg/auth.cxx b/bpkg/auth.cxx index 85cf5fa..663054d 100644 --- a/bpkg/auth.cxx +++ b/bpkg/auth.cxx @@ -7,7 +7,6 @@ #include // numeric_limits #include // ostreambuf_iterator -#include #include #include #include diff --git a/bpkg/checksum.cxx b/bpkg/checksum.cxx index 65ed377..b761d0b 100644 --- a/bpkg/checksum.cxx +++ b/bpkg/checksum.cxx @@ -331,7 +331,7 @@ namespace bpkg } string - sha256 (const common_options& o, const path& f) + sha256sum (const common_options& o, const path& f) { if (!exists (f)) fail << "file " << f << " does not exist"; diff --git a/bpkg/checksum.hxx b/bpkg/checksum.hxx index 73f52e8..54e5b3c 100644 --- a/bpkg/checksum.hxx +++ b/bpkg/checksum.hxx @@ -4,8 +4,6 @@ #ifndef BPKG_CHECKSUM_HXX #define BPKG_CHECKSUM_HXX -#include - #include #include @@ -16,7 +14,7 @@ namespace bpkg // Calculate SHA256 sum of the specified memory buffer in binary mode. // inline string - sha256 (const char* buf, size_t n) {return butl::sha256 (buf, n).string ();} + sha256sum (const char* buf, size_t n) {return sha256 (buf, n).string ();} // The same but for a file. Issue diagnostics and throw failed if anything // goes wrong. @@ -26,7 +24,7 @@ namespace bpkg // optimized for the platform. // string - sha256 (const common_options&, const path& file); + sha256sum (const common_options&, const path& file); } #endif // BPKG_CHECKSUM_HXX diff --git a/bpkg/database.cxx b/bpkg/database.cxx index b0a673e..d96c53b 100644 --- a/bpkg/database.cxx +++ b/bpkg/database.cxx @@ -10,8 +10,6 @@ #include #include -#include - #include #include #include @@ -706,7 +704,7 @@ namespace bpkg // std::string schema; { - butl::sha256 h (d.string ()); + sha256 h (d.string ()); for (size_t n (4);; ++n) { diff --git a/bpkg/fetch-pkg.cxx b/bpkg/fetch-pkg.cxx index 64abb43..ca4767f 100644 --- a/bpkg/fetch-pkg.cxx +++ b/bpkg/fetch-pkg.cxx @@ -52,7 +52,7 @@ namespace bpkg is.close (); string s (bs.str ()); - string sha256sum (sha256 (s.c_str (), s.size ())); + string cs (sha256sum (s.c_str (), s.size ())); istringstream ts (s); // Text mode. @@ -60,7 +60,7 @@ namespace bpkg M m (mp, ignore_unknown); if (pr.wait ()) - return make_pair (move (m), move (sha256sum)); + return make_pair (move (m), move (cs)); // Child existed with an error, fall through. } @@ -146,14 +146,14 @@ namespace bpkg // and reading the manifest. The file should be opened in the binary // mode for the first operation and in the text mode for the second one. // - string sha256sum; + string cs; if (o != nullptr) - sha256sum = sha256 (*o, f); // Read file in the binary mode. + cs = sha256sum (*o, f); // Read file in the binary mode. ifdstream ifs (f); // Open file in the text mode. manifest_parser mp (ifs, f.string ()); - return make_pair (M (mp, ignore_unknown), move (sha256sum)); + return make_pair (M (mp, ignore_unknown), move (cs)); } catch (const manifest_parsing& e) { diff --git a/bpkg/manifest-utility.cxx b/bpkg/manifest-utility.cxx index f690a40..d205c2d 100644 --- a/bpkg/manifest-utility.cxx +++ b/bpkg/manifest-utility.cxx @@ -6,7 +6,6 @@ #include // strcspn() #include -#include #include // wildcard_version #include diff --git a/bpkg/package-configuration.cxx b/bpkg/package-configuration.cxx new file mode 100644 index 0000000..e8ea13a --- /dev/null +++ b/bpkg/package-configuration.cxx @@ -0,0 +1,491 @@ +// file : bpkg/package-configuration.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include + +namespace bpkg +{ + using build2::config::variable_origin; + + string + serialize_cmdline (const string& name, const optional& value) + { + using namespace build2; + + string r (name + '='); + + if (!value) + r += "[null]"; + else + { + if (!value->empty ()) + { + // Note: we need to use command-line (effective) quoting. + // + ostringstream os; + to_stream (os, *value, quote_mode::effective, '@'); + r += os.str (); + } + } + + return r; + } + + void package_configuration:: + print (diag_record& dr, + const char* indent, + const dependent_config_variable_values* ovrs) const + { + bool first (true); + for (const config_variable_value& v: *this) + { + if (v.origin != variable_origin::buildfile && + v.origin != variable_origin::override_) + continue; + + if (first) + first = false; + else + dr << '\n'; + + dr << indent; + + if (ovrs != nullptr && v.origin == variable_origin::buildfile) + { + if (const dependent_config_variable_value* ov = ovrs->find (v.name)) + { + dr << ov->serialize_cmdline () << " (set by " << ov->dependent << ')'; + continue; + } + } + + dr << v.serialize_cmdline () << " ("; + + if (v.origin == variable_origin::buildfile) + dr << "set by " << *v.dependent; + else + dr << "user configuration"; + + dr << ')'; + } + } + + void + to_checksum (sha256& cs, const config_variable_value& v) + { + using namespace build2; + + cs.append (v.name); + cs.append (static_cast (v.origin)); + if (v.type) + cs.append (*v.type); + + if (v.origin != variable_origin::undefined) + { + if (v.value) + for (const name& n: *v.value) + to_checksum (cs, n); + + if (v.origin == variable_origin::buildfile) + { + cs.append (v.dependent->string ()); + cs.append (v.confirmed); + } + } + } + + bool + negotiate_configuration ( + package_configurations& cfgs, + package_skeleton& dept, + pair pos, + const small_vector, 1>& depcs) + { + assert (!dept.system); + + pos.first--; pos.second--; // Convert to 0-base. + + const dependency_alternative& da ( + dept.available->dependencies[pos.first][pos.second]); + + assert (da.require || da.prefer); + + // Step 1: save a snapshot of the old configuration while unsetting values + // that have this dependent as the originator and reloading the defaults. + // + // The idea behind unsetting values previously (first) set by this + // dependent is to allow it to "change its mind" based on other changes in + // the configuration (e.g., some expensive feature got enabled by another + // dependent which this dependent might as well use). + // + // This works well if the default values of configuration variables are + // independent. However, consider this example: + // + // dependency: + // + // config [bool] config.foo.x ?= false + // config [bool] config.foo.buf ?= ($config.foo.x ? 8196 : 4096) + // + // dependent: + // + // config.foo.x = true + // config.foo.buf = ($config.foo.buf < 6144 ? 6144 : $config.foo.buf) + // + // Here if we unset both x and buf to their defaults, we will get an + // incorrect result. + // + // The long-term solution here is to track dependencies between + // configuration variables (which we can do as part of the config + // directive via our parser::lookup_variable() hook and save this + // information in the config module's saved_variables list). Then, we + // "levelize" all the variables and have an inner "refinement" loop over + // these levels. Specifically, we first unset all of them. Then we unset + // all except the first level (which contains configuration variables that + // don't depend on any others). And so on. + // + // And until we implement this, we expect the dependent to take such + // configuration variable dependencies into account. For example: + // + // config.foo.x = true + // config.foo.buf = ($config.foo.buf < 6144 + // ? ($config.foo.x ? 8196 : 6144) + // : $config.foo.buf) + // + // Another issue with this "originating dependent" logic is that it will + // be tricky to scale to containers where we would need to track + // originating dependents for individual elements of a value rather than + // the whole value as we do now. As an example, consider some "set of + // enabled backends" configuration variable. Technically, this doesn't + // seem insurmountable if we make some assumptions (e.g., if a value + // contains multiple elements, then it is such a set-like value; or + // we could use actual type names). + // + // For now we recommend to use multiple bool configurations to handle such + // cases (and, in fact, we currently don't have any set/map-like types, + // which we may want to add in the future). However, one could come up + // with some open-ended configuration list that will be difficult to + // support with bool. For example, we may need to support a set of buffer + // sizes or some such. + // + // Our assumptions regarding require: + // + // - Can only set bool configuration variables and only to true. + // + // - Should not have any conditions on the state of other configuration + // variables, including their origin (but can have other conditions, + // for example on the target platform). + // + // This means that we don't need to set the default values, but will need + // the type information as well as overrides. So what we will do is only + // call reload_defaults() for the first time to load types/override. Note + // that this assumes the set of configuration variables cannot change + // based on the values of other configuration variables (we have a note + // in the manual instructing the user not to do this). + // + // The dependency could also be system in which case there could be no + // skeleton information to load the types/defaults from. In this case we + // can handle require in the "lax mode" (based on the above assumptions) + // but not prefer. + // + // While at it, also collect the configurations to pass to dependent's + // evaluate_*() calls. + // + dependent_config_variable_values old_cfgs; + package_skeleton::dependency_configurations depc_cfgs; + depc_cfgs.reserve (depcs.size ()); + + for (package_skeleton& depc: depcs) + { + package_configuration& cfg (cfgs[depc.package]); + + for (config_variable_value& v: cfg) + { + if (v.origin == variable_origin::buildfile) + { + if (*v.dependent == dept.package) + { + old_cfgs.push_back ( + dependent_config_variable_value { + v.name, move (v.value), move (*v.dependent)}); + + // Note that we will not reload it to default in case of require. + // + v.undefine (); + } + else + old_cfgs.push_back ( + dependent_config_variable_value {v.name, v.value, *v.dependent}); + } + } + + if (depc.available == nullptr) + { + assert (depc.system); + + if (da.prefer) + fail << "unable to negotiate configuration for system dependency " + << depc.package << " without configuration information" << + info << "consider specifying system dependency version that has " + << "corresponding available package" << + info << "dependent " << dept.package << " has prefer/accept clauses " + << "that cannot be evaluated without configuration information"; + + if (!cfg.system) + { + // Note that we still need the overrides. + // + depc.load_overrides (cfg); + cfg.system = true; + } + + continue; + } + else + assert (!cfg.system); + + if (da.prefer || cfg.empty ()) + depc.reload_defaults (cfg); + } + + // Note that we need to collect the dependency configurations as a + // separate loop so that the stored references are not invalidated by + // operator[] (which is really a push_back() into a vector). + // + for (package_skeleton& depc: depcs) + depc_cfgs.push_back (cfgs[depc.package]); + + // Step 2: execute the prefer/accept or requires clauses. + // + if (!(da.require + ? dept.evaluate_require (depc_cfgs, *da.require, pos) + : dept.evaluate_prefer_accept (depc_cfgs, + *da.prefer, *da.accept, pos))) + { + diag_record dr (fail); + + dr << "unable to negotiate acceptable configuration with dependent " + << dept.package << " for dependencies "; + + for (size_t i (0); i != depcs.size (); ++i) + dr << (i == 0 ? "" : ", ") << depcs[i].get ().package; + + dr << info << "configuration before negotiation:\n"; + + // Note that we won't print this dependent's values (which we have unset + // above), but that seems like a good thing since they are not the cause + // of this impasse. + // + for (const package_configuration& cfg: depc_cfgs) + cfg.print (dr, " "); // Note 4 spaces since in nested info. + } + + // Check if anything changed by comparing to entries in old_cfgs. + // + // While at it, also detect if we have any changes where one dependent + // overrides a value set by another dependent (see below). + // + bool cycle (false); + { + optional n (0); // Number of unchanged. + + for (package_skeleton& depc: depcs) + { + package_configuration& cfg (cfgs[depc.package]); + + for (config_variable_value& v: cfg) + { + if (v.origin == variable_origin::buildfile) + { + if (const dependent_config_variable_value* ov = + old_cfgs.find (v.name)) + { + if (ov->value == v.value) + { + // If the value hasn't change, so shouldn't the originating + // dependent. + // + assert (ov->dependent == *v.dependent); + + if (n) + ++*n; + + continue; + } + else + { + // Note that it's possible the same dependent overrides its + // old value (e.g., because a conditional default changed to a + // better value). + // + if (ov->dependent != *v.dependent) + cycle = true; + } + } + + n = nullopt; + + if (cycle) + break; + } + } + + if (!n && cycle) + break; + } + + // If we haven't seen any changed and we've seen the same number, then + // nothing has changed. + // + if (n && *n == old_cfgs.size ()) + return false; + } + + // Besides the dependent returning false from its accept clause, there is + // another manifestation of the inability to negotiate an acceptable + // configuration: two dependents keep changing the same configuration to + // mutually unacceptable values. To detect this, we need to look for + // negotiation cycles. + // + // Specifically, given a linear change history in the form: + // + // O->N ... O->N ... O->N + // + // We need to look for a possibility of turning it into a cycle: + // + // O->N ... O->N + // \ ... / + // + // Where O->N is a change that involves one dependent overriding a value + // set by another dependent and `...` are identical history segments. + // + if (!cycle) + return true; + + // Populate new_cfgs. + // + dependent_config_variable_values new_cfgs; + for (package_skeleton& depc: depcs) + { + package_configuration& cfg (cfgs[depc.package]); + + for (config_variable_value& v: cfg) + { + if (v.origin == variable_origin::buildfile) + { + new_cfgs.push_back ( + dependent_config_variable_value {v.name, v.value, *v.dependent}); + } + } + } + + // Sort both. + // + { + auto cmp = [] (const dependent_config_variable_value& x, + const dependent_config_variable_value& y) + { + return x.name < y.name; + }; + + sort (old_cfgs.begin (), old_cfgs.end (), cmp); + sort (new_cfgs.begin (), new_cfgs.end (), cmp); + } + + // Look backwards for identical O->N changes and see if we can come + // up with two identical segments between them. + // + cycle = false; + + auto& change_history (cfgs.change_history_); + + for (size_t n (change_history.size ()), i (n); i != 0; i -= 2) + { + if (change_history[i - 2] == old_cfgs && + change_history[i - 1] == new_cfgs) + { + size_t d (n - i); // Segment length. + + // See if there is an identical segment before this that also starts + // with O->N. + // + if (i < 2 + d + 2) + break; // Not long enough to possibly find anything. + + size_t j (i - 2 - d); // Earlier O->N. + + if (change_history[j - 2] == old_cfgs && + change_history[j - 1] == new_cfgs) + { + if (equal (change_history.begin () + j, + change_history.begin () + j + d, + change_history.begin () + i)) + { + cycle = true; + break; + } + } + + // Otherwise, keep looking for a potentially longer segment. + } + } + + if (!cycle) + { + change_history.push_back (move (old_cfgs)); + change_history.push_back (move (new_cfgs)); + + return true; + } + + diag_record dr (fail); + + dr << "unable to negotiate acceptable configuration between dependents " + << dept.package; + + // Analyze the O->N changes and determine the problematic dependent(s). + // Do we actually know for sure they are all problematic? Well, they + // repeatedly changed the values to the ones we don't like, so I guess so. + // + small_vector, 1> depts; // Duplicates. + for (const dependent_config_variable_value& nv: new_cfgs) + { + if (nv.dependent == dept.package) + { + if (const dependent_config_variable_value* ov = old_cfgs.find (nv.name)) + { + if (ov->value != nv.value && ov->dependent != nv.dependent) + { + if (find_if (depts.begin (), depts.end (), + [ov] (reference_wrapper pk) + { + return ov->dependent == pk.get (); + }) == depts.end ()) + { + dr << ", " << ov->dependent; + depts.push_back (ov->dependent); + } + } + } + } + } + + dr << " for dependencies "; + + for (size_t i (0); i != depcs.size (); ++i) + dr << (i == 0 ? "" : ", ") << depcs[i].get ().package; + + dr << info << "configuration before negotiation:\n"; + for (const package_configuration& cfg: depc_cfgs) + cfg.print (dr, " ", &old_cfgs); + + dr << info << "configuration after negotiation:\n"; + for (const package_configuration& cfg: depc_cfgs) + cfg.print (dr, " "); + + dr << endf; + } +} diff --git a/bpkg/package-configuration.hxx b/bpkg/package-configuration.hxx new file mode 100644 index 0000000..73f05ff --- /dev/null +++ b/bpkg/package-configuration.hxx @@ -0,0 +1,206 @@ +// file : bpkg/package-configuration.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef BPKG_PACKAGE_CONFIGURATION_HXX +#define BPKG_PACKAGE_CONFIGURATION_HXX + +#include // build2::names +#include // build2::config::variable_origin + +#include +#include + +#include + +using namespace std; + +namespace bpkg +{ + class package_skeleton; + + // Serialize the variable value as a command line override. + // + string + serialize_cmdline (const string& name, const optional& value); + + struct config_variable_value + { + string name; + + // The variable_origin values have the following meaning: + // + // default -- default value from the config directive + // buildfile -- dependent configuration (config_source::dependent) + // override -- user configuration (config_source::user) + // undefined -- none of the above + // + build2::config::variable_origin origin; + + // Variable type name with absent signifying untyped. + // + optional type; + + // If origin is not undefined, then this is the reversed variable value + // with absent signifying NULL. + // + optional value; + + // If origin is buildfile, then this is the "originating dependent" which + // first set this variable to this value. + // + optional dependent; + + // If origin is buildfile, then this flag indicates whether the + // originating dependent has been encountered during the negotiation + // retry. + // + bool confirmed; + + public: + void + undefine () + { + origin = build2::config::variable_origin::undefined; + value = nullopt; + dependent = nullopt; + confirmed = false; + } + + string + serialize_cmdline () const + { + return bpkg::serialize_cmdline (name, value); + } + }; + + void + to_checksum (sha256&, const config_variable_value&); + + // A subset of config_variable_value for variable values set by the + // dependents (origin is buildfile). Used to track change history. + // + struct dependent_config_variable_value + { + string name; + optional value; + package_key dependent; + + public: + string + serialize_cmdline () const + { + return bpkg::serialize_cmdline (name, value); + } + }; + + inline bool + operator== (const dependent_config_variable_value& x, + const dependent_config_variable_value& y) + { + return x.name == y.name && x.value == y.value && x.dependent == y.dependent; + } + + class dependent_config_variable_values: + public small_vector + { + public: + const dependent_config_variable_value* + find (const string& name) const + { + auto i (find_if (begin (), end (), + [&name] (const dependent_config_variable_value& v) + { + return v.name == name; + })); + return i != end () ? &*i : nullptr; + } + }; + + class package_configuration: public vector + { + public: + package_key package; + bool system = false; // True if system package without skeleton info. + + explicit + package_configuration (package_key p): package (move (p)) {} + + config_variable_value* + find (const string& name) + { + auto i (find_if (begin (), end (), + [&name] (const config_variable_value& v) + { + return v.name == name; + })); + return i != end () ? &*i : nullptr; + } + + const config_variable_value* + find (const string& name) const + { + auto i (find_if (begin (), end (), + [&name] (const config_variable_value& v) + { + return v.name == name; + })); + return i != end () ? &*i : nullptr; + } + + // Print buildfile and override configuration variable values as command + // line overrides one per line with the specified indentation. After each + // variable also print in parenthesis its origin. If overrides is not + // NULL, then it is used to override the value/dependent information. + // + void + print (diag_record&, const char* indent, + const dependent_config_variable_values* overrides = nullptr) const; + }; + + class package_configurations: public small_vector + { + public: + // Note: may invalidate references. + // + package_configuration& + operator[] (const package_key& p) + { + auto i (find_if (begin (), end (), + [&p] (const package_configuration& pc) + { + return pc.package == p; + })); + if (i != end ()) + return *i; + + push_back (package_configuration (p)); + return back (); + } + + void + clear () + { + small_vector::clear (); + change_history_.clear (); + } + + // Implementation details. + // + public: + // Note: dependent_config_variable_values must be sorted by name. + // + small_vector change_history_; + }; + + // Negotiate the configuration for the specified dependencies of the + // specified dependent. Return true if the configuration has changed. + // + bool + negotiate_configuration ( + package_configurations&, + package_skeleton& dependent, + pair position, + const small_vector, 1>& dependencies); +} + +#endif // BPKG_PACKAGE_CONFIGURATION_HXX diff --git a/bpkg/package-skeleton.cxx b/bpkg/package-skeleton.cxx index e0d85ed..8b4b09f 100644 --- a/bpkg/package-skeleton.cxx +++ b/bpkg/package-skeleton.cxx @@ -20,6 +20,8 @@ #include #include +#include + #include #include #include @@ -38,6 +40,119 @@ namespace bpkg void build2_init (const common_options&); +} + +namespace bpkg +{ + // Check whether the specified configuration variable override has a project + // variable (i.e., its name starts with config.). If the last + // argument is not NULL, then set it to the length of the variable portion. + // + // Note that some user-specified variables may have qualifications + // (global, scope, etc) but there is no reason to expect any project + // configuration variables to use such qualifications (since they can + // only apply to one project). So we ignore all qualified variables. + // + static inline bool + project_override (const string& v, const string& p, size_t* l = nullptr) + { + size_t n (p.size ()); + + if (v.compare (0, n, p) == 0) + { + if (v[n] == '.') + { + if (l != nullptr) + *l = v.find_first_of ("=+ \t", n + 1); + + return true; + } + else if (strchr ("=+ \t", v[n]) != nullptr) + { + if (l != nullptr) + *l = n; + + return true; + } + } + + return false; + } + + // Check whether the specified configuration variable name is a project + // variable (i.e., its name starts with config.). + // + static inline bool + project_variable (const string& v, const string& p) + { + size_t n (p.size ()); + return v.compare (0, n, p) == 0 && (v[n] == '.' || v[n] == '\0'); + } + + // Customized buildfile parser that is used to detect and diagnose + // references to undefined dependency configuration variables. + // + class buildfile_parser: public build2::parser + { + public: + buildfile_parser (build2::context& ctx, + const strings& dvps, + optional dvps_pending = {}) + : parser (ctx), + dependency_var_prefixes_ (dvps), + dependency_var_prefixes_pending_ (dvps_pending) {} + + protected: + virtual build2::lookup + lookup_variable (build2::name&& qual, + string&& name, + const build2::location& loc) override + { + using namespace build2; + using build2::fail; + using build2::info; + + // To avoid making copies of the name, pre-check if it is from one + // of the dependencies. + // + optional dep; + if (!pre_parse_ && qual.empty ()) + { + auto b (dependency_var_prefixes_.begin ()); + auto e (dependency_var_prefixes_pending_ + ? b + *dependency_var_prefixes_pending_ + : dependency_var_prefixes_.end ()); + + if (find_if (b, e, + [&name] (const string& p) + { + return project_variable (name, p); + }) != e) + dep = name; + } + + lookup l (parser::lookup_variable (move (qual), move (name), loc)); + + if (dep && !l.defined ()) + fail (loc) << "undefined dependency configuration variable " << *dep << + info << "was " << *dep << " set in earlier prefer or require clause?"; + + return l; + } + + private: + const strings& dependency_var_prefixes_; + optional dependency_var_prefixes_pending_; + }; + + static void + create_context (package_skeleton&, const strings&); + + // Note: cannot be package_skeleton member function due to iterator return + // (build2 stuff is only forward-declared in the header). + // + static build2::scope_map::iterator + bootstrap (package_skeleton&, const strings&); package_skeleton:: ~package_skeleton () @@ -46,8 +161,36 @@ namespace bpkg package_skeleton:: package_skeleton (package_skeleton&& v) + : package (move (v.package)), + system (v.system), + available (move (v.available)), + co_ (v.co_), + db_ (v.db_), + var_prefix_ (move (v.var_prefix_)), + config_vars_ (move (v.config_vars_)), + disfigure_ (v.disfigure_), + config_srcs_ (v.config_srcs_), + src_root_ (move (v.src_root_)), + out_root_ (move (v.out_root_)), + created_ (v.created_), + verified_ (v.verified_), + loaded_old_config_ (v.loaded_old_config_), + develop_ (v.develop_), + ctx_ (move (v.ctx_)), + rs_ (v.rs_), + cmd_vars_ (move (v.cmd_vars_)), + cmd_vars_cache_ (v.cmd_vars_cache_), + dependent_vars_ (move (v.dependent_vars_)), + dependent_orgs_ (move (v.dependent_orgs_)), + reflect_ (move (v.reflect_)), + dependency_reflect_ (move (v.dependency_reflect_)), + dependency_reflect_index_ (v.dependency_reflect_index_), + dependency_reflect_pending_ (v.dependency_reflect_pending_), + dependency_var_prefixes_ (move (v.dependency_var_prefixes_)), + dependency_var_prefixes_pending_ (v.dependency_var_prefixes_pending_), + prefer_accept_ (v.prefer_accept_) { - *this = move (v); + v.db_ = nullptr; } package_skeleton& package_skeleton:: @@ -55,22 +198,36 @@ namespace bpkg { if (this != &v) { + package = move (v.package); + system = v.system; + available = move (v.available); co_ = v.co_; db_ = v.db_; - available_ = v.available_; + var_prefix_ = move (v.var_prefix_); config_vars_ = move (v.config_vars_); + disfigure_ = v.disfigure_; + config_srcs_ = v.config_srcs_; src_root_ = move (v.src_root_); out_root_ = move (v.out_root_); created_ = v.created_; + verified_ = v.verified_; + loaded_old_config_ = v.loaded_old_config_; + develop_ = v.develop_; ctx_ = move (v.ctx_); rs_ = v.rs_; cmd_vars_ = move (v.cmd_vars_); - reflect_names_ = move (v.reflect_names_); - reflect_vars_ = move (v.reflect_vars_); - reflect_frag_ = move (v.reflect_frag_); + cmd_vars_cache_ = v.cmd_vars_cache_; + dependent_vars_ = move (v.dependent_vars_); + dependent_orgs_ = move (v.dependent_orgs_); + reflect_ = move (v.reflect_); + dependency_reflect_ = move (v.dependency_reflect_); + dependency_reflect_index_ = v.dependency_reflect_index_; + dependency_reflect_pending_ = v.dependency_reflect_pending_; + dependency_var_prefixes_ = move (v.dependency_var_prefixes_); + dependency_var_prefixes_pending_ = v.dependency_var_prefixes_pending_; + prefer_accept_ = v.prefer_accept_; v.db_ = nullptr; - v.available_ = nullptr; } return *this; @@ -78,34 +235,135 @@ namespace bpkg package_skeleton:: package_skeleton (const package_skeleton& v) - : co_ (v.co_), + : package (v.package), + system (v.system), + available (v.available), + co_ (v.co_), db_ (v.db_), - available_ (v.available_), + var_prefix_ (v.var_prefix_), config_vars_ (v.config_vars_), + disfigure_ (v.disfigure_), + config_srcs_ (v.config_srcs_), src_root_ (v.src_root_), out_root_ (v.out_root_), created_ (v.created_), + verified_ (v.verified_), + loaded_old_config_ (v.loaded_old_config_), + develop_ (v.develop_), cmd_vars_ (v.cmd_vars_), - reflect_names_ (v.reflect_names_), - reflect_vars_ (v.reflect_vars_), - reflect_frag_ (v.reflect_frag_) + cmd_vars_cache_ (v.cmd_vars_cache_), + dependent_vars_ (v.dependent_vars_), + dependent_orgs_ (v.dependent_orgs_), + reflect_ (v.reflect_), + dependency_reflect_ (v.dependency_reflect_), + dependency_reflect_index_ (v.dependency_reflect_index_), + dependency_reflect_pending_ (v.dependency_reflect_pending_), + dependency_var_prefixes_ (v.dependency_var_prefixes_), + dependency_var_prefixes_pending_ (v.dependency_var_prefixes_pending_), + prefer_accept_ (v.prefer_accept_) { // The idea here is to create an "unloaded" copy but with enough state // that it can be loaded if necessary. + // + // Note that there is a bit of a hole in this logic with regards to the + // prefer_accept_ semantics but it looks like we cannot plausible trigger + // it (which is fortified with an assert in evaluate_reflect(); note that + // doing it here would be overly strict since this may have a left-over + // prefer_accept_ position). + } + + void package_skeleton:: + reset () + { + assert (db_ != nullptr); // Cannot be called after collect_config(). + + rs_ = nullptr; + ctx_ = nullptr; // Free. + + cmd_vars_.clear (); + cmd_vars_cache_ = false; + + dependent_vars_.clear (); + dependent_orgs_.clear (); + + reflect_.clear (); + + dependency_reflect_.clear (); + dependency_reflect_index_ = 0; + dependency_reflect_pending_ = 0; + + dependency_var_prefixes_.clear (); + dependency_var_prefixes_pending_ = 0; + + prefer_accept_ = nullopt; } package_skeleton:: package_skeleton (const common_options& co, - database& db, - const available_package& ap, + package_key pk, + bool sys, + shared_ptr ap, strings cvs, + bool df, + const vector* css, optional src_root, optional out_root) - : co_ (&co), db_ (&db), available_ (&ap), config_vars_ (move (cvs)) + : package (move (pk)), + system (sys), + available (move (ap)), + co_ (&co), + db_ (&package.db.get ()), + var_prefix_ ("config." + package.name.variable ()), + config_vars_ (move (cvs)), + disfigure_ (df), + config_srcs_ (df ? nullptr : css) { - // Should not be created for stubs. + if (available != nullptr) + assert (available->bootstrap_build); // Should have skeleton info. + else + assert (system); + + // We are only interested in old user configuration variables. + // + if (config_srcs_ != nullptr) + { + if (find_if (config_srcs_->begin (), config_srcs_->end (), + [] (const config_variable& v) + { + return v.source == config_source::user; + }) == config_srcs_->end ()) + config_srcs_ = nullptr; + } + + // We don't need to load old user configuration if there isn't any and + // there is no new project configuration specified by the user. // - assert (available_->bootstrap_build); + // Note that at first it may seem like we shouldn't do this for any system + // packages but if we want to verify the user configuration, why not do so + // for system if we can (i.e., have skeleton info)? + // + if (available == nullptr) + loaded_old_config_ = true; + else + loaded_old_config_ = + (config_srcs_ == nullptr) && + find_if (config_vars_.begin (), config_vars_.end (), + [this] (const string& v) + { + // For now tighten it even further so that we can continue + // using repositories without package skeleton information + // (bootstrap.build, root.build). See load_old_config() for + // details. + // +#if 0 + return project_override (v, var_prefix_); +#else + size_t vn; + size_t pn (var_prefix_.size ()); + return (project_override (v, var_prefix_, &vn) && + v.compare (pn, vn - pn, ".develop") == 0); +#endif + }) == config_vars_.end (); if (src_root) { @@ -118,6 +376,380 @@ namespace bpkg assert (!out_root); } + // Serialize a variable assignment for a command line override. + // + static string + serialize_cmdline (const string& var, const build2::value& val, + build2::names& storage) + { + using namespace build2; + + string r (var + '='); + + if (val.null) + r += "[null]"; + else + { + storage.clear (); + names_view nv (reverse (val, storage)); + + if (!nv.empty ()) + { + // Note: we need to use command-line (effective) quoting. + // + ostringstream os; + to_stream (os, nv, quote_mode::effective, '@'); + r += os.str (); + } + } + + return r; + } + + // Reverse value to names. + // + static optional + reverse_value (const build2::value& val) + { + using namespace build2; + + if (val.null) + return nullopt; + + names storage; + names_view nv (reverse (val, storage)); + + return (nv.data () == storage.data () + ? move (storage) + : names (nv.begin (), nv.end ())); + } + + // Return the dependent (origin==buildfile) configuration variables as + // command line overrides. If the second argument is not NULL, then populate + // it with the corresponding originating dependents. + // + static strings + dependent_cmd_vars (const package_configuration& cfg, + vector* orgs = nullptr) + { + using build2::config::variable_origin; + + strings r; + + for (const config_variable_value& v: cfg) + { + if (v.origin == variable_origin::buildfile) + { + r.push_back (v.serialize_cmdline ()); + + if (orgs != nullptr) + orgs->push_back (*v.dependent); + } + } + + return r; + } + + void package_skeleton:: + reload_defaults (package_configuration& cfg) + { + // Should only be called before dependent_config()/evaluate_*(). + // + assert (dependent_vars_.empty () && + reflect_.empty () && + dependency_reflect_.empty () && + available != nullptr && + ctx_ == nullptr); + + if (!loaded_old_config_) + load_old_config (); + + try + { + using namespace build2; + + // This is what needs to happen to the variables of different origins in + // the passed configuration: + // + // default -- reloaded + // buildfile/dependent -- made command line override + // override/user -- should match what's in config_vars_ + // undefined -- reloaded + // + // Note also that on the first call we will have no configuration. And + // so to keep things simple, we merge variable of the buildfile origin + // into cmd_vars and then rebuild things from scratch. Note, however, + // that below we need to sort out these merged overrides into user and + // dependent, so we keep the old configuration for reference. + // + // Note also that dependent values do not clash with user overrides by + // construction (in evaluate_{prefer_accept,require}()): we do not add + // as dependent variables that have the override origin. + // + scope& rs ( + *bootstrap ( + *this, merge_cmd_vars (dependent_cmd_vars (cfg)))->second.front ()); + + // Load project's root.build. + // + load_root (rs); + + package_configuration old (move (cfg)); + cfg.package = move (old.package); + + // Note that a configuration variable may not have a default value so we + // cannot just iterate over all the config.** values set on the + // root scope. Our options seem to be either iterating over the variable + // pool or forcing the config module with config.config.module=true and + // then using its saved variables map. Since the amount of stuff we load + // is quite limited, there shouldn't be too many variables in the pool. + // So let's go with the simpler approach for now. + // + // Though the saved variables map approach would have been more accurate + // since that's the variables that were introduced with the config + // directive. Potentially the user could just have a buildfile + // config.** variable but it feels like that should be harmless + // (we will return it but nobody will presumably use that information). + // Also, if/when we start tracking the configuration variable + // dependencies (i.e., which default value depend on which config + // variable), then the saved variables map seem like the natural place + // to keep this information. + // + for (const variable& var: rs.ctx.var_pool) + { + if (!project_variable (var.name, var_prefix_)) + continue; + + using config::variable_origin; + + pair ol (config::origin (rs, var)); + + switch (ol.first) + { + case variable_origin::default_: + case variable_origin::override_: + case variable_origin::undefined: + { + config_variable_value v {var.name, ol.first, {}, {}, {}, false}; + + // Override could mean user override from config_vars_ or the + // dependent override that we have merged above. + // + if (v.origin == variable_origin::override_) + { + if (config_variable_value* ov = old.find (v.name)) + { + if (ov->origin == variable_origin::buildfile) + { + v.origin = variable_origin::buildfile; + v.dependent = move (ov->dependent); + v.confirmed = ov->confirmed; + } + else + assert (ov->origin == variable_origin::override_); + } + } + + // Save value. + // + if (v.origin != variable_origin::undefined) + v.value = reverse_value (*ol.second); + + // Save type. + // + if (var.type != nullptr) + v.type = var.type->name; + + cfg.push_back (move (v)); + break; + } + case variable_origin::buildfile: + { + // Feel like this shouldn't happen since we have disfigured them. + // + assert (false); + break; + } + } + } + + verified_ = true; // Managed to load without errors. + ctx_ = nullptr; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + + void package_skeleton:: + load_overrides (package_configuration& cfg) + { + // Should only be called before dependent_config()/evaluate_*() and only + // on system package without skeleton info. + // + assert (dependent_vars_.empty () && + reflect_.empty () && + dependency_reflect_.empty () && + available == nullptr && + system); + + if (find_if (config_vars_.begin (), config_vars_.end (), + [this] (const string& v) + { + return project_override (v, var_prefix_); + }) == config_vars_.end ()) + return; + + try + { + using namespace build2; + using build2::fail; + using build2::endf; + + using config::variable_origin; + + // Create the build context. + // + create_context (*this, strings {}); + context& ctx (*ctx_); + + scope& gs (ctx.global_scope.rw ()); + auto& vp (ctx.var_pool.rw ()); + + for (const string& v: config_vars_) + { + size_t vn; + if (!project_override (v, var_prefix_, &vn)) + continue; + + const variable& var (vp.insert (string (v, 0, vn))); + + // Parse the value part (note that all evaluate_require() cares about + // is whether the value is true or not, but we do need accurate values + // for diagnostics). + // + size_t p (v.find ('=', vn)); + assert (p != string::npos); + if (v[p + 1] == '+') + ++p; + + optional val; + { + // Similar to context() ctor. + // + istringstream is (string (v, p + 1)); + is.exceptions (istringstream::failbit | istringstream::badbit); + + path_name in (""); + lexer lex (is, in, 1 /* line */, "\'\"\\$("); // Effective. + + parser par (ctx); + pair r ( + par.parse_variable_value (lex, gs, &build2::work, var)); + + if (r.second.type != token_type::eos) + fail << "invalid command line variable override '" << v << "'"; + + val = reverse_value (r.first); + } + + // @@ Should we do anything with append/prepend? + // + if (config_variable_value* v = cfg.find (var.name)) + v->value = move (val); + else + cfg.push_back ( + config_variable_value { + var.name, variable_origin::override_, {}, move (val), {}, false}); + } + + ctx_ = nullptr; // Free. + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + + pair package_skeleton:: + verify_sensible (const package_configuration& cfg) + { + // Should only be called before dependent_config()/evaluate_*(). + // + assert (dependent_vars_.empty () && + reflect_.empty () && + dependency_reflect_.empty () && + available != nullptr && + ctx_ == nullptr); + + if (!loaded_old_config_) + load_old_config (); + + try + { + using namespace build2; + + // For now we treat any failure to load root.build as bad configuration, + // which is not very precise. One idea to make this more precise would + // be to invent some kind of tagging for "bad configuration" diagnostics + // (e.g., either via an attribute or via special config.assert directive + // or some such). + // + // For now we rely on load_defaults() and load_old_config() to "flush" + // out any unrelated errors (e.g., one of the modules configuration is + // bad, etc). However, if that did not happen naturally, then we must do + // it ourselves. + // + if (!verified_) + { + scope& rs ( + *bootstrap (*this, merge_cmd_vars (strings {}))->second.front ()); + load_root (rs); + + verified_ = true; + ctx_ = nullptr; + } + + scope& rs ( + *bootstrap ( + *this, merge_cmd_vars (dependent_cmd_vars (cfg)))->second.front ()); + + // Load project's root.build while redirecting the diagnostics stream. + // + ostringstream ds; + auto dg (make_guard ([ods = diag_stream] () {diag_stream = ods;})); + diag_stream = &ds; + + pair r; + try + { + load_root (rs); + r.first = true; + } + catch (const build2::failed&) + { + r.first = false; + r.second = trim (ds.str ()); + } + + ctx_ = nullptr; + return r; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + + void package_skeleton:: + dependent_config (const package_configuration& cfg) + { + assert (dependent_vars_.empty ()); // Must be called at most once. + + dependent_vars_ = dependent_cmd_vars (cfg, &dependent_orgs_); + } + // Print the location of a depends value in the specified manifest file. // // Note that currently we only use this function for the external packages. @@ -126,7 +758,7 @@ namespace bpkg // the temp directory is not cleaned in case of an error. Maybe one day. // static void - depends_location (const diag_record& dr, + depends_location (const build2::diag_record& dr, const path& mf, size_t depends_index) { @@ -146,10 +778,10 @@ namespace bpkg { if (nv.name == "depends" && i++ == depends_index) { - dr << info (location (p.name (), - nv.value_line, - nv.value_column)) - << "depends value defined here"; + dr << build2::info (build2::location (mf, + nv.value_line, + nv.value_column)) + << "in depends manifest value defined here"; break; } } @@ -160,16 +792,29 @@ namespace bpkg } bool package_skeleton:: - evaluate_enable (const string& cond, size_t depends_index) + evaluate_enable (const string& cond, pair indexes) { + size_t depends_index (indexes.first); + try { using namespace build2; using build2::fail; + using build2::info; using build2::endf; + // Drop the state from the previous evaluation of prefer/accept. + // + if (prefer_accept_) + { + ctx_ = nullptr; + prefer_accept_ = nullopt; + } + scope& rs (load ()); + // Evaluate the enable condition. + // istringstream is ('(' + cond + ')'); is.exceptions (istringstream::failbit | istringstream::badbit); @@ -183,21 +828,24 @@ namespace bpkg uint64_t il (1); auto df = build2::make_diag_frame ( - [&cond, &rs, depends_index] (const diag_record& dr) + [this, &cond, &rs, depends_index] (const build2::diag_record& dr) { dr << info << "enable condition: (" << cond << ")"; // For external packages we have the manifest so print the location // of the depends value in questions. // - if (!rs.out_eq_src ()) + if (rs.out_eq_src ()) + dr << info << "in depends manifest value of package " + << package.name; + else depends_location (dr, rs.src_path () / manifest_file, depends_index); }); lexer l (is, in, il /* start line */); - parser p (rs.ctx); + buildfile_parser p (rs.ctx, dependency_var_prefixes_); value v (p.parse_eval (l, rs, rs, parser::pattern_mode::expand)); try @@ -217,9 +865,13 @@ namespace bpkg } } +#if 1 + void package_skeleton:: - evaluate_reflect (const string& refl, size_t depends_index) + evaluate_reflect (const string& refl, pair indexes) { + size_t depends_index (indexes.first); + // The reflect configuration variables are essentially overrides that will // be passed on the command line when we configure the package. They could // clash with configuration variables specified by the user (config_vars_) @@ -227,6 +879,11 @@ namespace bpkg // also argue we should diagnose this case and fail not to cause more // confusion. // + // They could also clash with dependent configuration. Probably should be + // handled in the same way (it's just another type of "user"). Yes, since + // dependent_vars_ are entered as cmd line overrides, this is how they are + // treated. + // // It seems like the most straightforward way to achieve the desired // semantics with the mechanisms that we have (in other words, without // inventing another "level" of overrides) is to evaluate the reflect @@ -234,19 +891,11 @@ namespace bpkg // variables set by root.build in conditions, (2) override default values // of configuration variables (and those loaded from config.build), and // (3) be overriden by configuration variables specified by the user. - // Naturally, this approach is not without a few corner cases: - // - // 1. Append in the reflect clause may not produce the desired result - // (i.e., it will append to the default value in root.build) rather - // than overriding it, as would have happen if it were a real variable - // override. - // - // config.hello.x ?= 1 # root.build - // config.hello.x += 2 # reflect clause + // Naturally, this approach is probably not without a few corner cases. // // We may also have configuration values from the previous reflect clause - // which we want to "factor in" before evaluating the next enable or - // reflect clauses (so that they can use the previously reflect values or + // which we want to "factor in" before evaluating the next clause (enable, + // reflect etc.; so that they can use the previously reflected values or // values that are derived from them in root.build). It seems like we have // two options here: either enter them as true overrides similar to // config_vars_ or just evaluate them similar to loading config.build @@ -259,42 +908,382 @@ namespace bpkg // responsibility of the package author (who is in control of both // root.build and manifest) to arrange for suitable compartmentalization. // - // BTW, a plan B would be to restrict reflect to just config vars in which - // case we could merge them with true overrides. Though how exactly would - // we do this merging is unclear. - // try { // Note: similar in many respects to evaluate_enable(). // using namespace build2; + using config::variable_origin; + using build2::diag_record; using build2::fail; + using build2::info; using build2::endf; - scope& rs (load ()); + // Drop the state from the previous evaluation of prefer/accept if it's + // from the wrong position. + // + optional dependency_var_prefixes_pending; + if (prefer_accept_) + { + if (*prefer_accept_ != indexes) + { + ctx_ = nullptr; + prefer_accept_ = nullopt; + } + else + { + // This could theoretically happen if we make a copy of the skeleton + // after evaluate_prefer_accept() and then attempt to continue with + // the call on the copy to evaluate_reflect() passing the same + // position. But it doesn't appear our usage should trigger this. + // + assert (ctx_ != nullptr); - istringstream is (refl); - is.exceptions (istringstream::failbit | istringstream::badbit); + dependency_var_prefixes_pending = dependency_var_prefixes_pending_; + } + } - path_name in (""); - uint64_t il (1); + scope& rs (load ()); + + // Collect all the set config..* variables on the first pass and + // filter out unchanged on the second. + // + // Note: a lot of this code is inspired by the config module. + // + struct rvar + { + const variable* var; + const value* val; + size_t ver; + }; + + class rvars: public vector + { + public: + pair + insert (const rvar& v) + { + auto i (find_if (begin (), end (), + [&v] (const rvar& o) {return v.var == o.var;})); + if (i != end ()) + return make_pair (i, false); + + push_back (v); + return make_pair (--end (), true); + } + }; + + rvars vars; + + auto process = [this, &rs, &vars] (bool collect) + { + // @@ TODO: would be nice to verify no bogus variables are set (can + // probably only be done via the saved variables map). + + for (auto p (rs.vars.lookup_namespace (var_prefix_)); + p.first != p.second; + ++p.first) + { + const variable& var (p.first->first); + + // This can be one of the overrides (__override, __prefix, etc), + // which we skip. + // + if (var.override ()) + continue; + + // What happens to version if overriden? A: appears to be still + // incremented! + // + const variable_map::value_data& val (p.first->second); + + if (collect) + vars.insert (rvar {&var, nullptr, val.version}); + else + { + auto p (vars.insert (rvar {&var, &val, 0})); + + if (!p.second) + { + auto i (p.first); + + if (i->ver == val.version) + vars.erase (i); // Unchanged. + else + i->val = &val; + } + } + } + }; + + // Evaluate the reflect clause. + // + istringstream is (refl); + is.exceptions (istringstream::failbit | istringstream::badbit); + + path_name in (""); + uint64_t il (1); + + // Note: keep it active until the end (see the override detection). + // + auto df = build2::make_diag_frame ( + [this, &refl, &rs, depends_index] (const build2::diag_record& dr) + { + // Probably safe to assume a one-line fragment contains a variable + // assignment. + // + if (refl.find ('\n') == string::npos) + dr << info << "reflect variable: " << trim (string (refl)); + else + dr << info << "reflect clause:\n" + << trim_right (string (refl)); + + // For external packages we have the manifest so print the + // location of the depends value in questions. + // + if (rs.out_eq_src ()) + dr << info << "in depends manifest value of package " + << package.name; + else + depends_location (dr, + rs.src_path () / manifest_file, + depends_index); + }); + + lexer l (is, in, il /* start line */); + buildfile_parser p (rs.ctx, + dependency_var_prefixes_, + dependency_var_prefixes_pending); + + process (true); + p.parse_buildfile (l, &rs, rs); + process (false); + + // Add to the vars set the reflect variables collected previously. + // + auto& vp (rs.var_pool ()); + for (const reflect_variable_value& v: reflect_) + { + const variable* var (vp.find (v.name)); + assert (var != nullptr); // Must be there (set by load()). + + auto p (vars.insert (rvar {var, nullptr, 0})); + + if (p.second) + p.first->val = rs.vars[*var].value; // Must be there (set by load()). + } + + // Re-populate everything from the var set but try to re-use buffers as + // much a possible (normally we would just be appending more variables + // at the end). + // + reflect_.resize (vars.size ()); + + // Collect the config..* variables that were changed by this + // and previous reflect clauses. + // + for (size_t i (0); i != vars.size (); ++i) + { + const variable& var (*vars[i].var); + const value& val (*vars[i].val); + + pair ol ( + config::origin (rs, + var, + pair { + lookup {val, var, rs.vars}, 1 /* depth */})); + + reflect_variable_value& v (reflect_[i]); + v.name = var.name; + v.origin = ol.first; + + if (ol.first == variable_origin::override_) + { + // Detect an overridden reflect value, but allowing it if the values + // match (think both user/dependent and reflect trying to enable the + // same feature). + // + // What would be the plausible scenarios for an override? + // + // 1. Append override that adds some backend or some such to the + // reflect value. + // + // 2. A reflect may enable a feature based on the dependency + // alternative selected (e.g., I see we are using Qt6 so we might + // as well enable feature X). The user may want do disable it + // with an override. + // + // Note also that a sufficiently smart reflect clause can detect if + // a variable is overridden (with $config.origin()) and avoid the + // clash. Perhaps that should be the recommendation for reflect + // variables that can also plausibly be set by the user (it feels + // like configuration variables have the intf/impl split similar + // to libraries)? + // + if (val != *ol.second) + { + // See if this is a dependent or user override. + // + const package_key* d (nullptr); + { + for (size_t i (0); i != dependent_vars_.size (); ++i) + { + const string& v (dependent_vars_[i]); + size_t n (var.name.size ()); + if (v.compare (0, n, var.name) == 0 && v[n] == '=') + { + d = &dependent_orgs_[i]; + break; + } + } + } + + diag_record dr (fail); + + dr << "reflect variable " << var << " overriden by "; + + if (d != nullptr) + dr << "dependent " << *d; + else + dr << "user configuration"; + + names storage; + dr << info << "reflect value: " + << serialize_cmdline (var.name, val, storage); + + dr << info << (d != nullptr ? "dependent" : "user") << " value: " + << serialize_cmdline (var.name, *ol.second, storage); + } + + // Skipped in load(), collect_config(), but used in print_config(). + } + else + { + assert (ol.first == variable_origin::buildfile); + + // Note that we keep it always untyped letting the config directives + // in root.build to take care of typing. + // + v.value = reverse_value (val); + } + } + + // Drop the build system state since it needs reloading (some computed + // values in root.build may depend on the new configuration values). + // + ctx_ = nullptr; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + +#else + + // @@ TMP: keep this in dep-config-neg but drop in master. + + void package_skeleton:: + evaluate_reflect (const string& refl, pair indexes) + { + size_t depends_index (indexes.first); + + // The reflect configuration variables are essentially overrides that will + // be passed on the command line when we configure the package. They could + // clash with configuration variables specified by the user (config_vars_) + // and it feels like user values should take precedence. Though one could + // also argue we should diagnose this case and fail not to cause more + // confusion. + // + // They could also clash with dependent configuration. Probably should be + // handled in the same way (it's just another type of "user"). Yes, since + // dependent_vars_ are entered as cmd line overrides, this is how they are + // treated. + // + // It seems like the most straightforward way to achieve the desired + // semantics with the mechanisms that we have (in other words, without + // inventing another "level" of overrides) is to evaluate the reflect + // fragment after loading root.build. This way it will (1) be able to use + // variables set by root.build in conditions, (2) override default values + // of configuration variables (and those loaded from config.build), and + // (3) be overriden by configuration variables specified by the user. + // Naturally, this approach is probably not without a few corner cases. + // + // We may also have configuration values from the previous reflect clause + // which we want to "factor in" before evaluating the next clause (enable, + // reflect etc.; so that they can use the previously reflected values or + // values that are derived from them in root.build). It seems like we have + // two options here: either enter them as true overrides similar to + // config_vars_ or just evaluate them similar to loading config.build + // (which, BTW, we might have, in case of an external package). The big + // problem with the former approach is that it will then prevent any + // further reflect clauses from modifying the same values. + // + // So overall it feels like we have iterative/compartmentalized + // configuration process. A feedback loop, in a sense. And it's the + // responsibility of the package author (who is in control of both + // root.build and manifest) to arrange for suitable compartmentalization. + // + try + { + // Note: similar in many respects to evaluate_enable(). + // + using namespace build2; + using build2::fail; + using build2::info; + using build2::endf; + + // Drop the state from the previous evaluation of prefer/accept if it's + // from the wrong position. + // + optional dependency_var_prefixes_pending; + if (prefer_accept_) + { + if (*prefer_accept_ != indexes) + { + ctx_ = nullptr; + prefer_accept_ = nullopt; + } + else + { + // This could theoretically happen if we make a copy of the skeleton + // after evaluate_prefer_accept() and then attempt to continue with + // the call on the copy to evaluate_reflect() passing the same + // position. But it doesn't appear our usage should trigger this. + // + assert (ctx_ != nullptr); + + dependency_var_prefixes_pending = dependency_var_prefixes_pending_; + } + } + + scope& rs (load ()); + + istringstream is (refl); + is.exceptions (istringstream::failbit | istringstream::badbit); + + path_name in (""); + uint64_t il (1); auto df = build2::make_diag_frame ( - [&refl, &rs, depends_index] (const diag_record& dr) + [this, &refl, &rs, depends_index] (const build2::diag_record& dr) { // Probably safe to assume a one-line fragment contains a variable // assignment. // if (refl.find ('\n') == string::npos) - dr << info << "reflect variable: " << refl; + dr << info << "reflect variable: " << trim (string (refl)); else - dr << info << "reflect fragment:\n" - << refl; + dr << info << "reflect clause:\n" + << trim_right (string (refl)); // For external packages we have the manifest so print the location // of the depends value in questions. // - if (!rs.out_eq_src ()) + if (rs.out_eq_src ()) + dr << info << "in depends manifest value of package " + << package.name; + else depends_location (dr, rs.src_path () / manifest_file, depends_index); @@ -303,11 +1292,11 @@ namespace bpkg // Note: a lot of this code is inspired by the config module. // - // Collect all the config..* variables on the first pass and + // Collect all the set config..* variables on the first pass and // filter out unchanged on the second. // auto& vp (rs.var_pool ()); - const variable& ns (vp.insert ("config." + name ().variable ())); + const string& ns (var_prefix_); struct value_data { @@ -315,6 +1304,8 @@ namespace bpkg size_t ver; }; + // @@ Maybe redo as small_vector? + // map vars; auto process = [&rs, &ns, &vars] (bool collect) @@ -323,12 +1314,12 @@ namespace bpkg p.first != p.second; ++p.first) { - const variable* var (&p.first->first.get ()); + const variable& var (p.first->first); // This can be one of the overrides (__override, __prefix, etc), // which we skip. // - if (var->override ()) + if (var.override ()) continue; // What happens to version if overriden? A: appears to be still @@ -338,11 +1329,11 @@ namespace bpkg if (collect) { - vars.emplace (var, value_data {nullptr, val.version}); + vars.emplace (&var, value_data {nullptr, val.version}); } else { - auto i (vars.find (var)); + auto i (vars.find (&var)); if (i != vars.end ()) { @@ -352,7 +1343,7 @@ namespace bpkg i->second.val = &val; } else - vars.emplace (var, value_data {&val, 0}); + vars.emplace (&var, value_data {&val, 0}); } } }; @@ -360,15 +1351,26 @@ namespace bpkg process (true); lexer l (is, in, il /* start line */); - parser p (rs.ctx); + buildfile_parser p (rs.ctx, + dependency_var_prefixes_, + dependency_var_prefixes_pending); p.parse_buildfile (l, &rs, rs); process (false); - // Add to the map the reflect variables collected previously. + // Add to the map the reflect variables collected previously. Note that + // we can re-purpose the override since we re-populate it. // - for (string& n: reflect_names_) + for (string& n: reflect_vars_) { + // Transform `name=value` to just `name`. + // + { + size_t p (n.find ('=')); + assert (p != string::npos); // We've serialized it so must be there. + n.resize (p); + } + auto p (vars.emplace (&vp.insert (move (n)), value_data {nullptr, 0})); if (p.second) @@ -382,16 +1384,8 @@ namespace bpkg // Re-populate everything from the map. // - reflect_names_.clear (); - reflect_frag_.clear (); - -#if 0 - // NOTE: see also collect_config() if enabling this. - // reflect_vars_.clear (); -#else - reflect_vars_ = config_vars_; -#endif + reflect_frag_.clear (); // Collect the config..* variables that were changed by this // and previous reflect clauses. @@ -410,31 +1404,13 @@ namespace bpkg const variable& var (*p.first); const value& val (*p.second.val); - reflect_names_.push_back (var.name); - // For the accumulated fragment we always save the original and let // the standard overriding take its course. // - reflect_frag_ += var.name; - reflect_frag_ += " = "; - - if (val.null) - reflect_frag_ += "[null]"; - else - { - storage.clear (); - names_view nv (reverse (val, storage)); - - if (!nv.empty ()) - { - ostringstream os; - to_stream (os, nv, quote_mode::normal, '@'); - reflect_frag_ += os.str (); - } - } - - reflect_frag_ += '\n'; + serialize_buildfile (reflect_frag_, var.name, val, storage); + // Note: this is currently disabled and is likely very outdated. + // // For the accumulated overrides we have to merge user config_vars_ // with reflect values. Essentially, we have three possibilities: // @@ -452,107 +1428,1133 @@ namespace bpkg // config.hello.backend = foo # reflect // config.hello.backend += bar # user // + + // @@ Can't we redo it via origin() call like in other places? + // pair org {lookup {val, var, rs.vars}, 1 /* depth */}; pair ovr; - if (var.overrides == nullptr) - ovr = org; // Case #2. - else + if (var.overrides == nullptr) + ovr = org; // Case #2. + else + { + // NOTE: see also below if enabling this. + // +#if 0 + // Case #3. + // + // The override can come from two places: config_vars_ or one of the + // "global" sources (environment variable, default options file; see + // load() for details). The latter can only be a global override and + // can be found (together with global overrides from config_vars_) + // in context::global_var_overrides. + // + // It feels like mixing global overrides and reflect is misguided: + // we probably don't want to rewrite it with a global override (per + // case #3 above) since it will apply globally. So let's diagnose it + // for now. + // + { + const strings& ovs (ctx_->global_var_overrides); + auto i (find_if (ovs.begin (), ovs.end (), + [&var] (const string& o) + { + // TODO: extracting name is not easy. + })); + + if (i != ovs.end ()) + { + fail << "global override for reflect clause variable " << var << + info << "global override: " << *i; + } + } + + // Ok, this override must be present in config_vars_. + // + // @@ Extracting the name from config_vars_ and similar is not easy: + // they are buildfile fragments and context actually parses them. + // + // @@ What if we have multiple overrides? + // + // @@ What if it's some scoped override or some such (e.g., all + // these .../x=y, etc). + // + // @@ Does anything change if we have an override but it does not + // apply (i.e., ovr == org && var.overrides != nullptr)? + // + // @@ Perhaps a sensible approach is to start relaxing/allowing + // this for specific, sensible cases (e.g., single unqualified + // override)? + // + // What would be the plausible scenarios for an override? + // + // 1. Append override that adds some backend or some such to the + // reflect value. + // + // 2. A reflect may enable a feature based on the dependency + // alternative selected (e.g., I see we are using Qt6 so we might + // as well enable feature X). The user may want do disable it + // with an override. + // + ovr = rs.lookup_override (var, org); +#else + // @@ TODO: probably also depends, not just user. + // @@ TODO: maybe we should allow if the same value (say a bool + // feature set to true but both)?! + + fail << "command line override of reflect clause variable " << var + << endf; +#endif + } + + reflect_vars_.push_back ( + serialize_cmdline (var.name, *ovr.first, storage)); + } + +#if 0 + // TODO: copy over config_vars_ that are not in the map (case #1). +#endif + + // Drop the build system state since it needs reloading (some computed + // values in root.build may depend on the new configuration values). + // + ctx_ = nullptr; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + +#endif + + bool package_skeleton:: + evaluate_prefer_accept (const dependency_configurations& cfgs, + const string& prefer, + const string& accept, + pair indexes) + { + size_t depends_index (indexes.first); + + assert (dependency_reflect_index_ <= depends_index); + + try + { + using namespace build2; + using config::variable_origin; + using build2::fail; + using build2::info; + using build2::endf; + + // Drop the state from the previous evaluation of prefer/accept. + // + if (prefer_accept_) + { + ctx_ = nullptr; + prefer_accept_ = nullopt; + } + + // Drop any dependency reflect values from the previous evaluation of + // this clause, if any. + // + if (dependency_reflect_index_ == depends_index) + dependency_reflect_.resize (dependency_reflect_pending_); + + // This is what needs to happen to the variables of different origins in + // the passed dependency configurations: + // + // default -- set as default (value::extra=1) + // buildfile/dependent -- set as buildfile (value::extra=2) + // override/user -- set as override (so cannot be overriden) + // undefined -- ignored + // + // Note that we set value::extra to 2 for buildfile/dependent values. + // This is done so that we can detect when they were set by this + // dependent (even if to the same value). Note that the build2 config + // module only treats 1 as the default value marker. + // + // Additionally, for all origins we need to typify the variables. + // + // All of this is done by load(), including removing and returning the + // dependency variable prefixes (config.) which we later add + // to dependency_var_prefixes_. + // + strings dvps; + scope& rs (load (cfgs, &dvps, true /* defaults */)); + + // Evaluate the prefer clause. + // + { + istringstream is (prefer); + is.exceptions (istringstream::failbit | istringstream::badbit); + + path_name in (""); + uint64_t il (1); + + auto df = build2::make_diag_frame ( + [this, &prefer, &rs, depends_index] (const build2::diag_record& dr) + { + dr << info << "prefer clause:\n" + << trim_right (string (prefer)); + + // For external packages we have the manifest so print the + // location of the depends value in questions. + // + if (rs.out_eq_src ()) + dr << info << "in depends manifest value of package " + << package.name; + else + depends_location (dr, + rs.src_path () / manifest_file, + depends_index); + }); + + lexer l (is, in, il /* start line */); + buildfile_parser p (rs.ctx, dependency_var_prefixes_); + p.parse_buildfile (l, &rs, rs); + + // Check if the dependent set any stray configuration variables. + // + for (size_t i (0); i != cfgs.size (); ++i) + { + package_configuration& cfg (cfgs[i]); + + const string& ns (dvps[i]); // Parallel. + for (auto p (rs.vars.lookup_namespace (ns)); + p.first != p.second; + ++p.first) + { + const variable& var (p.first->first); + + // This can be one of the overrides (__override, __prefix, etc), + // which we skip. + // + if (var.override ()) + continue; + + if (cfg.find (var.name) == nullptr) + { + fail << "package " << cfg.package.name << " has no " + << "configuration variable " << var.name << + info << var.name << " set in prefer clause of dependent " + << package.string (); + } + } + } + } + + // Evaluate the accept condition. + // + bool r; + { + istringstream is ('(' + accept + ')'); + is.exceptions (istringstream::failbit | istringstream::badbit); + + path_name in (""); + uint64_t il (1); + + auto df = build2::make_diag_frame ( + [this, &accept, &rs, depends_index] (const build2::diag_record& dr) + { + dr << info << "accept condition: (" << accept << ")"; + + // For external packages we have the manifest so print the + // location of the depends value in questions. + // + if (rs.out_eq_src ()) + dr << info << "in depends manifest value of package " + << package.name; + else + depends_location (dr, + rs.src_path () / manifest_file, + depends_index); + }); + + lexer l (is, in, il /* start line */); + buildfile_parser p (rs.ctx, dependency_var_prefixes_); + value v (p.parse_eval (l, rs, rs, parser::pattern_mode::expand)); + + try + { + // Should evaluate to 'true' or 'false'. + // + r = build2::convert (move (v)); + } + catch (const invalid_argument& e) + { + fail (build2::location (in, il)) << e << endf; + } + } + + // If acceptable, update the configuration with the new values, if any. + // + // We also save the subset of values that were set by this dependent to + // be reflected to further clauses. + // + if (r) + { + dependency_reflect_index_ = depends_index; + dependency_reflect_pending_ = dependency_reflect_.size (); + + for (size_t i (0); i != cfgs.size (); ++i) + { + package_configuration& cfg (cfgs[i]); + + const string& ns (dvps[i]); + for (auto p (rs.vars.lookup_namespace (ns)); + p.first != p.second; + ++p.first) + { + const variable& var (p.first->first); + + if (var.override ()) + continue; + + const value& val (p.first->second); + + pair ol ( + config::origin (rs, + var, + pair { + lookup {val, var, rs.vars}, 1 /* depth */})); + + config_variable_value& v (*cfg.find (var.name)); + + // An override cannot become a non-override. And a non-override + // cannot become an override. Except that the dependency override + // could be specified (only) for the dependent. + // + if (v.origin == variable_origin::override_) + { + assert (ol.first == variable_origin::override_); + } + else if (ol.first == variable_origin::override_ && + v.origin != variable_origin::override_) + { + fail << "dependency override " << var.name << " specified for " + << "dependent " << package.string () << " but not dependency" << + info << "did you mean to specify ?" << cfg.package.name + << " +{ " << var.name << "=... }"; + } + + switch (ol.first) + { + case variable_origin::buildfile: + { + optional ns (reverse_value (val)); + + // If this value was set, save it as a dependency reflect. + // + if (val.extra == 0) + { + dependency_reflect_.push_back ( + reflect_variable_value {v.name, ol.first, v.type, ns}); + } + + // Possible transitions: + // + // default/undefine -> buildfile -- override dependency default + // buildfile -> buildfile -- override other dependent + // + if (v.origin == variable_origin::buildfile) + { + // If unchanged, then we keep the old originating dependent + // (even if the value was technically "overwritten" by this + // dependent). + // + if (val.extra == 2 || v.value == ns) + break; + } + else + v.origin = variable_origin::buildfile; + + v.value = move (ns); + v.dependent = package; // We are the originating dependent. + v.confirmed = true; + break; + } + case variable_origin::default_: + { + // A default can only come from a default. + // + assert (ol.first == v.origin); + break; + } + case variable_origin::override_: + { + // If the value was set by this dependent then we need to + // reflect it even if it was overridden (but as the overridden + // value). Note that the mere presence of the value in rs.vars + // is not enough to say that it was set -- it could also be + // the default value. But we can detect that by examining + // value::extra. + // + if (val.extra == 0) + { + dependency_reflect_.push_back ( + reflect_variable_value { + v.name, ol.first, v.type, reverse_value (*ol.second)}); + } + break; + } + case variable_origin::undefined: + { + // Not possible since we have the defined original. + // + assert (false); + break; + } + } + } + } + + // Note that because we add it here, the following reflect clause will + // not be able to expand undefined values. We handle this by keeping a + // pending position. + // + dependency_var_prefixes_pending_ = dependency_var_prefixes_.size (); + dependency_var_prefixes_.insert (dependency_var_prefixes_.end (), + make_move_iterator (dvps.begin ()), + make_move_iterator (dvps.end ())); + + // Note: do not drop the build system state yet since it should be + // reused by the following reflect clause, if any. + // + prefer_accept_ = indexes; + } + else + ctx_ = nullptr; + + return r; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + + bool package_skeleton:: + evaluate_require (const dependency_configurations& cfgs, + const string& require, pair indexes) + { + size_t depends_index (indexes.first); + + assert (dependency_reflect_index_ <= depends_index); + + try + { + using namespace build2; + using config::variable_origin; + using build2::fail; + using build2::info; + using build2::endf; + + // Drop the state from the previous evaluation of prefer/accept. + // + if (prefer_accept_) + { + ctx_ = nullptr; + prefer_accept_ = nullopt; + } + + // Drop any dependency reflect values from the previous evaluation of + // this clause, if any. + // + if (dependency_reflect_index_ == depends_index) + dependency_reflect_.resize (dependency_reflect_pending_); + + // A require clause can only set bool configuration variables and only + // to true and may not have any conditions on other configuration + // variables (including their origin). As a result, we don't need to set + // the default (or other dependent) values, but will need the type + // information as well as overrides. Except that the type information + // may not be available for system packages, so we must deal with + // that. See negotiate_configuration() for details. + // + strings dvps; + scope& rs (load (cfgs, &dvps, false /* defaults */)); + + // Evaluate the require clause. + // + { + istringstream is (require); + is.exceptions (istringstream::failbit | istringstream::badbit); + + path_name in (""); + uint64_t il (1); + + auto df = build2::make_diag_frame ( + [this, &require, &rs, depends_index] (const build2::diag_record& dr) + { + dr << info << "require clause:\n" + << trim_right (string (require)); + + // For external packages we have the manifest so print the + // location of the depends value in questions. + // + if (rs.out_eq_src ()) + dr << info << "in depends manifest value of package " + << package.name; + else + depends_location (dr, + rs.src_path () / manifest_file, + depends_index); + }); + + lexer l (is, in, il /* start line */); + buildfile_parser p (rs.ctx, dependency_var_prefixes_); + p.parse_buildfile (l, &rs, rs); + + // Check for stray variables and enforce all the require restrictions + // (bool, set to true, etc). + // + for (size_t i (0); i != cfgs.size (); ++i) + { + package_configuration& cfg (cfgs[i]); + + const string& ns (dvps[i]); // Parallel. + for (auto p (rs.vars.lookup_namespace (ns)); + p.first != p.second; + ++p.first) + { + // Note that because we didn't set any default (or other + // dependent) values, all the values we see are set by this + // dependent. + // + const variable& var (p.first->first); + + // This can be one of the overrides (__override, __prefix, etc), + // which we skip. + // + if (var.override ()) + continue; + + const value& val (p.first->second); + + // Deal with a system package that has no type information. + // + if (!cfg.system) + { + const config_variable_value* v (cfg.find (var.name)); + + if (v == nullptr) + { + fail << "package " << cfg.package.name << " has no " + << "configuration variable " << var.name << + info << var.name << " set in require clause of dependent " + << package.string (); + } + + if (!v->type || *v->type != "bool") + { + fail << "configuration variable " << var.name << " is not of " + << "bool type" << + info << var.name << " set in require clause of dependent " + << package.string (); + } + } + + bool r; + if (cfg.system) + { + try + { + r = build2::convert (val); + } + catch (const invalid_argument&) + { + r = false; + } + } + else + r = cast_false (val); + + if (!r) + { + fail << "configuration variable " << var.name << " is not set " + << "to true" << + info << var.name << " set in require clause of dependent " + << package.string (); + } + } + } + } + + // First determine if acceptable. + // + bool r (true); + for (size_t i (0); i != cfgs.size (); ++i) + { + package_configuration& cfg (cfgs[i]); + + const string& ns (dvps[i]); + for (auto p (rs.vars.lookup_namespace (ns)); + p.first != p.second; + ++p.first) + { + const variable& var (p.first->first); + + if (var.override ()) + continue; + + const value& val (p.first->second); + + // Note: could be NULL if cfg.system. + // + const config_variable_value* v (cfg.find (var.name)); + + // The only situation where the result would not be acceptable is if + // one of the values were overridden to false. + // + pair ol ( + config::origin (rs, + var, + pair { + lookup {val, var, rs.vars}, 1 /* depth */})); + + // An override cannot become a non-override. And a non-override + // cannot become an override. Except that the dependency override + // could be specified (only) for the dependent. + // + if (v != nullptr && v->origin == variable_origin::override_) + { + assert (ol.first == variable_origin::override_); + } + else if (ol.first == variable_origin::override_ && + (v == nullptr || v->origin != variable_origin::override_)) + { + fail << "dependency override " << var.name << " specified for " + << "dependent " << package.string () << " but not dependency" << + info << "did you mean to specify ?" << cfg.package.name + << " +{ " << var.name << "=... }"; + } + + if (ol.first == variable_origin::override_) + { + if (cfg.system) + { + try + { + if (!build2::convert (*ol.second)) + r = false; + } + catch (const invalid_argument&) + { + r = false; + } + } + else + { + if (!cast_false (*ol.second)) + r = false; + } + } + } + } + + // If acceptable, update the configuration with the new values, if any. + // + // Note that we cannot easily combine this loop with the above because + // we should not modify configurations if the result is not acceptable. + // + // We also save the subset of values that were set by this dependent to + // be reflected to further clauses. + // + if (r) + { + dependency_reflect_index_ = depends_index; + dependency_reflect_pending_ = dependency_reflect_.size (); + + for (size_t i (0); i != cfgs.size (); ++i) + { + package_configuration& cfg (cfgs[i]); + + const string& ns (dvps[i]); + for (auto p (rs.vars.lookup_namespace (ns)); + p.first != p.second; + ++p.first) + { + const variable& var (p.first->first); + + if (var.override ()) + continue; + + config_variable_value* v (cfg.find (var.name)); + + if (v == nullptr) // cfg.system + { + cfg.push_back ( + config_variable_value { + var.name, variable_origin::undefined, {}, {}, {}, false}); + v = &cfg.back (); + } + + // This value was set so save it as a dependency reflect. + // + // Note that unlike the equivalent evaluate_prefer_accept() logic, + // here the value cannot be the default/buildfile (since we don't + // set those; see the load() call above). + // + optional ns (names {name ("true")}); + + // Note: force bool type if system. + // + dependency_reflect_.push_back ( + reflect_variable_value { + v->name, + (v->origin == variable_origin::override_ + ? v->origin + : variable_origin::buildfile), + cfg.system ? optional ("bool") : v->type, + ns}); + + if (v->origin != variable_origin::override_) + { + // Possible transitions: + // + // default/undefine -> buildfile -- override dependency default + // buildfile -> buildfile -- override other dependent + // + + if (v->origin == variable_origin::buildfile) + { + // If unchanged, then we keep the old originating dependent + // (even if the value was technically "overwritten" by this + // dependent). + // + if (v->value == ns) + continue; + } + else + v->origin = variable_origin::buildfile; + + v->value = move (ns); + v->dependent = package; // We are the originating dependent. + v->confirmed = true; + } + } + } + + dependency_var_prefixes_.insert (dependency_var_prefixes_.end (), + make_move_iterator (dvps.begin ()), + make_move_iterator (dvps.end ())); + } + + // Drop the build system state since it needs reloading (while it may + // seem safe for us to keep the state since we didn't set any defaults, + // we may have overrides that the clause did not set, so let's drop it + // for good measure and also to keep things simple). + // + ctx_ = nullptr; + + return r; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } + } + + bool package_skeleton:: + empty_print () + { + if (!loaded_old_config_) + load_old_config (); + + return (dependent_vars_.empty () && + reflect_.empty () && + find_if (config_vars_.begin (), config_vars_.end (), + [this] (const string& v) + { + // See print_config() for details. + // + size_t vn; + if (project_override (v, var_prefix_, &vn)) + { + if (!develop_) + { + size_t pn (var_prefix_.size ()); + if (v.compare (pn, vn - pn, ".develop") == 0) + return false; + } + + return true; + } + return false; + }) == config_vars_.end ()); + } + + void package_skeleton:: + print_config (ostream& os, const char* indent) + { + using build2::config::variable_origin; + + if (!loaded_old_config_) + load_old_config (); + + auto print = [&os, + indent, + first = true] (const string& v) mutable -> ostream& + { + if (first) + first = false; + else + os << '\n'; + + os << indent << v; + return os; + }; + + // NOTE: see also empty_print() if changing anything here. + + // @@ TODO: we could have added the package itself to "set by ..." + // for overriden (to the same value) reflect. But then why not + // do the same for dependent that is overriden by user (we could + // have kept them as reflect_variable_values rather than strings)? + // Maybe one day. + + // First comes the user configuration. + // + for (const string& v: config_vars_) + { + size_t vn; + if (project_override (v, var_prefix_, &vn)) + { + // To reduce the noise (e.g., during bdep-init), skip + // config..develop if the package doesn't use it. + // + if (!develop_) + { + size_t pn (var_prefix_.size ()); + if (v.compare (pn, vn - pn, ".develop") == 0) + continue; + } + + print (v) << " (" << (system ? "expected " : "") + << "user configuration)"; + } + } + + // Next dependent configuration. + // + for (size_t i (0); i != dependent_vars_.size (); ++i) + { + const string& v (dependent_vars_[i]); + const package_key& d (dependent_orgs_[i]); // Parallel. + + print (v) << " (" << (system ? "expected" : "set") << " by " + << d << ')'; + } + + // Finally reflect (but skip overriden). + // + for (const reflect_variable_value& v: reflect_) + { + if (v.origin == variable_origin::override_) + continue; + + string s (serialize_cmdline (v.name, v.value)); + print (s) << " (" << (system ? "expected" : "set") << " by " + << package.name << ')'; + } + } + + pair> package_skeleton:: + collect_config () && + { + assert (db_ != nullptr); // Must be called only once. + + using build2::config::variable_origin; + + if (!loaded_old_config_) + load_old_config (); + + // Merge all the variables into a single list in the correct order + // and assign their sources while at it. + // + strings vars; + vector srcs; + + if (size_t n = (config_vars_.size () + + dependent_vars_.size () + + reflect_.size ())) + { + // For vars we will steal the first non-empty *_vars_. But for sources + // reserve the space. + // + srcs.reserve (n); // At most that many. + + // Return the variable name given the variable override. + // + auto var_name = [] (const string& v) + { + size_t p (v.find_first_of ("=+ \t")); + assert (p != string::npos); + return string (v, 0, p); + }; + + // Note that we assume the three sets of variables do not clash. + // + + // First comes the user configuration. + // + if (!config_vars_.empty ()) + { + // Assign the user source only to user-specified configuration + // variables which are project variables (i.e., names start with + // config.). + // + for (const string& v: config_vars_) + { + if (project_override (v, var_prefix_)) + { + string n (var_name (v)); + + // Check for a duplicate. + // + auto i (find_if (srcs.begin (), srcs.end (), + [&n] (const config_variable& cv) + { + return cv.name == n; + })); + + if (i == srcs.end ()) + srcs.push_back (config_variable {move (n), config_source::user}); + } + } + + vars = move (config_vars_); + } + + // Next dependent configuration. + // + if (!dependent_vars_.empty ()) + { + // These are all project variables. There should also be no duplicates + // by construction. + // + for (const string& v: dependent_vars_) + srcs.push_back ( + config_variable {var_name (v), config_source::dependent}); + + if (vars.empty ()) + vars = move (dependent_vars_); + else + { + vars.reserve (n); + vars.insert (vars.end (), + make_move_iterator (dependent_vars_.begin ()), + make_move_iterator (dependent_vars_.end ())); + } + } + + // Finally reflect. + // + if (!reflect_.empty ()) + { + vars.reserve (n); + + // These are all project variables. There should also be no duplicates + // by construction (see evaluate_reflect()). + // + for (const reflect_variable_value& v: reflect_) + { + if (v.origin == variable_origin::override_) + continue; + + vars.push_back (serialize_cmdline (v.name, v.value)); + srcs.push_back (config_variable {v.name, config_source::reflect}); + } + } + } + + ctx_ = nullptr; // Free. + db_ = nullptr; + + return make_pair (move (vars), move (srcs)); + } + + const strings& package_skeleton:: + merge_cmd_vars (const strings& dependent_vars, + const strings& dependency_vars, + bool cache) + { + // Merge variable overrides (note that the order is important). See also a + // custom/optimized version in load_old_config(). + // + if (!cache || !cmd_vars_cache_) + { + const strings& vs1 (build2_cmd_vars); + const strings& vs2 (config_vars_); + const strings& vs3 (dependent_vars); // Should not override. + const strings& vs4 (dependency_vars); // Should not override. + + // Try to reuse both vector and string buffers. + // + cmd_vars_.resize ( + 1 + vs1.size () + vs2.size () + vs3.size () + vs4.size ()); + + size_t i (0); + { + string& v (cmd_vars_[i++]); + + // If the package is being disfigured, then don't load config.build at + // all. Otherwise, disfigure all package variables (config.**). + // + // Note that this semantics must be consistent with how we actually + // configure the package in pkg_configure(). + // + if (disfigure_) + v = "config.config.unload=true"; + else + { + // Note: must be quoted to preserve the pattern. + // + v = "config.config.disfigure='config."; + v += package.name.variable (); + v += "**'"; + } + } + + for (const string& v: vs1) cmd_vars_[i++] = v; + for (const string& v: vs2) cmd_vars_[i++] = v; + for (const string& v: vs3) cmd_vars_[i++] = v; + for (const string& v: vs4) cmd_vars_[i++] = v; + + cmd_vars_cache_ = cache; + } + + return cmd_vars_; + } + + void package_skeleton:: + load_old_config () + { + assert (!loaded_old_config_ && ctx_ == nullptr); + + try + { + using namespace build2; + + // This load that must be done without config.config.disfigure. Also, it + // would be nice to optimize for the common case where the only load is + // to get the old configuration (e.g., config.*.develop) as part of + // collect_config(). So instead of calling merge_cmd_vars() we will do + // our own (but consistent) thing. + // + const strings* cmd_vars (nullptr); + { + assert (!cmd_vars_cache_); // Sanity check (we are always first). + + const strings& vs1 (build2_cmd_vars); + const strings& vs2 (config_vars_); + + if (!disfigure_) + cmd_vars = (vs2.empty () ? &vs1 : vs1.empty () ? &vs2 : nullptr); + + if (cmd_vars == nullptr) { - // NOTE: see also above and below if enabling this. - // -#if 0 - // Case #3. + // Note: the order is important (see merge_cmd_vars()). // - // The override can come from two places: config_vars_ or one of the - // "global" sources (environment variable, default options file; see - // load() for details). The latter can only be a global override and - // can be found (together with global overrides from config_vars_) - // in context::global_var_overrides. - // - // It feels like mixing global overrides and reflect is misguided: - // we probably don't want to rewrite it with a global override (per - // case #3 above) since it will apply globally. So let's diagnose it - // for now. + cmd_vars_.reserve ((disfigure_ ? 1 : 0) + vs1.size () + vs2.size ()); + + // If the package is being disfigured, then don't load config.build + // at all. // - { - const strings& ovs (ctx_->global_var_overrides); - auto i (find_if (ovs.begin (), ovs.end (), - [&var] (const string& o) - { - // TODO: extracting name is not easy. - })); + if (disfigure_) + cmd_vars_.push_back ("config.config.unload=true"); - if (i != ovs.end ()) - { - fail << "global override for reflect clause variable " << var << - info << "global override: " << *i; - } - } + cmd_vars_.insert (cmd_vars_.end (), vs1.begin (), vs1.end ()); + cmd_vars_.insert (cmd_vars_.end (), vs2.begin (), vs2.end ()); - // Ok, this override must be present in config_vars_. - // - // @@ Extracting the name from config_vars_ and similar is not easy: - // they are buildfile fragments and context actually parses them. - // - // @@ What if we have multiple overrides? - // - // @@ What if it's some scoped override or some such (e.g., all - // these .../x=y, etc). - // - // @@ Does anything change if we have an override but it does not - // apply (i.e., ovr == org && var.overrides != nullptr)? - // - // @@ Perhaps a sensible approach is to start relaxing/allowing - // this for specific, sensible cases (e.g., single unqualified - // override)? - // - // What would be the plausible scenarios for an override? - // - // 1. Append override that adds some backend or some such to the - // reflect value. - // - // 2. A reflect may enable a feature based on the dependency - // alternative selected (e.g., I see we are using Qt6 so we might - // as well enable feature X). The user may want do disable it - // with an override. - // - ovr = rs.lookup_override (var, org); -#else - fail << "command line override of reflect clause variable " << var - << endf; -#endif + cmd_vars = &cmd_vars_; } + } - string s (var.name + '='); + scope& rs (*bootstrap (*this, *cmd_vars)->second.front ()); - if (val.null) - s += "[null]"; - else + // Load project's root.build. + // + load_root (rs); + + if (const variable* var = rs.var_pool ().find (var_prefix_ + ".develop")) + { + // Use the fact that the variable is typed as a proxy for it being + // defined with config directive (the more accurate way would be via + // the config module's saved variables map). + // + develop_ = (var->type != nullptr); + } + + // @@ TODO: should we also verify user-specified project configuration + // variables are not bogus? But they could be untyped... + // + // Also, build2 warns about unused variables being dropped. + // + // Note that currently load_old_config() is disabled unless there is + // a config.*.develop variable; see package_skeleton ctor. + + // Extract and merge old user configuration variables from config.build + // (or equivalent) into config_vars. + // + if (config_srcs_ != nullptr) + { + assert (!disfigure_); + + auto i (config_vars_.begin ()); // Insert position, see below. + + names storage; + for (const config_variable& v: *config_srcs_) { - storage.clear (); - names_view nv (reverse (*ovr.first, storage)); + if (v.source != config_source::user) + continue; + + using config::variable_origin; + + pair ol (config::origin (rs, v.name)); - if (!nv.empty ()) + switch (ol.first) { - // Note: we need to use command-line (effective) quoting. - // - ostringstream os; - to_stream (os, nv, quote_mode::effective, '@'); - s += os.str (); + case variable_origin::override_: + { + // Already in config_vars. + // + // @@ TODO: theoretically, this could be an append/prepend + // override(s) and to make this work correctly we would need + // to replace them with an assign override with the final + // value. Maybe one day. + // + break; + } + case variable_origin::buildfile: + { + // Doesn't really matter where we add them though conceptually + // feels like old should go before new (and in the original + // order). + // + i = config_vars_.insert ( + i, + serialize_cmdline (v.name, *ol.second, storage)) + 1; + + break; + } + case variable_origin::undefined: + case variable_origin::default_: + { + // Old user configuration no longer in config.build. We could + // complain but that feels overly drastic. Seeing that we will + // recalculate the new set of config variable sources, let's + // just ignore this (we could issue a warning, but who knows how + // many times it will be issued with all this backtracking). + // + break; + } } } - - reflect_vars_.push_back (move (s)); } -#if 0 - // TODO: copy over config_vars_ that are not in the map (case #1). -#endif - - // Drop the build system state since it needs reloading (some computed - // values in root.build may depend on the new configuration values). - // + loaded_old_config_ = true; + verified_ = true; // Managed to load without errors. ctx_ = nullptr; } catch (const build2::failed&) @@ -561,75 +2563,241 @@ namespace bpkg } } - pair>> package_skeleton:: - collect_config () && + build2::scope& package_skeleton:: + load (const dependency_configurations& cfgs, strings* dvps, bool defaults) { - // Note that if the reflect variables list is not empty, then it also - // contains the user-specified configuration variables, which all come - // first (see above). - // - size_t nc (config_vars_.size ()); + if (ctx_ != nullptr) + { + // We have to reload if there is any dependency configuration. + // + if (cfgs.empty ()) + return *rs_; - strings vars (reflect_vars_.empty () - ? move (config_vars_) - : move (reflect_vars_)); + ctx_ = nullptr; + } - vector> sources; + if (!loaded_old_config_) + load_old_config (); - if (!vars.empty ()) + try { - sources.reserve (vars.size ()); + using namespace build2; + using build2::config::variable_origin; + + // If we have any dependency configurations, then here we need to add + // dependency configuration variables with the override origin to the + // command line overrides (see evaluate_prefer_accept() for details). + // While at it, handle dependency variable prefixes. + // + strings dependency_vars; + for (const package_configuration& cfg: cfgs) + { + for (const config_variable_value& v: cfg) + { + if (v.origin == variable_origin::override_) + dependency_vars.push_back (v.serialize_cmdline ()); + } + + string p ("config." + cfg.package.name.variable ()); + + auto i (find (dependency_var_prefixes_.begin (), + dependency_var_prefixes_.end (), + p)); + if (i != dependency_var_prefixes_.end ()) + dependency_var_prefixes_.erase (i); + + dvps->push_back (move (p)); + } + + // If there aren't any, then we can reuse already merged cmd_vars (they + // don't change during evaluate_*() calls except for the dependency + // overrides case). + // + const strings& cmd_vars ( + merge_cmd_vars (dependent_vars_, + dependency_vars, + dependency_vars.empty () /* cache */)); - // Assign the user source to only those user-specified configuration - // variables which are project variables (i.e., names start with - // config.). Assign the reflect source to all variables that - // follow the user-specified configuration variables (which can only be - // project variables). + auto rsi (bootstrap (*this, cmd_vars)); + scope& rs (*rsi->second.front ()); + + // Load project's root.build as well as potentially accumulated reflect + // variables. + // + // If we have the accumulated reflect variables, wedge them just before + // loading root.build (but after initializing config which may load + // config.build and which we wish to override). + // + // Note that the plan for non-external packages is to extract the + // configuration and then load it with config.config.load and this + // approach should work for that case too. // - // Note that some user-specified variables may have qualifications - // (global, scope, etc) but there is no reason to expect any project - // configuration variables to use such qualifications (since they can - // only apply to one project). So we skip all qualified variables. + // This is also where we set dependency configuration variables with the + // default and buildfile origins and typify all dependency variables + // (see evaluate_prefer_accept() for details). // - string p ("config." + name ().variable ()); - size_t n (p.size ()); + function pre; - for (const string& v: vars) + struct data { - if (sources.size () < nc) + scope& rs; + const dependency_configurations& cfgs; + bool defaults; + } d {rs, cfgs, defaults}; + + if (!cfgs.empty () || + !reflect_.empty () || + !dependency_reflect_.empty ()) + { + pre = [this, &d] (parser&) { - if (v.compare (0, n, p) == 0 && strchr (".=+ \t", v[n]) != nullptr) - sources.push_back (config_source::user); - else - sources.push_back (nullopt); - } - else - sources.push_back (config_source::reflect); + scope& rs (d.rs); + + auto insert_var = [&rs] (const string& name, + const optional& type) + -> const variable& + { + const value_type* vt (nullptr); + if (type) + { + vt = parser::find_value_type (&rs, *type); + assert (vt != nullptr); + } + + return rs.var_pool ().insert (name, vt); + }; + + for (const reflect_variable_value& v: reflect_) + { + if (v.origin == variable_origin::override_) + continue; + + const variable& var (insert_var (v.name, v.type)); // Note: untyped. + value& val (rs.assign (var)); + + if (v.value) + val.assign (names (*v.value), &var); + else + val = nullptr; + } + + // Note that for now we don't bother setting overridden reflect + // values as overrides. It seems the only reason to go through the + // trouble would be to get the accurate $origin() result. But basing + // any decisions on whether the reflect value was overridden or not + // seems far fetched. + // + for (const reflect_variable_value& v: dependency_reflect_) + { + const variable& var (insert_var (v.name, v.type)); + value& val (rs.assign (var)); + + if (v.value) + val.assign (names (*v.value), &var); + else + val = nullptr; + } + + for (const package_configuration& cfg: d.cfgs) + { + for (const config_variable_value& v: cfg) + { + const variable& var (insert_var (v.name, v.type)); + + switch (v.origin) + { + case variable_origin::default_: + case variable_origin::buildfile: + { + if (d.defaults) + { + auto& val ( + static_cast ( + rs.assign (var))); + + if (v.value) + val.assign (names (*v.value), &var); + else + val = nullptr; + + val.extra = v.origin == variable_origin::default_ ? 1 : 2; + } + break; + } + case variable_origin::undefined: + case variable_origin::override_: break; + } + } + } + }; } + + load_root (rs, pre); + + setup_base (rsi, + out_root_.empty () ? src_root_ : out_root_, + src_root_); + + rs_ = &rs; + return rs; + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. } + } - assert (db_ != nullptr); + // Create the build context. + // + static void + create_context (package_skeleton& skl, const strings& cmd_vars) + { + assert (skl.db_ != nullptr && skl.ctx_ == nullptr); - ctx_ = nullptr; // In case we only had conditions. + // Initialize the build system. + // + if (!build2_sched.started ()) + build2_init (*skl.co_); - db_ = nullptr; - available_ = nullptr; + try + { + using namespace build2; + using build2::fail; + using build2::endf; - return make_pair (move (vars), move (sources)); + // Create build context. + // + skl.ctx_.reset ( + new context (build2_sched, + build2_mutexes, + build2_fcache, + false /* match_only */, // Shouldn't matter. + false /* no_external_modules */, + false /* dry_run */, // Shouldn't matter. + false /* keep_going */, // Shouldnt' matter. + cmd_vars)); + } + catch (const build2::failed&) + { + throw failed (); // Assume the diagnostics has already been issued. + } } - build2::scope& package_skeleton:: - load () + // Bootstrap the package skeleton. + // + static build2::scope_map::iterator + bootstrap (package_skeleton& skl, const strings& cmd_vars) { - if (ctx_ != nullptr) - return *rs_; + assert (skl.db_ != nullptr && + skl.ctx_ == nullptr && + skl.available != nullptr); // The overall plan is as follows: // // 0. Create filesystem state if necessary (could have been created by // another instance, e.g., during simulation). // - // 1. Load the state potentially with accumulated reflect fragment. + // 1. Bootstrap the package skeleton. // // Creating a new context is not exactly cheap (~1.2ms debug, 0.08ms // release) so we could try to re-use it by cleaning all the scopes other @@ -640,22 +2808,22 @@ namespace bpkg // Create the skeleton filesystem state, if it doesn't exist yet. // - if (!created_) + if (!skl.created_) { - const available_package& ap (*available_); + const available_package& ap (*skl.available); // Note that we create the skeleton directories in the skeletons/ // subdirectory of the configuration temporary directory to make sure // they never clash with other temporary subdirectories (git // repositories, etc). // - if (src_root_.empty () || out_root_.empty ()) + if (skl.src_root_.empty () || skl.out_root_.empty ()) { // Cannot be specified if src_root_ is unspecified. // - assert (out_root_.empty ()); + assert (skl.out_root_.empty ()); - auto i (tmp_dirs.find (db_->config_orig)); + auto i (tmp_dirs.find (skl.db_->config_orig)); assert (i != tmp_dirs.end ()); // Make sure the source and out root directories, if set, are absolute @@ -667,15 +2835,15 @@ namespace bpkg dir_path d (normalize (i->second, "temporary directory")); d /= "skeletons"; - d /= name ().string () + '-' + ap.version.string (); + d /= skl.package.name.string () + '-' + ap.version.string (); - if (src_root_.empty ()) - src_root_ = move (d); // out_root_ is the same. + if (skl.src_root_.empty ()) + skl.src_root_ = move (d); // out_root_ is the same. else - out_root_ = move (d); // Don't even need to create it. + skl.out_root_ = move (d); // Don't even need to create it. } - if (!exists (src_root_)) + if (!exists (skl.src_root_)) { // Create the buildfiles. // @@ -684,7 +2852,7 @@ namespace bpkg // additional files. // { - path bf (src_root_ / std_bootstrap_file); + path bf (skl.src_root_ / std_bootstrap_file); mk_p (bf.directory ()); @@ -707,7 +2875,7 @@ namespace bpkg save (*ap.bootstrap_build, bf); if (ap.root_build) - save (*ap.root_build, src_root_ / std_root_file); + save (*ap.root_build, skl.src_root_ / std_root_file); } // Create the manifest file containing the bare minimum of values @@ -716,7 +2884,7 @@ namespace bpkg // { package_manifest m; - m.name = name (); + m.name = skl.package.name; m.version = ap.version; // Note that there is no guarantee that the potential build2 @@ -738,7 +2906,7 @@ namespace bpkg m.dependencies.push_back (das); } - path mf (src_root_ / manifest_file); + path mf (skl.src_root_ / manifest_file); try { @@ -763,58 +2931,19 @@ namespace bpkg } } - created_ = true; + skl.created_ = true; } - // Initialize the build system. - // - if (!build2_sched.started ()) - build2_init (*co_); - try { using namespace build2; using build2::fail; using build2::endf; - // Create build context. - // - // We can reasonably assume reflect cannot have global or absolute scope - // variable overrides so we don't need to pass them to context. - - // Merge variable overrides (note that the order is important). + // Create the build context. // - strings* cmd_vars; - { - strings& v1 (build2_cmd_vars); - strings& v2 (config_vars_); - - cmd_vars = (v2.empty () ? &v1 : v1.empty () ? &v2 : nullptr); - - if (cmd_vars == nullptr) - { - if (cmd_vars_.empty ()) // Cached. - { - cmd_vars_.reserve (v1.size () + v2.size ()); - cmd_vars_.assign (v1.begin (), v1.end ()); - cmd_vars_.insert (cmd_vars_.end (), v2.begin (), v2.end ()); - } - - cmd_vars = &cmd_vars_; - } - } - - ctx_.reset ( - new context (build2_sched, - build2_mutexes, - build2_fcache, - false /* match_only */, // Shouldn't matter. - false /* no_external_modules */, - false /* dry_run */, // Shouldn't matter. - false /* keep_going */, // Shouldnt' matter. - *cmd_vars)); - - context& ctx (*ctx_); + create_context (skl, cmd_vars); + context& ctx (*skl.ctx_); // This is essentially a subset of the steps we perform in b.cxx. See // there for more detailed comments. @@ -835,8 +2964,10 @@ namespace bpkg // Note that it's ok for out_root to not exist (external package). // - const dir_path& src_root (src_root_); - const dir_path& out_root (out_root_.empty () ? src_root_ : out_root_); + const dir_path& src_root (skl.src_root_); + const dir_path& out_root (skl.out_root_.empty () + ? skl.src_root_ + : skl.out_root_); auto rsi (create_root (ctx, out_root, src_root)); scope& rs (*rsi->second.front ()); @@ -849,16 +2980,31 @@ namespace bpkg if (!v) v = src_root; else - assert (cast (v) == src_root); + { + // If the package directory was moved, then it's possible we will have + // src-root.build with an old src_root value. Presumably this will + // cause the package to be re-configured and so ignoring the old value + // here should be ok. Note that the outdated src-root.build can also + // mess up subproject discovery in create_bootstrap_outer() but we + // omit that part. + // + if (cast (v) != src_root) + v = src_root; + } setup_root (rs, false /* forwarded */); bootstrap_pre (rs, altn); bootstrap_src (rs, altn, - db_->config.relative (out_root) /* amalgamation */, - false /* subprojects */); + skl.db_->config.relative (out_root) /* amalgamation */, + false /* subprojects */); + - create_bootstrap_outer (rs); + // Omit discovering amalgamation's subprojects (i.e., all the packages + // in the configuration). Besides being a performance optimization, this + // also sidesteps the issue of outdated src-root.build (see above). + // + create_bootstrap_outer (rs, false /* subprojects */); bootstrap_post (rs); assert (mif.meta_operation_pre == nullptr); @@ -866,41 +3012,7 @@ namespace bpkg ctx.enter_project_overrides (rs, out_root, ctx.var_overrides); - // Load project's root.build. - // - // If we have the accumulated reflect fragment, wedge it just before - // loading root.build (but after initializing config which may load - // config.build and which we wish to override). - // - // Note that the plan for non-external packages is to extract the - // configuration and then load it with config.config.load and this - // approach should work for that case too. - // - function pre; - - if (!reflect_frag_.empty ()) - { - pre = [this, &rs] (parser& p) - { - istringstream is (reflect_frag_); - is.exceptions (istringstream::failbit | istringstream::badbit); - - // Note that the fragment is just a bunch of variable assignments - // and thus unlikely to cause any errors. - // - path_name in (""); - p.parse_buildfile (is, in, &rs, rs); - }; - } - - load_root (rs, pre); - - // Setup root scope as base. - // - setup_base (rsi, out_root, src_root); - - rs_ = &rs; - return rs; + return rsi; } catch (const build2::failed&) { diff --git a/bpkg/package-skeleton.hxx b/bpkg/package-skeleton.hxx index aaa9b8c..7224d1d 100644 --- a/bpkg/package-skeleton.hxx +++ b/bpkg/package-skeleton.hxx @@ -4,12 +4,13 @@ #ifndef BPKG_PACKAGE_SKELETON_HXX #define BPKG_PACKAGE_SKELETON_HXX -#include // build2::context +#include #include #include #include +#include #include namespace bpkg @@ -20,6 +21,12 @@ namespace bpkg class package_skeleton { public: + // If the package is system, then its available package should be NULL if + // it doesn't match the system package version "close enough" to be usable + // as the source of its configuration information (types, defaults). If it + // is NULL, then the skeleton can only be used to print and collect the + // configuration information. + // // If the package is external, then the existing package source root // directory needs to be specified (as absolute and normalized). In this // case, if output root is specified (as absolute and normalized; normally @@ -29,50 +36,152 @@ namespace bpkg // If the package is not external, then none of the root directories // should be specified. // - // Note that the options, database, and available_package are expected to + // The disfigure argument should indicate whether the package is being + // reconfigured from scratch (--disfigure). + // + // The config_vars argument contains configuration variables specified by + // the user in this bpkg execution. Optional config_srcs is used to + // extract (from config.build or equivalent) configuration variables + // specified by the user in previous bpkg executions. It should be NULL if + // this is the first build of the package. The extracted variables are + // merged with config_vars and the combined result is returned by + // collect_config() below. + // + // @@ TODO: speaking of the "config.build or equivalent" part, the + // equivalent is likely to be extracted configuration (probably saved + // to file in tmp somewhere) that we will load with config.config.load. + // It doesn't seem like a good idea to pass it as part of config_vars + // (because sometimes we may need to omit it) so most likely it will be + // passed as a separate arguments (likely a file path). + // + // Note that the options, database, and config_srcs are expected to // outlive this object. // // Note also that this creates an "unloaded" skeleton and is therefore // relatively cheap. // package_skeleton (const common_options& co, - database&, - const available_package&, + package_key, + bool system, + shared_ptr, strings config_vars, + bool disfigure, + const vector* config_srcs, optional src_root, optional out_root); - // For the following evaluate_*() functions assume that the clause belongs - // to the specified (by index) depends value (used to print its location - // on failure for an external package). + + package_key package; + bool system; + shared_ptr available; + + // The following functions should be called in the following sequence + // (* -- zero or more, ? -- zero or one): + // + // * reload_defaults() | verify_sensible() + // ? dependent_config() + // * evaluate_*() + // * empty() | print_config() + // collect_config() + // + // Note that a copy of the skeleton is expected to continue with the + // sequence rather than starting from scratch, unless reset() is called. + // + public: + // Reload the default values and type information for configuration + // variables using the values with the buildfile origin as a "tentative" + // dependent configuration. + // + void + reload_defaults (package_configuration&); + + // Load overrides for a system package without skeleton info. Note that + // this is done in an ad hoc manner and only to support evaluate_require() + // semantics (see the implementation for details). + // + void + load_overrides (package_configuration&); + + // Verify the specified "tentative" dependent configuration is sensible, + // that is, acceptable to the dependency itself. If it is not, then the + // second half of the result contains the diagnostics. + // + pair + verify_sensible (const package_configuration&); + + // Incorporate the "final" dependent configuration into subsequent + // evaluations. Dependent configuration variables are expected not to + // clash with user. // + void + dependent_config (const package_configuration&); + + // For the following evaluate_*() functions assume that the clause belongs + // to the dependency alternative specified as a pair of indexes (depends + // value index and alternative index). + // Evaluate the enable clause. // bool - evaluate_enable (const string&, size_t depends_index); + evaluate_enable (const string&, pair); // Evaluate the reflect clause. // void - evaluate_reflect (const string&, size_t depends_index); + evaluate_reflect (const string&, pair); + + // Evaluate the prefer/accept or require clauses on the specified + // dependency configurations (serves as both input and output). + // + // Return true is acceptable and false otherwise. If acceptable, the + // passed configuration is updated with new values, if any. + // + using dependency_configurations = + small_vector, 1>; + + bool + evaluate_prefer_accept (const dependency_configurations&, + const string&, const string&, pair); + + bool + evaluate_require (const dependency_configurations&, + const string&, pair); + + // Reset the skeleton to the start of the call sequence. + // + // Note that this function cannot be called after collect_config(). + // + void + reset (); - // Return the accumulated reflect values together with their sources (the - // resulting vectors are parallel). + // Return true if there are no accumulated *project* configuration + // variables that will be printed by print_config(). // - // Note that the result is merged with config_vars and should be used - // instead rather than in addition to config_vars. The source of - // configuration variables which are not the project variables is absent. + bool + empty_print (); + + // Print the accumulated *project* configuration variables as command line + // overrides one per line with the specified indentation. + // + void + print_config (ostream&, const char* indent); + + // Return the accumulated configuration variables (first) and project + // configuration variable sources (second). Note that the arrays are not + // necessarily parallel (config_vars may contain non-project variables). + // + // Note that the dependent and reflect variables are merged with + // config_vars/config_srcs and should be used instead rather than in + // addition to config_vars. // // Note also that this should be the final call on this object. // - pair>> + pair> collect_config () &&; - const package_name& - name () const {return available_->id.name;} - // Implementation details. // + public: // We have to define these because context is forward-declared. Also, copy // constructor has some special logic. // @@ -84,32 +193,146 @@ namespace bpkg package_skeleton& operator= (const package_skeleton&) = delete; private: - // Create the skeleton if necessary and (re)load the build system state. + // Load old user configuration variables from config.build (or equivalent) + // and merge them into config_vars_. Also verify new user configuration + // already in config_vars_ makes sense. + // + // This should be done before any attempt to load the configuration with + // config.config.disfigure and, if this did not happen, inside + // collect_config() (since the package will be reconfigured with + // config.config.disfigure). + // + void + load_old_config (); + + // (Re)load the build system state. // // Call this function before evaluating every clause. // + // If dependency configurations are specified, then typify the variables + // and set their values. If defaults is false, then only typify the + // variables and set overrides without setting the default/buildfile + // values. Note that buildfile values have value::extra set to 2. While + // at it, also remove from dependency_var_prefixes_ and add to + // dependency_var_prefixes variable prefixes (config.) for + // the passed dependencies. + // build2::scope& - load (); + load (const dependency_configurations& = {}, + strings* dependency_var_prefixes = nullptr, + bool defaults = true); - private: + // Merge command line variable overrides into one list (normally to be + // passed to bootstrap()). + // + // If cache is true, then assume the result can be reused on subsequent + // calls. + // + const strings& + merge_cmd_vars (const strings& dependent_vars, + const strings& dependency_vars = {}, + bool cache = false); + + // Implementation details (public for bootstrap()). + // + public: // NOTE: remember to update move/copy constructors! // const common_options* co_; database* db_; - const available_package* available_; + + string var_prefix_; // config. + strings config_vars_; + bool disfigure_; + const vector* config_srcs_; // NULL if nothing to do or + // already done. dir_path src_root_; // Must be absolute and normalized. dir_path out_root_; // If empty, the same as src_root_. bool created_ = false; + bool verified_ = false; + bool loaded_old_config_; + bool develop_ = true; // Package has config.*.develop. + unique_ptr ctx_; build2::scope* rs_ = nullptr; - strings cmd_vars_; // Storage for merged build2_cmd_vars and config_vars_. - strings reflect_names_; // Reflect configuration variable names. - strings reflect_vars_; // Reflect configuration variable overrides. - string reflect_frag_; // Reflect configuration variable fragment. + // Storage for merged build2_cmd_vars and config_vars_ and extra overrides + // (like config.config.disfigure). If cache is true, then the existing + // content can be reused. + // + strings cmd_vars_; + bool cmd_vars_cache_ = false; + + strings dependent_vars_; // Dependent variable overrides. + vector dependent_orgs_; // Dependent originators (parallel). + + // Reflect variable value storage. Used for both real reflect and + // dependency reflect. + // + struct reflect_variable_value + { + string name; + build2::config::variable_origin origin; + optional type; + optional value; + }; + + class reflect_variable_values: public vector + { + public: + const reflect_variable_value* + find (const string& name) + { + auto i (find_if (begin (), end (), + [&name] (const reflect_variable_value& v) + { + return v.name == name; + })); + return i != end () ? &*i : nullptr; + } + }; + + reflect_variable_values reflect_; // Reflect variables. + + // Dependency configuration variables set by the prefer/require clauses + // and that should be reflected in subsequent clauses. + // + // The same prefer/require clause could be re-evaluated multiple times in + // which case the previous dependency reflect values from this clause (but + // not from any previous clauses) should be dropped. This is achieved by + // keeping track of the depends_index for the most recently evaluated + // prefer/require clause along with the position of the first element that + // was added by this clause. Note also that this logic does the right + // thing if we move to a different dependency alternative withing the same + // depends value. + // + reflect_variable_values dependency_reflect_; + size_t dependency_reflect_index_ = 0; + size_t dependency_reflect_pending_ = 0; + + // List of variable prefixes (config.) of all known dependencies. + // + // This information is used to detect and diagnose references to undefined + // dependency configuration variables (for example, those that were not + // set and therefore not reflected). The pending index is used to ignore + // the entries added by the last evaluate_prefer_accept() in the following + // reflect clause (see prefer_accept_ below for details). + // + strings dependency_var_prefixes_; + size_t dependency_var_prefixes_pending_ = 0; + + // Position of the last successfully evaluated prefer/accept clauses. + // + // This information is used to make all (as opposed to only those set by + // the prefer clause) dependency configuration variables available to the + // reflect clause but only at the same position. This allows for some more + // advanced configuration techniques, such as, using a feature if enabled + // by someone else but not having any preferences ourselves. + // + optional> prefer_accept_; }; } diff --git a/bpkg/package.cxx b/bpkg/package.cxx index 3a356f8..c02bdf4 100644 --- a/bpkg/package.cxx +++ b/bpkg/package.cxx @@ -53,17 +53,17 @@ namespace bpkg return path; } - // config_package + // package_key // - string config_package:: + string package_key:: string () const { - const std::string& s (db.string); + const std::string& s (db.get ().string); return !s.empty () ? name.string () + ' ' + s : name.string (); } - bool config_package:: - operator< (const config_package& v) const + bool package_key:: + operator< (const package_key& v) const { int r (name.compare (v.name)); return r != 0 ? (r < 0) : (db < v.db); @@ -827,7 +827,7 @@ namespace bpkg bool toolchain_buildtime_dependency (const common_options& o, const dependency_alternatives_ex& das, - const package_name& pkg) + const package_name* pkg) { if (das.buildtime) { @@ -839,10 +839,10 @@ namespace bpkg if (dn == "build2") { - if (d.constraint && !satisfy_build2 (o, d)) + if (pkg != nullptr && d.constraint && !satisfy_build2 (o, d)) { fail << "unable to satisfy constraint (" << d << ") for " - << "package " << pkg << + << "package " << *pkg << info << "available build2 version is " << build2_version; } @@ -850,10 +850,10 @@ namespace bpkg } else if (dn == "bpkg") { - if (d.constraint && !satisfy_bpkg (o, d)) + if (pkg != nullptr && d.constraint && !satisfy_bpkg (o, d)) { fail << "unable to satisfy constraint (" << d << ") for " - << "package " << pkg << + << "package " << *pkg << info << "available bpkg version is " << bpkg_version; } @@ -865,4 +865,18 @@ namespace bpkg return false; } + + bool + has_dependencies (const common_options& o, + const dependencies& deps, + const package_name* pkg) + { + for (const auto& das: deps) + { + if (!toolchain_buildtime_dependency (o, das, pkg)) + return true; + } + + return false; + } } diff --git a/bpkg/package.hxx b/bpkg/package.hxx index 7dc8e11..dc5031c 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -27,7 +27,7 @@ // #define DB_SCHEMA_VERSION_BASE 7 -#pragma db model version(DB_SCHEMA_VERSION_BASE, 18, closed) +#pragma db model version(DB_SCHEMA_VERSION_BASE, 19, closed) namespace bpkg { @@ -580,17 +580,27 @@ namespace bpkg make_move_iterator (das.end ())); } - // If this is a toolchain build-time dependency, then verify its constraint - // returning true if it is satisfied and failing otherwise. Return false for - // a regular dependency. Note that the package argument is used for - // diagnostics only. + // Return true if this is a toolchain build-time dependency. If the package + // argument is specified and this is a toolchain build-time dependency then + // also verify its constraint and fail if it is unsatisfied. Note that the + // package argument is used for diagnostics only. // class common_options; bool toolchain_buildtime_dependency (const common_options&, const dependency_alternatives_ex&, - const package_name&); + const package_name*); + + // Return true if any dependency other than toolchain build-time + // dependencies is specified. Optionally, verify toolchain build-time + // dependencies specifying the package argument which will be used for + // diagnostics only. + // + bool + has_dependencies (const common_options&, + const dependencies&, + const package_name* = nullptr); // Return true if some clause that is a buildfile fragment is specified for // any of the dependencies. @@ -1022,21 +1032,63 @@ namespace bpkg } // A map of "effective" prerequisites (i.e., pointers to other selected - // packages) to optional version constraint. Note that because it is a - // single constraint, we don't support multiple dependencies on the same - // package (e.g., two ranges of versions). See pkg_configure(). + // packages) to optional version constraint (plus some other info). Note + // that because it is a single constraint, we don't support multiple + // dependencies on the same package (e.g., two ranges of versions). See + // pkg_configure(). // // Note also that the pointer can refer to a selected package in another // database. // class selected_package; + #pragma db value + struct prerequisite_info + { + // The "tightest" version constraint among all dependencies resolved to + // this prerequisite. + // + optional constraint; + + // Position of the first dependency alternative with a configuration + // clause, if any. + // + // Specifically, if there is such an alternative then this is a pair of + // 1-based indexes of the respective depends value (first) and the + // dependency alternative (second) in the dependent's manifest. Otherwise, + // this is a pair of zeros. + // + // For example, for the following dependent the position for libfoo/1.2.0 + // prerequisite will be {2,2}: + // + // libbar: depends: libfoo >= 1.1.0 + // depends: libfox | libfoo >= 1.2.0 {require {...}} + // + pair config_position; + + // Database mapping. + // + #pragma db member(constraint) column("") + + #pragma db member(config_position) transient + + #pragma db member(config_dependency_index) \ + virtual(size_t) \ + access(config_position.first) \ + default(0) + + #pragma db member(config_alternative_index) \ + virtual(size_t) \ + access(config_position.second) \ + default(0) + }; + // Note that the keys for this map need to be created with the database // passed to their constructor, which is required for persisting them (see // _selected_package_ref() implementation for details). // using package_prerequisites = std::map, - optional, + prerequisite_info, compare_lazy_ptr>; // Database mapping for lazy_shared_ptr to configuration @@ -1476,6 +1528,21 @@ namespace bpkg #pragma db column("pp.package") package_name name; + #pragma db transient + pair config_position; + + #pragma db member(config_dependency_index) \ + column("pp.config_dependency_index") \ + virtual(size_t) \ + access(config_position.first) \ + default(0) + + #pragma db member(config_alternative_index) \ + column("pp.config_alternative_index") \ + virtual(size_t) \ + access(config_position.second) \ + default(0) + #pragma db column("pp.") optional constraint; }; @@ -1501,34 +1568,50 @@ namespace bpkg // not detached during such map lifetimes. Considers both package name and // database for objects comparison. // - struct config_package + struct package_key { - database& db; - package_name name; + reference_wrapper db; + package_name name; - config_package (database& d, package_name n): db (d), name (move (n)) {} + package_key (database& d, package_name n): db (d), name (move (n)) {} // Create a pseudo-package (command line as a dependent, etc). // - config_package (database& d, string n) + package_key (database& d, string n) : db (d), name (n.empty () ? package_name () : package_name (move (n))) {} bool - operator== (const config_package& v) const + operator== (const package_key& v) const { // See operator==(database, database). // - return name == v.name && &db == &v.db; + return name == v.name && &db.get () == &v.db.get (); } bool - operator< (const config_package&) const; + operator!= (const package_key& v) const + { + return !(*this == v); + } + bool + operator< (const package_key&) const; + + // Return the package string representation in the form: + // + // [ ] + // std::string string () const; }; + inline ostream& + operator<< (ostream& os, const package_key& p) + { + return os << p.string (); + } + // Return a count of repositories that contain this repository fragment. // #pragma db view table("main.repository_fragments") diff --git a/bpkg/package.xml b/bpkg/package.xml index 904d0f6..8959529 100644 --- a/bpkg/package.xml +++ b/bpkg/package.xml @@ -1,4 +1,11 @@ + + + + + + + diff --git a/bpkg/pkg-build.cli b/bpkg/pkg-build.cli index b1415fe..493ffbe 100644 --- a/bpkg/pkg-build.cli +++ b/bpkg/pkg-build.cli @@ -379,11 +379,12 @@ namespace bpkg { "", - "Hash the names and versions of all the packages that would be built. - If the resulting checksum matches the specified, then exit without - building anything (potentially with a special error code specified - with the \cb{--noop-exit} option). Otherwise, proceed to build as - normal. In both cases, print the resulting checksum to \cb{stdout}." + "Hash the names, versions, and configurations of all the packages that + would be built. If the resulting checksum matches the specified, then + exit without building anything (potentially with a special error code + specified with the \cb{--noop-exit} option). Otherwise, proceed to + build as normal. In both cases, print the resulting checksum to + \cb{stdout}." } uint16_t --no-private-config diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 6d90ff9..331ab9d 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -6,10 +6,13 @@ #include #include #include -#include // strlen() -#include // cout +#include // numeric_limits +#include // strlen() +#include +#include // cout +#include // ref() +#include -#include #include #include @@ -34,6 +37,7 @@ #include #include #include +#include using namespace std; using namespace butl; @@ -434,6 +438,55 @@ namespace bpkg return make_pair (move (ap), move (rf)); } + // Try to find an available package corresponding to the specified selected + // package and, if not found, return a transient one. + // + static shared_ptr + find_available (const common_options& options, + database& db, + const shared_ptr& sp) + { + available_package_id pid (sp->name, sp->version); + for (database& ddb: dependent_repo_configs (db)) + { + shared_ptr ap (ddb.find (pid)); + + if (ap != nullptr && !ap->stub ()) + return ap; + } + + return make_available (options, db, sp); + } + + // As above but also pair the available package with the repository fragment + // the available package comes from. Note that the package locations list is + // left empty and that the returned repository fragment could be NULL if the + // package is an orphan. + // + static pair, + lazy_shared_ptr> + find_available_fragment (const common_options& options, + database& db, + const shared_ptr& sp) + { + available_package_id pid (sp->name, sp->version); + for (database& ddb: dependent_repo_configs (db)) + { + shared_ptr ap (ddb.find (pid)); + + if (ap != nullptr && !ap->stub ()) + { + if (shared_ptr f = ddb.find ( + sp->repository_fragment.canonical_name ())) + return make_pair (ap, + lazy_shared_ptr (ddb, + move (f))); + } + } + + return make_pair (find_available (options, db, sp), nullptr); + } + // Return true if the version constraint represents the wildcard version. // static inline bool @@ -453,7 +506,7 @@ namespace bpkg // prerequisites being replaced ("old"). The unamended prerequisites have no // entries. // - using repointed_dependents = map>; + using repointed_dependents = map>; // List of the private configuration paths, relative to the containing // configuration directories (.bpkg/host/, etc), together with the @@ -480,10 +533,12 @@ namespace bpkg // any version constraints. Specifically, during this step, we may // "upgrade" or "downgrade" a package that is already in a map as a // result of another package depending on it and, for example, requiring - // a different version. One notable side-effect of this process is that - // we may end up with a lot more packages in the map (but not in the list) - // than we will have on the list. This is because some of the prerequisites - // of "upgraded" or "downgraded" packages may no longer need to be built. + // a different version. If that happens, we make sure that the replaced + // package version doesn't apply constraints and/or configuration to its + // own dependencies anymore and also that its non-shared dependencies are + // gone from the map, recursively (see replaced_versions for details). + // One notable side-effect of this process is that all the packages in the + // map end up in the list. // // Note that we don't try to do exhaustive constraint satisfaction (i.e., // there is no backtracking). Specifically, if we have two candidate @@ -557,10 +612,15 @@ namespace bpkg // // Initially nullopt. Can be filled partially if the package prerequisite // builds collection is postponed for any reason (see postponed_packages - // for possible reasons). + // and postponed_configurations for possible reasons). // optional dependencies; + // Indexes of the selected dependency alternatives stored in the above + // dependencies member. + // + optional> alternatives; + // If we end up collecting the prerequisite builds for this package, then // this member stores the skeleton of the package being built. // @@ -573,15 +633,36 @@ namespace bpkg // If the package prerequisite builds collection is postponed, then this // member stores the references to the enabled alternatives (in available - // package) of a dependency being the cause of the postponement. This, in - // particular, allows not to re-evaluate conditions multiple times on the - // re-collection attempts. + // package) of a dependency being the cause of the postponement together + // with their original indexes in the respective dependency alternatives + // list. This, in particular, allows us not to re-evaluate conditions + // multiple times on the re-collection attempts. // // Note: it shouldn't be very common for a dependency to contain more than // two true alternatives. // - optional, 2>> - postponed_dependency_alternatives; + using dependency_alternatives_refs = + small_vector, + size_t>, + 2>; + + optional postponed_dependency_alternatives; + + // True if the recursive collection of the package has been started or + // performed. + // + // Used by the dependency configuration negotiation machinery which makes + // sure that its configuration is negotiated between dependents before its + // recursive collection is started (see postponed_configurations for + // details). + // + // Note that the dependencies member cannot be used for that purpose since + // it is not always created (think of a system dependency or an existing + // dependency that doesn't need its prerequisites re-collection). In a + // sense the recursive collection flag is a barrier for the dependency + // configuration negotiation. + // + bool recursive_collection; // Hold flags. Note that we only "increase" the hold_package value that is // already in the selected package. @@ -645,7 +726,7 @@ namespace bpkg // selection and can be present regardless of the required_by_dependents // flag value. // - set required_by; + set required_by; // If this flags is true, then required_by contains dependents. // @@ -659,8 +740,38 @@ namespace bpkg bool user_selection () const { - return required_by.find (config_package {db.get ().main_database (), - ""}) != required_by.end (); + return required_by.find (package_key {db.get ().main_database (), + ""}) != required_by.end (); + } + + // Return true if the configured package needs to be recollected + // recursively. + // + // This is required if it is being built as a source package and needs to + // be up/down-graded and/or reconfigured and has some buildfile clauses, + // it is a repointed dependent, or it is already in the process of being + // collected. + // + bool + recollect_recursively (const repointed_dependents& rpt_depts) const + { + assert (action && + *action == build_package::build && + available != nullptr && + selected != nullptr && + selected->state == package_state::configured && + selected->substate != package_substate::system); + + // Note that if the skeleton is present then the package is either being + // already collected or its configuration has been negotiated between + // the dependents. + // + return !system && + (dependencies || + selected->version != available_version () || + ((!config_vars.empty () || skeleton) && + has_buildfile_clause (available->dependencies)) || + rpt_depts.find (package_key (db, name ())) != rpt_depts.end ()); } // State flags. @@ -882,7 +993,8 @@ namespace bpkg // dependency alternative choice. // assert (!skeleton || - (p.config_vars == config_vars && p.disfigure == disfigure)); + ((p.config_vars.empty () || p.config_vars == config_vars) && + p.disfigure == disfigure)); if (p.keep_out) keep_out = p.keep_out; @@ -945,6 +1057,57 @@ namespace bpkg // For other cases ("weak system") we don't want to copy system over in // order not prevent, for example, system to non-system upgrade. } + + // Initialize the skeleton of a being built package. + // + package_skeleton& + init_skeleton (const common_options& options, + const shared_ptr& override = nullptr) + { + shared_ptr ap (override != nullptr + ? override + : available); + + assert (!skeleton && ap != nullptr); + + package_key pk (db, ap->id.name); + + if (system) + { + // Keep the available package if its version is "close enough" to the + // system package version. For now we will require the exact match + // but in the future we could relax this (e.g., allow the user to + // specify something like libfoo/^1.2.0 or some such). + // + const version* v (!ap->stub () ? ap->system_version (db) : nullptr); + + if (v == nullptr || *v != ap->version) + ap = nullptr; + } + + optional src_root, out_root; + + if (ap != nullptr) + { + src_root = external_dir (); + out_root = (src_root && !disfigure + ? dir_path (db.get ().config) /= name ().string () + : optional ()); + } + + skeleton = package_skeleton ( + options, + move (pk), + system, + move (ap), + config_vars, // @@ Maybe make optional and move? + disfigure, + (selected != nullptr ? &selected->config_variables : nullptr), + move (src_root), + move (out_root)); + + return *skeleton; + } }; using build_package_list = list>; @@ -954,1892 +1117,5348 @@ namespace bpkg using add_priv_cfg_function = void (database&, dir_path&&); - struct build_packages: build_package_list + // Base for exception types that indicate an inability to collect a package + // build because it was collected prematurely (version needs to be replaced, + // configuration requires further negotiation, etc). + // + struct scratch_collection + { + // Only used for tracing. + // + const char* description; + const package_key* package = nullptr; // Could be NULL. + + explicit + scratch_collection (const char* d): description (d) {} + }; + + // Map of packages which need to be re-collected with the different version + // and/or system flag or dropped. + // + // Note that the initial package version may be adjusted to satisfy + // constraints of dependents discovered during the packages collection. It + // may also be dropped if this is a dependency which turns out to be unused. + // However, it may not always be possible to perform such an adjustment + // in-place since the intermediate package version could already apply some + // constraints and/or configuration to its own dependencies. Thus, we may + // need to note the desired package version information and re-collect from + // scratch. + // + // Also note that during re-collection such a desired version may turn out + // to not be a final version and the adjustment/re-collection can repeat. + // + // And yet, it doesn't seem plausible to ever create a replacement for the + // drop: replacing one drop with another is meaningless (all drops are the + // same) and replacing the package drop with a package version build can + // always been handled in-place. + // + // On the first glance, the map entries which have not been used for + // replacement during the package collection (bogus entries) are harmless + // and can be ignored. However, the dependency configuration negotiation + // machinery refers to this map and skips existing dependents with + // configuration clause which belong to it (see query_existing_dependents() + // for details). Thus, if after collection of packages some bogus entries + // are present in the map, then it means that we could have erroneously + // skipped some existing dependents because of them and so need to erase + // these entries and re-collect. + // + struct replaced_version { - // Packages with postponed prerequisites collection, for one of the - // following reasons: + // Desired package version, repository fragment, and system flag. // - // - Postponed due to the inability to find a version satisfying the pre- - // entered constraint from repositories available to this package. The - // idea is that this constraint could still be satisfied from a - // repository fragment of some other package (that we haven't processed - // yet) that also depends on this prerequisite. + // Both are NULL for the replacement with the drop. // - // - Postponed due to the inability to choose between two dependency - // alternatives, both having dependency packages which are not yet - // selected in the configuration nor being built. The idea is that this - // ambiguity could still be resolved after some of those dependency - // packages get built via some other dependents. + shared_ptr available; + lazy_shared_ptr repository_fragment; + bool system; // Meaningless for the drop. + + // True if the entry has been inserted or used for the replacement during + // the current (re-)collection iteration. Used to keep track of "bogus" + // (no longer relevant) entries. // - using postponed_packages = set; + bool replaced; - // Pre-enter a build_package without an action. No entry for this package - // may already exists. + // Create replacement with the different version. + // + replaced_version (shared_ptr a, + lazy_shared_ptr f, + bool s) + : available (move (a)), + repository_fragment (move (f)), + system (s), + replaced (true) {} + + // Create replacement with the drop. + // + replaced_version (): system (false), replaced (true) {} + }; + + class replaced_versions: public map + { + public: + // Erase the bogus replacements and, if any, throw cancel_replacement, if + // requested. // + struct cancel_replacement: scratch_collection + { + cancel_replacement () + : scratch_collection ("bogus version replacement cancellation") {} + }; + void - enter (package_name name, build_package pkg) + cancel_bogus (tracer& trace, bool scratch) { - assert (!pkg.action); + bool bogus (false); + for (auto i (begin ()); i != end (); ) + { + const replaced_version& v (i->second); - database& db (pkg.db); // Save before the move() call. - auto p (map_.emplace (config_package {db, move (name)}, - data_type {end (), move (pkg)})); + if (!v.replaced) + { + bogus = true; - assert (p.second); + l5 ([&]{trace << "erase bogus version replacement " + << i->first;}); + + i = erase (i); + } + else + ++i; + } + + if (bogus && scratch) + { + l5 ([&]{trace << "bogus version replacement erased, throwing";}); + throw cancel_replacement (); + } } + }; + + // List of dependency groups whose recursive processing should be postponed + // due to dependents with configuration clauses, together with these + // dependents (we will call them package clusters). + // + // The idea is that configuration for the dependencies in the cluster needs + // to be negotiated between the dependents in the cluster. Note that at any + // given time during collection a dependency can only belong to a single + // cluster. For example, the following dependent/dependencies with + // configuration clauses: + // + // foo: depends: libfoo + // bar: depends: libfoo + // depends: libbar + // baz: depends: libbaz + // + // End up in the following clusters (see string() below for the cluster + // representation): + // + // {foo bar | libfoo->{foo/1,1 bar/1,1}} + // {bar | libbar->{bar/2,1}} + // {baz | libbaz->{baz/1,1}} + // + // Or, another example: + // + // foo: depends: libfoo + // bar: depends: libfoo libbar + // baz: depends: libbaz + // + // {foo bar | libfoo->{foo/1,1 bar/1,1} libbar->{bar/1,1}} + // {baz | libbaz->{baz/1,1}} + // + // Note that a dependent can belong to any given non-negotiated cluster with + // only one `depends` position. However, if some dependency configuration is + // up-negotiated for a dependent, then multiple `depends` positions will + // correspond to this dependent in the same cluster. Naturally, such + // clusters are always (being) negotiated. + // + // Note that adding new dependent/dependencies to the postponed + // configurations can result in merging some of the existing clusters if the + // dependencies being added intersect with multiple clusters. For example, + // adding: + // + // fox: depends: libbar libbaz + // + // to the clusters in the second example will merge them into a single + // cluster: + // + // {foo bar baz fox | libfoo->{foo/1,1 bar/1,1} libbar->{bar/1,1 fox/1,1} + // libbaz->{baz/1,1 fox/1,1}} + // + // Also note that we keep track of packages which turn out to be + // dependencies of existing (configured) dependents with configuration + // clauses. The recursive processing of such packages should be postponed + // until negotiation between all the existing and new dependents which may + // or may not be present. + // + class postponed_configuration; - // Return true if a package is already in the map. + static ostream& + operator<< (ostream&, const postponed_configuration&); + + class postponed_configuration + { + public: + // The id of the cluster plus the ids of all the clusters that have been + // merged into it. // - bool - entered (database& db, const package_name& name) + size_t id; + small_vector merged_ids; + + using packages = small_vector; + + class dependency: public packages { - return map_.find (db, name) != map_.end (); - } + public: + pair position; // depends + alternative - // Collect the package being built. Return its pointer if this package - // version was, in fact, added to the map and NULL if it was already there - // or the existing version was preferred. So can be used as bool. + dependency (const pair& pos, packages deps) + : packages (move (deps)), position (pos) {} + }; + + class dependent_info + { + public: + bool existing; + small_vector dependencies; + + dependency* + find_dependency (pair pos) + { + auto i (find_if (dependencies.begin (), + dependencies.end (), + [&pos] (const dependency& d) + { + return d.position == pos; + })); + return i != dependencies.end () ? &*i : nullptr; + } + + void + add (dependency&& dep) + { + if (dependency* d = find_dependency (dep.position)) + { + // Feels like we can accumulate dependencies into an existing + // position only for an existing dependent. + // + assert (existing); + + for (package_key& p: dep) + { + // Add the dependency unless it's already there. + // + if (find (d->begin (), d->end (), p) == d->end ()) + d->push_back (move (p)); + } + } + else + dependencies.push_back (move (dep)); + } + }; + + using dependents_map = map; + + dependents_map dependents; + packages dependencies; + + // Dependency configuration. // - // Also, in the recursive mode (dep_chain is not NULL): + // Note that this container may not yet contain some entries that are + // already in the dependencies member above. And it may already contain + // entries that are not yet in dependencies due to the retry_configuration + // logic. // - // - Use the custom search function to find the package dependency - // databases. + package_configurations dependency_configurations; + + // Shadow clusters. // - // - For the repointed dependents collect the prerequisite replacements - // rather than prerequisites being replaced. + // See the collect lambda in collect_build_prerequisites() for details. // - // - Add paths of the created private configurations, together with the - // containing configuration databases, into the specified list (see - // private_configs for details). + using positions = small_vector, 1>; + using shadow_dependents_map = map; + + shadow_dependents_map shadow_cluster; + + // Absent -- not negotiated yet, false -- being negotiated, true -- has + // been negotiated. // - // Note that postponed_* and dep_chain arguments must all be either - // specified or not. + optional negotiated; + + // The depth of the negotiating recursion (see collect_build_postponed() + // for details). // - build_package* - collect_build (const pkg_build_options& options, - build_package pkg, - const function& fdb, - const repointed_dependents& rpt_depts, - const function& apc, - postponed_packages* postponed_repo = nullptr, - postponed_packages* postponed_alts = nullptr, - build_package_refs* dep_chain = nullptr) + size_t depth = 0; + + // Add dependencies of a new dependent. + // + postponed_configuration (size_t i, + package_key&& dependent, + bool existing, + pair position, + packages&& deps) + : id (i) { - using std::swap; // ...and not list::swap(). + add (move (dependent), existing, position, move (deps)); + } - tracer trace ("collect_build"); + // Add dependency of an existing dependent. + // + postponed_configuration (size_t i, + package_key&& dependent, + pair position, + package_key&& dep) + : id (i) + { + add (move (dependent), + true /* existing */, + position, + packages ({move (dep)})); + } - // Must all be either specified or not. - // - assert ((dep_chain == nullptr) == (postponed_repo == nullptr)); - assert ((postponed_repo == nullptr) == (postponed_alts == nullptr)); + // Add dependencies of a dependent. + // + // Note: adds the specified dependencies to the end of the configuration + // dependencies list suppressing duplicates. + // + void + add (package_key&& dependent, + bool existing, + pair position, + packages&& deps) + { + assert (position.first != 0 && position.second != 0); - // Only builds are allowed here. - // - assert (pkg.action && *pkg.action == build_package::build && - pkg.available != nullptr); + add_dependencies (deps); // Don't move from since will be used later. - auto i (map_.find (pkg.db, pkg.available->id.name)); + auto i (dependents.find (dependent)); - // If we already have an entry for this package name, then we - // have to pick one over the other. - // - // If the existing entry is a pre-entered or is non-build one, then we - // merge it into the new build entry. Otherwise (both are builds), we - // pick one and merge the other into it. - // - if (i != map_.end ()) + if (i != dependents.end ()) { - build_package& bp (i->second.package); + dependent_info& ddi (i->second); - // Can't think of the scenario when this happens. We would start - // collecting from scratch (see below). + ddi.add (dependency (position, move (deps))); + + // Conceptually, on the first glance, we can only move from existing + // to non-existing (e.g., due to a upgrade/downgrade later) and that + // case is handled via the version replacement rollback. However, + // after re-evaluation the existing dependent is handled similar to + // the new dependent and we can potentially up-negotiate the + // dependency configuration for it. // - assert (!bp.action || *bp.action != build_package::drop); + assert (ddi.existing || !existing); + } + else + { + small_vector ds ({dependency (position, move (deps))}); + + dependents.emplace (move (dependent), + dependent_info {existing, move (ds)}); + } + } + + // Return true if any of the configuration's dependents depend on the + // specified package. + // + bool + contains_dependency (const package_key& d) const + { + return find (dependencies.begin (), dependencies.end (), d) != + dependencies.end (); + } + + // Return true if this configuration contains any of the specified + // dependencies. + // + bool + contains_dependency (const packages& ds) const + { + for (const package_key& d: ds) + { + if (contains_dependency (d)) + return true; + } + + return false; + } - if (!bp.action || *bp.action != build_package::build) // Non-build. + // Return true if this and specified configurations contain any common + // dependencies. + // + bool + contains_dependency (const postponed_configuration& c) const + { + for (const auto& d: c.dependencies) + { + if (contains_dependency (d)) + return true; + } + + return false; + } + + // If the configuration contains the specified existing dependent, then + // return the earliest dependency position. Otherwise return NULL. + // + const pair* + existing_dependent_position (const package_key& p) const + { + const pair* r (nullptr); + + auto i (dependents.find (p)); + if (i != dependents.end () && i->second.existing) + { + for (const dependency& d: i->second.dependencies) { - pkg.merge (move (bp)); - bp = move (pkg); + if (r == nullptr || d.position < *r) + r = &d.position; } - else // Build. + + assert (r != nullptr); + } + + return r; + } + + // Notes: + // + // - Adds dependencies of the being merged from configuration to the end + // of the current configuration dependencies list suppressing + // duplicates. + // + // - Doesn't change the negotiate member of this configuration. + // + void + merge (postponed_configuration&& c) + { + assert (c.id != id); // Can't merge to itself. + + merged_ids.push_back (c.id); + + // Merge dependents. + // + for (auto& d: c.dependents) + { + auto i (dependents.find (d.first)); + + if (i != dependents.end ()) { - // At the end we want p1 to point to the object that we keep - // and p2 to the object that we merge from. - // - build_package* p1 (&bp); - build_package* p2 (&pkg); + dependent_info& ddi (i->second); // Destination dependent info. + dependent_info& sdi (d.second); // Source dependent info. - // Pick with the following preference order: user selection over - // implicit one, source package over a system one, newer version - // over an older one. So get the preferred into p1 and the other - // into p2. + for (dependency& sd: sdi.dependencies) + ddi.add (move (sd)); + + // As in add() above. // - { - int us (p1->user_selection () - p2->user_selection ()); - int sf (p1->system - p2->system); + assert (ddi.existing || !sdi.existing); + } + else + dependents.emplace (d.first, move (d.second)); + } - if (us < 0 || - (us == 0 && sf > 0) || - (us == 0 && - sf == 0 && - p2->available_version () > p1->available_version ())) - swap (p1, p2); - } - - // If the versions differ, pick the satisfactory one and if both are - // satisfactory, then keep the preferred. - // - if (p1->available_version () != p2->available_version ()) - { - using constraint_type = build_package::constraint_type; - - // See if pv's version satisfies pc's constraints. Return the - // pointer to the unsatisfied constraint or NULL if all are - // satisfied. - // - auto test = [] (build_package* pv, - build_package* pc) -> const constraint_type* - { - for (const constraint_type& c: pc->constraints) - { - if (!satisfies (pv->available_version (), c.value)) - return &c; - } - - return nullptr; - }; - - // First see if p1 satisfies p2's constraints. - // - if (auto c2 = test (p1, p2)) - { - // If not, try the other way around. - // - if (auto c1 = test (p2, p1)) - { - const package_name& n (i->first.name); - const string& d1 (c1->dependent); - const string& d2 (c2->dependent); - - fail << "unable to satisfy constraints on package " << n << - info << d1 << c1->db << " depends on (" << n << " " - << c1->value << ")" << - info << d2 << c2->db << " depends on (" << n << " " - << c2->value << ")" << - info << "available " << p1->available_name_version () << - info << "available " << p2->available_name_version () << - info << "explicitly specify " << n << " version to manually " - << "satisfy both constraints"; - } - else - swap (p1, p2); - } - - l4 ([&]{trace << "pick " << p1->available_name_version_db () - << " over " << p2->available_name_version_db ();}); - } - - // See if we are replacing the object. If not, then we don't - // need to collect its prerequisites since that should have - // already been done. Remember, p1 points to the object we - // want to keep. - // - bool replace (p1 != &i->second.package); - - if (replace) - { - swap (*p1, *p2); - swap (p1, p2); // Setup for merge below. - } - - p1->merge (move (*p2)); + // Merge dependencies. + // + add_dependencies (move (c.dependencies)); - if (!replace) - return nullptr; - } + // Pick the depth of the outermost negotiated configuration (minimum + // non-zero depth) between the two. + // + if (depth != 0) + { + if (c.depth != 0 && depth > c.depth) + depth = c.depth; } else + depth = c.depth; + } + + void + set_shadow_cluster (postponed_configuration&& c) + { + shadow_cluster.clear (); + + for (auto& dt: c.dependents) { - // This is the first time we are adding this package name to the map. - // - l4 ([&]{trace << "add " << pkg.available_name_version_db ();}); + positions ps; + for (auto& d: dt.second.dependencies) + ps.push_back (d.position); - // Note: copy; see emplace() below. - // - database& db (pkg.db); // Save before the move() call. - package_name n (pkg.available->id.name); - i = map_.emplace (config_package {db, move (n)}, - data_type {end (), move (pkg)}).first; + shadow_cluster.emplace (dt.first, move (ps)); } + } - build_package& p (i->second.package); - - // Recursively collect build prerequisites, if requested. - // - // Note that detecting dependency cycles during the satisfaction phase - // would be premature since they may not be present in the final package - // list. Instead we check for them during the ordering phase. - // - // The question, of course, is whether we can still end up with an - // infinite recursion here? Note that for an existing map entry we only - // recurse after the entry replacement. The infinite recursion would - // mean that we may replace a package in the map with the same version - // multiple times: - // - // ... p1 -> p2 -> ... p1 - // - // Every replacement increases the entry version and/or tightens the - // constraints the next replacement will need to satisfy. It feels - // impossible that a package version can "return" into the map being - // replaced once. So let's wait until some real use case proves this - // reasoning wrong. - // - if (dep_chain != nullptr) - collect_build_prerequisites (options, - p, - fdb, - rpt_depts, - apc, - postponed_repo, - postponed_alts, - 0 /* max_alt_index */, - *dep_chain); + bool + contains_in_shadow_cluster (package_key dependent, + pair pos) const + { + auto i (shadow_cluster.find (dependent)); - return &p; + if (i != shadow_cluster.end ()) + { + const positions& ps (i->second); + return find (ps.begin (), ps.end (), pos) != ps.end (); + } + else + return false; } - // Collect prerequisites of the package being built recursively. + // Return the postponed configuration string representation in the form: // - // But first "prune" this process if the package we build is a system one - // or is already configured, since that would mean all its prerequisites - // are configured as well. Note that this is not merely an optimization: - // the package could be an orphan in which case the below logic will fail - // (no repository fragment in which to search for prerequisites). By - // skipping the prerequisite check we are able to gracefully handle - // configured orphans. + // {[ ]* | [ ]*}['!'|'?'] // - // There are, however, some cases when we still need to re-collect - // prerequisites of a configured package: + // = ['^'] + // = ->{/[ /]*} // - // - For the repointed dependent we still need to collect its prerequisite - // replacements to make sure its dependency constraints are satisfied. + // The potential trailing '!' or '?' of the configuration representation + // indicates that the configuration is negotiated or is being negotiated, + // respectively. // - // - If configuration variables are specified for the dependent which has - // any buildfile clauses in the dependencies, then we need to - // re-evaluate them. This can result in a different set of dependencies - // required by this dependent (due to conditional dependencies, etc) - // and, potentially, for its reconfigured existing prerequisites, - // recursively. + // '^' character that may follow a dependent indicates that this is an + // existing dependent. // - // Note that for these cases, as it was said above, we can potentially - // fail if the dependent is an orphan, but this is exactly what we need to - // do in that case, since we won't be able to re-collect its dependencies. + // = ',' // - // Only a single true dependency alternative can be selected per function - // call. Such an alternative can only be selected if its index in the - // postponed alternatives list is less than the specified maximum (used by - // the heuristics that determines in which order to process packages with - // alternatives; if 0 is passed, then no true alternative will be - // selected). + // and are the 1-based serial numbers + // of the respective depends value and the dependency alternative in the + // dependent's manifest. // - // The idea here is to postpone the true alternatives selection till the - // end of the packages collection and then try to optimize the overall - // resulting selection (over all the dependents) by selecting alternatives - // with the lower indexes first (see collect_build_postponed() for - // details). + // See package_key for details on . // - void - collect_build_prerequisites (const pkg_build_options& options, - build_package& pkg, - const function& fdb, - const repointed_dependents& rpt_depts, - const function& apc, - postponed_packages* postponed_repo, - postponed_packages* postponed_alts, - size_t max_alt_index, - build_package_refs& dep_chain) + // For example: + // + // {foo^ bar | libfoo->{foo/2,3 bar/1,1} libbar->{bar/1,1}}! + // + std::string + string () const { - tracer trace ("collect_build_prerequisites"); + std::string r; - assert (pkg.action && *pkg.action == build_package::build); + for (const auto& d: dependents) + { + r += r.empty () ? '{' : ' '; + r += d.first.string (); - if (pkg.system) - return; + if (d.second.existing) + r += '^'; + } - const shared_ptr& ap (pkg.available); - const shared_ptr& sp (pkg.selected); - database& pdb (pkg.db); + if (r.empty ()) + r += '{'; - // True if this is an up/down-grade. - // - bool ud (false); + r += " |"; - // If this is a repointed dependent, then it points to its prerequisite - // replacements flag map (see repointed_dependents for details). - // - const map* rpt_prereq_flags (nullptr); + for (const package_key& d: dependencies) + { + r += ' '; + r += d.string (); + r += "->{"; - assert (ap != nullptr); + bool first (true); + for (const auto& dt: dependents) + { + for (const dependency& dp: dt.second.dependencies) + { + if (find (dp.begin (), dp.end (), d) != dp.end ()) + { + if (!first) + r += ' '; + else + first = false; - // Bail out if this is a configured non-system package and no - // up/down-grade nor collecting prerequisite replacements are required. - // - // NOTE: remember to update the check condition in - // collect_order_dependents() if changing anything here. - // - bool src_conf (sp != nullptr && - sp->state == package_state::configured && - sp->substate != package_substate::system); + r += dt.first.string (); + r += '/'; + r += to_string (dp.position.first); + r += ','; + r += to_string (dp.position.second); + } + } + } - if (src_conf) - { - ud = sp->version != pkg.available_version (); + r += '}'; + } - repointed_dependents::const_iterator i ( - rpt_depts.find (config_package {pdb, sp->name})); + r += '}'; - if (i != rpt_depts.end ()) - rpt_prereq_flags = &i->second; + if (negotiated) + r += *negotiated ? '!' : '?'; - if (!ud && - rpt_prereq_flags == nullptr && - (pkg.config_vars.empty () || - !has_buildfile_clause (ap->dependencies))) - return; + return r; + } + + private: + // Add the specified packages to the end of the dependencies list + // suppressing duplicates. + // + void + add_dependencies (packages&& deps) + { + for (auto& d: deps) + { + if (find (dependencies.begin (), dependencies.end (), d) == + dependencies.end ()) + dependencies.push_back (move (d)); } + } - // Iterate over dependencies, trying to unambiguously select a - // satisfactory dependency alternative for each of them. Fail or - // postpone the collection if unable to do so. + void + add_dependencies (const packages& deps) + { + for (const auto& d: deps) + { + if (find (dependencies.begin (), dependencies.end (), d) == + dependencies.end ()) + dependencies.push_back (d); + } + } + }; + + static ostream& + operator<< (ostream& os, const postponed_configuration& c) + { + return os << c.string (); + } + + // Note that we could be adding new/merging existing entries while + // processing an entry. Thus we use a list. + // + class postponed_configurations: public forward_list + { + public: + // Return the configuration the dependent is added to (after all the + // potential configuration merges, etc). + // + // Also return in second absent if the merge happened due to the shadow + // cluster logic (in which case the cluster was/is being negotiated), + // false if any non-negotiated or being negotiated clusters has been + // merged in, and true otherwise. + // + // If some configurations needs to be merged and this involves the (being) + // negotiated configurations, then merge into the outermost-depth + // negotiated configuration (with minimum non-zero depth). + // + pair> + add (package_key dependent, + bool existing, + pair position, + postponed_configuration::packages dependencies) + { + tracer trace ("postponed_configurations::add"); + + assert (!dependencies.empty ()); + + // The plan is to first go through the existing clusters and check if + // any of them contain this dependent/dependencies in their shadow + // clusters. If such a cluster is found, then force-add them to + // it. Otherwise, if any dependency-intersecting clusters are present, + // then add the specified dependent/dependencies to the one with the + // minimum non-zero depth, if any, and to the first one otherwise. + // Otherwise, add the new cluster. Afterwards, merge into the resulting + // cluster other dependency-intersecting clusters. Note that in case of + // shadow, this should normally not happen because such a cluster should + // have been either pre-merged or its dependents should be in the + // cluster. But it feels like it may still happen if things change, in + // which case we will throw again (admittedly a bit fuzzy). // - const dependencies& deps (ap->dependencies); + iterator ri; + bool rb (true); - // Must both be either present or not. + // Note that if a single dependency is added, then it can only belong to + // a single existing cluster and so no clusters merge can happen, unless + // we are force-adding. In the later case we can only merge once for a + // single dependency. + // + // Let's optimize for the common case based on these facts. // - assert (pkg.dependencies.has_value () == pkg.skeleton.has_value ()); + bool single (dependencies.size () == 1); - // Note that the selected alternatives list can be filled partially (see - // build_package::dependencies for details). In this case we continue - // collecting where we stopped previously. + // Merge dependency-intersecting clusters in the specified range into + // the resulting cluster and reset change rb to false if any of the + // merged in clusters is non-negotiated or is being negotiated. // - if (!pkg.dependencies) + // The iterator arguments refer to entries before and after the range + // endpoints, respectively. + // + auto merge = [&trace, &ri, &rb, single, this] (iterator i, + iterator e, + bool shadow_based) { - pkg.dependencies = dependencies (); + postponed_configuration& rc (*ri); - if (size_t n = deps.size ()) - pkg.dependencies->reserve (n); + iterator j (i); - optional src_root (pkg.external_dir ()); + // Merge the intersecting configurations. + // + bool merged (false); + for (++i; i != e; ++i) + { + postponed_configuration& c (*i); - optional out_root ( - src_root && !pkg.disfigure - ? dir_path (pdb.config) /= pkg.name ().string () - : optional ()); + if (c.contains_dependency (rc)) + { + if (!c.negotiated || !*c.negotiated) + rb = false; - pkg.skeleton = package_skeleton (options, - pdb, - *ap, - pkg.config_vars, - move (src_root), - move (out_root)); - } + l5 ([&]{trace << "merge " << c << " into " << rc;}); - dependencies& sdeps (*pkg.dependencies); + assert (!shadow_based || (c.negotiated && *c.negotiated)); - // Check if there is nothing to collect anymore. - // - if (sdeps.size () == deps.size ()) - return; + rc.merge (move (c)); + c.dependencies.clear (); // Mark as merged from (see above). - // Show how we got here if things go wrong. - // - // To suppress printing this information clear the dependency chain - // before throwing an exception. - // - auto g ( - make_exception_guard ( - [&dep_chain] () - { - // Note that we also need to clear the dependency chain, to - // prevent the caller's exception guard from printing it. - // - while (!dep_chain.empty ()) - { - info << "while satisfying " - << dep_chain.back ().get ().available_name_version_db (); - - dep_chain.pop_back (); - } - })); + merged = true; - dep_chain.push_back (pkg); + if (single) + break; + } + } - assert (sdeps.size () < deps.size ()); + // Erase configurations which we have merged from. + // + if (merged) + { + i = j; - package_skeleton& skel (*pkg.skeleton); + for (++i; i != e; ) + { + if (!i->dependencies.empty ()) + { + ++i; + ++j; + } + else + i = erase_after (j); + } + } + }; - for (size_t di (sdeps.size ()); di != deps.size (); ++di) + auto trace_add = [&trace, &dependent, existing, position, &dependencies] + (const postponed_configuration& c, bool shadow) { - const dependency_alternatives_ex& das (deps[di]); + if (verb >= 5) + { + diag_record dr (trace); + dr << "add {" << dependent; - // Add an empty alternatives list into the selected dependency list if - // this is a toolchain build-time dependency. - // - dependency_alternatives_ex sdas (das.buildtime, das.comment); + if (existing) + dr << '^'; - if (toolchain_buildtime_dependency (options, das, pkg.name ())) - { - sdeps.push_back (move (sdas)); - continue; - } + dr << ' ' << position.first << ',' << position.second << ':'; - // Evaluate alternative conditions and filter enabled alternatives. - // Add an empty alternatives list into the selected dependency list if - // there are none. - // - small_vector, 2> edas; + for (const auto& d: dependencies) + dr << ' ' << d; - if (pkg.postponed_dependency_alternatives) - { - edas = move (*pkg.postponed_dependency_alternatives); - pkg.postponed_dependency_alternatives = nullopt; + dr << "} to " << c; + + if (shadow) + dr << " (shadow cluster-based)"; } - else + }; + + // Try to add based on the shadow cluster. + // + { + auto i (begin ()); + for (; i != end (); ++i) { - for (const dependency_alternative& da: das) + postponed_configuration& c (*i); + + if (c.contains_in_shadow_cluster (dependent, position)) { - if (!da.enable || skel.evaluate_enable (*da.enable, di)) - edas.push_back (da); + trace_add (c, true /* shadow */); + + c.add (move (dependent), existing, position, move (dependencies)); + break; } } - if (edas.empty ()) + if (i != end ()) { - sdeps.push_back (move (sdas)); - continue; + // Note that the cluster with a shadow cluster is by definition + // either being negotiated or has been negotiated. Actually, there + // is also a special case when we didn't negotiate the configuration + // yet and are in the process of re-evaluating existing dependents. + // Note though, that in this case we have already got the try/catch + // frame corresponding to the cluster negotiation (see + // collect_build_postponed() for details). + // + assert (i->depth != 0); + + ri = i; + + merge (before_begin (), ri, true /* shadow_based */); + merge (ri, end (), true /* shadow_based */); + + return make_pair (ref (*ri), optional ()); } + } - // Try to pre-collect build information (pre-builds) for the - // dependencies of an alternative. Optionally, issue diagnostics into - // the specified diag record. + // Find the cluster to add the dependent/dependencies to. + // + optional depth; + + auto j (before_begin ()); // Precedes iterator i. + for (auto i (begin ()); i != end (); ++i, ++j) + { + postponed_configuration& c (*i); + + if (c.contains_dependency (dependencies) && + (!depth || (c.depth != 0 && (*depth == 0 || *depth > c.depth)))) + { + ri = i; + depth = c.depth; + } + } + + if (!depth) // No intersecting cluster? + { + // New cluster. Insert after the last element. // - // Note that rather than considering an alternative as unsatisfactory - // (returning no pre-builds) the function can fail in some cases - // (multiple possible configurations for a build-time dependency, - // orphan or broken selected package, etc). The assumption here is - // that the user would prefer to fix a dependency-related issue first - // instead of proceeding with the build which can potentially end up - // with some less preferable dependency alternative. + ri = insert_after (j, + postponed_configuration ( + next_id_++, + move (dependent), + existing, + position, + move (dependencies))); + + l5 ([&]{trace << "create " << *ri;}); + } + else + { + // Add the dependent/dependencies into an existing cluster. // - struct prebuild - { - bpkg::dependency dependency; - reference_wrapper db; - shared_ptr selected; - shared_ptr available; - lazy_shared_ptr repository_fragment; - bool system; - bool specified_dependency; - bool force; + postponed_configuration& c (*ri); - // True if the dependency package is either selected in the - // configuration or is already being built. - // - bool reused; - }; - using prebuilds = small_vector; + trace_add (c, false /* shadow */); - class precollect_result - { - public: - // Nullopt if some dependencies cannot be resolved. - // - optional builds; + c.add (move (dependent), existing, position, move (dependencies)); - // True if dependencies can all be resolved (builds is present) and - // are all reused (see above). - // - bool reused; + // Try to merge other clusters into this cluster. + // + merge (before_begin (), ri, false /* shadow_based */); + merge (ri, end (), false /* shadow_based */); + } - // True if some of the dependencies cannot be resolved (builds is - // nullopt) and the dependent package prerequisites collection needs - // to be postponed due to inability to find a version satisfying the - // pre-entered constraint from repositories available to the - // dependent package. - // - bool repo_postpone; + return make_pair (ref (*ri), optional (rb)); + } - // Create precollect result containing dependency builds. - // - precollect_result (prebuilds&& bs, bool r) - : builds (move (bs)), reused (r), repo_postpone (false) {} + // Add new postponed configuration cluster with a single dependency of an + // existing dependent. + // + // Note that it's the caller's responsibility to make sure that the + // dependency doesn't already belong to any existing cluster. + // + void + add (package_key dependent, + pair position, + package_key dependency) + { + tracer trace ("postponed_configurations::add"); - // Create precollect result without builds (some dependency can't be - // satisfied, etc). - // - explicit - precollect_result (bool p): reused (false), repo_postpone (p) {} - }; - auto precollect = [&options, - &pkg, - &pdb, - ud, - &fdb, - rpt_prereq_flags, - &apc, - postponed_repo, - &dep_chain, - this] - (const dependency_alternative& da, - bool buildtime, - const package_prerequisites* prereqs, - diag_record* dr = nullptr) - -> precollect_result - { - prebuilds r; - bool reused (true); + // Add the new cluster to the end of the list which we can only find by + // traversing the list. While at it, make sure that the dependency + // doesn't belong to any existing cluster. + // + auto i (before_begin ()); // Insert after this element. - const lazy_shared_ptr& af ( - pkg.repository_fragment); + for (auto j (begin ()); j != end (); ++i, ++j) + assert (!j->contains_dependency (dependency)); - for (const dependency& dp: da) - { - const package_name& dn (dp.name); + i = insert_after (i, + postponed_configuration (next_id_++, + move (dependent), + position, + move (dependency))); - if (buildtime && pdb.type == build2_config_type) - { - assert (dr == nullptr); // Should fail on the "silent" run. + l5 ([&]{trace << "create " << *i;}); + } - // Note that the dependent is not necessarily a build system - // module. - // - fail << "build-time dependency " << dn << " in build system " - << "module configuration" << - info << "build system modules cannot have build-time " - << "dependencies"; - } + postponed_configuration* + find (size_t id) + { + for (postponed_configuration& cfg: *this) + { + if (cfg.id == id) + return &cfg; + } - bool system (false); - bool specified (false); + return nullptr; + } - // If the user specified the desired dependency version constraint, - // then we will use it to overwrite the constraint imposed by the - // dependent package, checking that it is still satisfied. - // - // Note that we can't just rely on the execution plan refinement - // that will pick up the proper dependency version at the end of - // the day. We may just not get to the plan execution simulation, - // failing due to inability for dependency versions collected by - // two dependents to satisfy each other constraints (for an - // example see the - // pkg-build/dependency/apply-constraints/resolve-conflict{1,2} - // tests). + // Return address of the cluster the dependency belongs to and NULL if it + // doesn't belong to any cluster. + // + const postponed_configuration* + find_dependency (const package_key& d) const + { + for (const postponed_configuration& cfg: *this) + { + if (cfg.contains_dependency (d)) + return &cfg; + } - // Points to the desired dependency version constraint, if - // specified, and is NULL otherwise. Can be used as boolean flag. - // - const version_constraint* dep_constr (nullptr); + return nullptr; + } - database* ddb (fdb (pdb, dn, buildtime)); + // Return true if all the configurations have been negotiated. + // + bool + negotiated () const + { + for (const postponed_configuration& cfg: *this) + { + if (!cfg.negotiated || !*cfg.negotiated) + return false; + } - auto i (ddb != nullptr - ? map_.find (*ddb, dn) - : map_.find_dependency (pdb, dn, buildtime)); + return true; + } - if (i != map_.end ()) - { - const build_package& bp (i->second.package); + // Translate index to iterator and return the referenced configuration. + // + postponed_configuration& + operator[] (size_t index) + { + auto i (begin ()); + for (size_t j (0); j != index; ++j, ++i) assert (i != end ()); - specified = !bp.action; // Is pre-entered. + assert (i != end ()); + return *i; + } - if (specified && - // - // The version constraint is specified, - // - bp.hold_version && *bp.hold_version) - { - assert (bp.constraints.size () == 1); + size_t + size () const + { + size_t r (0); + for (auto i (begin ()); i != end (); ++i, ++r) ; + return r; + } - const build_package::constraint_type& c (bp.constraints[0]); + private: + size_t next_id_ = 1; + }; - dep_constr = &c.value; - system = bp.system; + // The following exceptions are used by the dependency configuration + // up-negotiation/refinement machinery. See the collect lambda in + // collect_build_prerequisites() for details. + // + struct retry_configuration + { + size_t depth; + package_key dependent; + }; - // If the user-specified dependency constraint is the wildcard - // version, then it satisfies any dependency constraint. - // - if (!wildcard (*dep_constr) && - !satisfies (*dep_constr, dp.constraint)) - { - if (dr != nullptr) - *dr << error << "unable to satisfy constraints on package " - << dn << - info << pkg.name () << pdb << " depends on (" << dn - << " " << *dp.constraint << ")" << - info << c.dependent << c.db << " depends on (" << dn - << " " << c.value << ")" << - info << "specify " << dn << " version to satisfy " - << pkg.name () << " constraint"; + struct merge_configuration + { + size_t depth; + }; - return precollect_result (false /* postpone */); - } - } - } + // Packages with postponed prerequisites collection, for one of the + // following reasons: + // + // - Postponed due to the inability to find a version satisfying the pre- + // entered constraint from repositories available to this package. The + // idea is that this constraint could still be satisfied from a repository + // fragment of some other package (that we haven't processed yet) that + // also depends on this prerequisite. + // + // - Postponed due to the inability to choose between two dependency + // alternatives, both having dependency packages which are not yet + // selected in the configuration nor being built. The idea is that this + // ambiguity could still be resolved after some of those dependency + // packages get built via some other dependents. + // + using postponed_packages = set; - const dependency& d (!dep_constr - ? dp - : dependency {dn, *dep_constr}); + // Map of dependency packages whose recursive processing should be postponed + // because they have dependents with configuration clauses. + // + // Note that dependents of such a package that don't have any configuration + // clauses are processed right away (since the negotiated configuration may + // not affect them) while those that do are postponed in the same way as + // those with dependency alternatives (see above). + // + // Note that the latter kind of dependent is what eventually causes + // recursive processing of the dependency packages. Which means we must + // watch out for bogus entries in this map which we may still end up with + // (e.g., because postponement caused cross-talk between dependency + // alternatives). Thus we keep flags that indicate whether we have seen each + // type of dependent and then just process dependencies that have the first + // (without config) but not the second (with config). We also need to track + // at which phase of collection an entry has been added to process the bogus + // entries accordingly. + // + struct postponed_dependency + { + bool wout_config; // Has dependent without config. + bool with_config; // Has dependent with config. + bool initial_collection; - // First see if this package is already selected. If we already - // have it in the configuration and it satisfies our dependency - // version constraint, then we don't want to be forcing its - // upgrade (or, worse, downgrade). - // - // If the prerequisite configuration is explicitly specified by the - // user, then search for the prerequisite in this specific - // configuration. Otherwise, search recursively in the explicitly - // linked configurations of the dependent configuration. - // - // Note that for the repointed dependent we will always find the - // prerequisite replacement rather than the prerequisite being - // replaced. - // - pair, database*> spd ( - ddb != nullptr - ? make_pair (ddb->find (dn), ddb) - : find_dependency (pdb, dn, buildtime)); + postponed_dependency (bool woc, bool wic, bool ic) + : wout_config (woc), + with_config (wic), + initial_collection (ic) {} - if (ddb == nullptr) - ddb = &pdb; + bool + bogus () const {return wout_config && !with_config;} + }; - shared_ptr& dsp (spd.first); + class postponed_dependencies: public map + { + public: + bool + has_bogus () const + { + for (const auto& pd: *this) + { + if (pd.second.bogus ()) + return true; + } + return false; + } - if (prereqs != nullptr && - (dsp == nullptr || - find_if (prereqs->begin (), prereqs->end (), - [&dsp] (const auto& v) - { - return v.first.object_id () == dsp->name; - }) == prereqs->end ())) - return precollect_result (false /* postpone */); + // Erase the bogus postponements and throw cancel_postponement, if any. + // + struct cancel_postponement: scratch_collection + { + cancel_postponement () + : scratch_collection ( + "bogus dependency collection postponement cancellation") {} + }; - pair, - lazy_shared_ptr> rp; + void + cancel_bogus (tracer& trace, bool initial_collection) + { + bool bogus (false); + for (auto i (begin ()); i != end (); ) + { + const postponed_dependency& d (i->second); - shared_ptr& dap (rp.first); + if (d.bogus () && (!initial_collection || d.initial_collection)) + { + bogus = true; - bool force (false); + l5 ([&]{trace << "erase bogus postponement " << i->first;}); - if (dsp != nullptr) - { - // Switch to the selected package configuration. - // - ddb = spd.second; + i = erase (i); + } + else + ++i; + } - // If we are collecting prerequisites of the repointed - // dependent, then only proceed further if this is either a - // replacement or unamended prerequisite and we are - // up/down-grading (only for the latter). - // - if (rpt_prereq_flags != nullptr) - { - auto i (rpt_prereq_flags->find (config_package {*ddb, dn})); + if (bogus) + { + l5 ([&]{trace << "bogus postponements erased, throwing";}); + throw cancel_postponement (); + } + } + }; - bool unamended (i == rpt_prereq_flags->end ()); - bool replacement (!unamended && i->second); + // Map of existing dependents which may not be re-evaluated to a position + // with the dependency index greater than the specified one. + // + // This mechanism applies when we re-evaluate an existing dependent to a + // certain position but later realize we've gone too far. In this case we + // note the earlier position information and re-collect from scratch. On the + // re-collection any re-evaluation of the dependent to a greater position + // will be either skipped or performed but to this earlier position (see the + // replace member for details). + // + // We consider the postponement bogus if some dependent re-evaluation was + // skipped due to its presence but no re-evaluation to this (or earlier) + // dependency index was performed. Thus, if after the collection of packages + // some bogus entries are present in the map, then it means that we have + // skipped the respective re-evaluations erroneously and so need to erase + // these entries and re-collect. + // + // Note that if no re-evaluation is skipped due to a postponement then it + // is harmless and we don't consider it bogus. + // + struct postponed_position: pair + { + // True if the "later" position should be replaced rather than merely + // skipped. The replacement deals with the case where the "earlier" + // position is encountered while processing the same cluster as what + // contains the later position. In this case, if we merely skip, then we + // will never naturally encounter the earlier position. So we have to + // force the issue (even if things change enough for us to never see the + // later position again). + // + bool replace; - // We can never end up with the prerequisite being replaced, - // since the fdb() function should always return the - // replacement instead (see above). - // - assert (unamended || replacement); + // Re-evaluation was skipped due to this postponement. + // + bool skipped = false; - if (!(replacement || (unamended && ud))) - continue; - } + // The dependent was re-evaluated. Note that it can be only re-evaluated + // to this or earlier dependency index. + // + bool reevaluated = false; - if (dsp->state == package_state::broken) - { - assert (dr == nullptr); // Should fail on the "silent" run. + postponed_position (pair p, bool r) + : pair (p), replace (r) {} + }; - fail << "unable to build broken package " << dn << *ddb << - info << "use 'pkg-purge --force' to remove"; - } + class postponed_positions: public map + { + public: + // If true, override the first encountered non-replace position to replace + // and clear this flag. See collect_build_postponed() for details. + // + bool replace = false; - // If the constraint is imposed by the user we also need to make - // sure that the system flags are the same. - // - if (satisfies (dsp->version, d.constraint) && - (!dep_constr || dsp->system () == system)) - { - system = dsp->system (); + // Erase the bogus postponements and throw cancel_postponement, if any. + // + struct cancel_postponement: scratch_collection + { + cancel_postponement () + : scratch_collection ("bogus existing dependent re-evaluation " + "postponement cancellation") {} + }; - optional vc ( - !system - ? version_constraint (dsp->version) - : optional ()); + void + cancel_bogus (tracer& trace) + { + bool bogus (false); + for (auto i (begin ()); i != end (); ) + { + const postponed_position& p (i->second); - // First try to find an available package for this exact - // version, falling back to ignoring version revision and - // iteration. In particular, this handles the case where a - // package moves from one repository to another (e.g., from - // testing to stable). For a system package we pick the latest - // one (its exact version doesn't really matter). - // - // It seems reasonable to search for the package in the - // repositories explicitly added by the user if the selected - // package was explicitly specified on command line, and in - // the repository (and its complements/prerequisites) of the - // dependent being currently built otherwise. - // - if (dsp->hold_package) - { - linked_databases dbs (dependent_repo_configs (*ddb)); + if (p.skipped && !p.reevaluated) + { + bogus = true; - rp = find_available_one (dbs, - dn, - vc, - true /* prereq */, - true /* revision */); + l5 ([&]{trace << "erase bogus existing dependent " << i->first + << " re-evaluation postponement with dependency index " + << i->second.first;}); - // Note: constraint is not present for the system package, - // so there is no sense to repeat the attempt. - // - if (dap == nullptr && !system) - rp = find_available_one (dbs, dn, vc); - } - else if (af != nullptr) - { - rp = find_available_one (dn, - vc, - af, - true /* prereq */, - true /* revision */); + // It seems that the replacement may never be bogus. + // + assert (!p.replace); - if (dap == nullptr && !system) - rp = find_available_one (dn, vc, af); - } + i = erase (i); + } + else + ++i; + } - // A stub satisfies any version constraint so we weed them out - // (returning stub as an available package feels wrong). - // - if (dap == nullptr || dap->stub ()) - rp = make_available_fragment (options, *ddb, dsp); - } - else - // Remember that we may be forcing up/downgrade; we will deal - // with it below. - // - force = true; - } + if (bogus) + { + l5 ([&]{trace << "bogus re-evaluation postponement erased, throwing";}); + throw cancel_postponement (); + } + } + }; - // If this is a build-time dependency and we build it for the - // first time, then we need to find a suitable configuration (of - // the host or build2 type) to build it in. - // - // If the current configuration (ddb) is of the suitable type, - // then we use that. Otherwise, we go through its immediate - // explicit links. If only one of them has the suitable type, then - // we use that. If there are multiple of them, then we fail - // advising the user to pick one explicitly. If there are none, - // then we create the private configuration and use that. If the - // current configuration is private, then search/create in the - // parent configuration instead. - // - // Note that if the user has explicitly specified the - // configuration for this dependency on the command line (using - // --config-*), then this configuration is used as the starting - // point for this search. - // - if (buildtime && - dsp == nullptr && - ddb->type != buildtime_dependency_type (dn)) - { - database* db (nullptr); - database& sdb (ddb->private_ () ? ddb->parent_config () : *ddb); + struct build_packages: build_package_list + { + build_packages () = default; - const string& type (buildtime_dependency_type (dn)); + // Copy-constructible and move-assignable (used for snapshoting). + // + build_packages (const build_packages& v) + : build_package_list () + { + // Copy the map. + // + for (const auto& p: v.map_) + map_.emplace (p.first, data_type {end (), p.second.package}); - // Skip the self-link. - // - const linked_configs& lcs (sdb.explicit_links ()); - for (auto i (lcs.begin_linked ()); i != lcs.end (); ++i) - { - database& ldb (i->db); + // Copy the list. + // + for (const auto& p: v) + { + auto i (map_.find (p.get ().db, p.get ().name ())); + assert (i != map_.end ()); + i->second.position = insert (end (), i->second.package); + } + } - if (ldb.type == type) - { - if (db == nullptr) - db = &ldb; - else - { - assert (dr == nullptr); // Should fail on the "silent" run. + build_packages (build_packages&&) = delete; - fail << "multiple possible " << type << " configurations " - << "for build-time dependency (" << dp << ")" << - info << db->config_orig << - info << ldb.config_orig << - info << "use --config-* to select the configuration"; - } - } - } + build_packages& operator= (const build_packages&) = delete; - // If no suitable configuration is found, then create and link - // it, unless the --no-private-config options is specified. In - // the latter case, print the dependency chain to stdout and - // exit with the specified code. - // - if (db == nullptr) - { - // The private config should be created on the "silent" run - // and so there always should be a suitable configuration on - // the diagnostics run. - // - assert (dr == nullptr); + build_packages& + operator= (build_packages&& v) + { + clear (); - if (options.no_private_config_specified ()) - try - { - // Note that we don't have the dependency package version - // yet. We could probably rearrange the code and obtain the - // available dependency package by now, given that it comes - // from the main database and may not be specified as system - // (we would have the configuration otherwise). However, - // let's not complicate the code further and instead print - // the package name and the constraint, if present. - // - // Also, in the future, we may still need the configuration - // to obtain the available dependency package for some - // reason (may want to fetch repositories locally, etc). - // - cout << d << '\n'; + // Move the map. + // + // Similar to what we do in the copy-constructor, but here we also need + // to restore the database reference and the package shared pointers in + // the source entry after the move. This way we can obtain the source + // packages databases and names later while copying the list. + // + for (auto& p: v.map_) + { + build_package& bp (p.second.package); - // Note that we also need to clean the dependency chain, to - // prevent the exception guard from printing it to stderr. - // - for (build_package_refs dc (move (dep_chain)); - !dc.empty (); ) - { - const build_package& p (dc.back ()); + database& db (bp.db); + shared_ptr sp (bp.selected); + shared_ptr ap (bp.available); - cout << p.available_name_version () << ' ' - << p.db.get ().config << '\n'; + map_.emplace (p.first, data_type {end (), move (bp)}); - dc.pop_back (); - } + bp.db = db; + bp.selected = move (sp); + bp.available = move (ap); + } - throw failed (options.no_private_config ()); - } - catch (const io_error&) - { - fail << "unable to write to stdout"; - } + // Copy the list. + // + for (const auto& p: v) + { + auto i (map_.find (p.get ().db, p.get ().name ())); + assert (i != map_.end ()); + i->second.position = insert (end (), i->second.package); + } - const strings mods {"cc"}; + return *this; + } - const strings vars { - "config.config.load=~" + type, - "config.config.persist+='config.*'@unused=drop"}; + // Pre-enter a build_package without an action. No entry for this package + // may already exists. + // + void + enter (package_name name, build_package pkg) + { + assert (!pkg.action); - dir_path cd (bpkg_dir / dir_path (type)); + database& db (pkg.db); // Save before the move() call. + auto p (map_.emplace (package_key {db, move (name)}, + data_type {end (), move (pkg)})); - // Wipe a potentially existing un-linked private configuration - // left from a previous faulty run. Note that trying to reuse - // it would be a bad idea since it can be half-prepared, with - // an outdated database schema version, etc. - // - cfg_create (options, - sdb.config_orig / cd, - optional (type) /* name */, - type /* type */, - mods, - vars, - false /* existing */, - true /* wipe */); + assert (p.second); + } - // Note that we will copy the name from the configuration - // unless it clashes with one of the existing links. - // - shared_ptr lc ( - cfg_link (sdb, - sdb.config / cd, - true /* relative */, - nullopt /* name */, - true /* sys_rep */)); + // Return the package pointer if it is already in the map and NULL + // otherwise (so can be used as bool). + // + build_package* + entered_build (database& db, const package_name& name) + { + auto i (map_.find (db, name)); + return i != map_.end () ? &i->second.package : nullptr; + } - // Save the newly-created private configuration, together with - // the containing configuration database, for their subsequent - // re-link. - // - apc (sdb, move (cd)); + build_package* + entered_build (const package_key& p) + { + return entered_build (p.db, p.name); + } - db = &sdb.find_attached (*lc->id); - } + // Collect the package being built. Return its pointer if this package + // version was, in fact, added to the map and NULL if it was already there + // and the existing version was preferred or if the package build has been + // replaced with the drop. So can be used as bool. + // + // Consult replaced_vers for an existing version replacement entry and + // follow it, if present, potentially collecting the package drop + // instead. Add entry to replaced_vers and throw replace_version if the + // existing version needs to be replaced but the new version cannot be + // re-collected recursively in-place (see replaced_versions for details). + // Also add an entry and throw if the existing dependent needs to be + // replaced. + // + // Optionally, pass the function which verifies the chosen package + // version. It is called before replace_version is potentially thrown or + // the recursive collection is performed. The scratch argument is true if + // the package version needs to be replaced but in-place replacement is + // not possible (see replaced_versions for details). + // + // Also, in the recursive mode (dep_chain is not NULL): + // + // - Use the custom search function to find the package dependency + // databases. + // + // - For the repointed dependents collect the prerequisite replacements + // rather than prerequisites being replaced. + // + // - Add paths of the created private configurations, together with the + // containing configuration databases, into the specified list (see + // private_configs for details). + // + // Note that postponed_* and dep_chain arguments must all be either + // specified or not. + // + struct replace_version: scratch_collection + { + replace_version () + : scratch_collection ("package version replacement") {} + }; - ddb = db; // Switch to the dependency configuration. - } + using verify_package_build_function = void (const build_package&, + bool scratch); - // Note that building a dependent which is not a build2 module in - // the same configuration with the build2 module it depends upon - // is an error. + build_package* + collect_build (const pkg_build_options& options, + build_package pkg, + const function& fdb, + const repointed_dependents& rpt_depts, + const function& apc, + bool initial_collection, + replaced_versions& replaced_vers, + postponed_configurations& postponed_cfgs, + build_package_refs* dep_chain = nullptr, + postponed_packages* postponed_repo = nullptr, + postponed_packages* postponed_alts = nullptr, + postponed_dependencies* postponed_deps = nullptr, + postponed_positions* postponed_poss = nullptr, + const function& vpb = nullptr) + { + using std::swap; // ...and not list::swap(). + + tracer trace ("collect_build"); + + // See the above notes. + // + bool recursive (dep_chain != nullptr); + assert ((postponed_repo != nullptr) == recursive && + (postponed_alts != nullptr) == recursive && + (postponed_deps != nullptr) == recursive && + (postponed_poss != nullptr) == recursive); + + // Only builds are allowed here. + // + assert (pkg.action && *pkg.action == build_package::build && + pkg.available != nullptr); + + package_key pk (pkg.db, pkg.available->id.name); + + // Apply the version replacement, if requested, and indicate that it was + // applied. + // + auto vi (replaced_vers.find (pk)); + + if (vi != replaced_vers.end () && !vi->second.replaced) + { + l5 ([&]{trace << "apply version replacement for " + << pkg.available_name_version_db ();}); + + replaced_version& v (vi->second); + + v.replaced = true; + + if (v.available != nullptr) + { + pkg.available = v.available; + pkg.repository_fragment = v.repository_fragment; + pkg.system = v.system; + + l5 ([&]{trace << "replacement: " + << pkg.available_name_version_db ();}); + } + else + { + l5 ([&]{trace << "replacement: drop";}); + + assert (pkg.selected != nullptr); + + collect_drop (options, pkg.db, pkg.selected, replaced_vers); + return nullptr; + } + } + + // Add the version replacement entry, call the verification function if + // specified, and throw replace_version. + // + auto replace_ver = [&pk, &vpb, &vi, &replaced_vers] + (const build_package& p) + { + replaced_version rv (p.available, p.repository_fragment, p.system); + + if (vi != replaced_vers.end ()) + vi->second = move (rv); + else + replaced_vers.emplace (move (pk), move (rv)); + + if (vpb) + vpb (p, true /* scratch */); + + throw replace_version (); + }; + + auto i (map_.find (pk)); + + // If we already have an entry for this package name, then we have to + // pick one over the other. + // + // If the existing entry is a drop, then we override it. If the existing + // entry is a pre-entered or is non-build one, then we merge it into the + // new build entry. Otherwise (both are builds), we pick one and merge + // the other into it. + // + if (i != map_.end ()) + { + build_package& bp (i->second.package); + + // Note that we used to think that the scenario when the build could + // replace drop could never happen since we would start collecting + // from scratch. This has changed when we introduced replaced_versions + // for collecting drops. + // + if (bp.action && *bp.action == build_package::drop) // Drop. + { + bp = move (pkg); + } + else if (!bp.action || *bp.action != build_package::build) // Non-build. + { + pkg.merge (move (bp)); + bp = move (pkg); + } + else // Build. + { + // At the end we want p1 to point to the object that we keep + // and p2 to the object that we merge from. + // + build_package* p1 (&bp); + build_package* p2 (&pkg); + + // Pick with the following preference order: user selection over + // implicit one, source package over a system one, newer version + // over an older one. So get the preferred into p1 and the other + // into p2. + // + { + int us (p1->user_selection () - p2->user_selection ()); + int sf (p1->system - p2->system); + + if (us < 0 || + (us == 0 && sf > 0) || + (us == 0 && + sf == 0 && + p2->available_version () > p1->available_version ())) + swap (p1, p2); + } + + // If the versions differ, pick the satisfactory one and if both are + // satisfactory, then keep the preferred. + // + if (p1->available_version () != p2->available_version ()) + { + using constraint_type = build_package::constraint_type; + + // See if pv's version satisfies pc's constraints. Return the + // pointer to the unsatisfied constraint or NULL if all are + // satisfied. // - if (buildtime && - !build2_module (pkg.name ()) && - build2_module (dn) && - pdb == *ddb) + auto test = [] (build_package* pv, + build_package* pc) -> const constraint_type* { - assert (dr == nullptr); // Should fail on the "silent" run. + for (const constraint_type& c: pc->constraints) + { + if (!satisfies (pv->available_version (), c.value)) + return &c; + } - // Note that the dependent package information is printed by the - // above exception guard. - // - fail << "unable to build build system module " << dn - << " in its dependent package configuration " - << pdb.config_orig << - info << "use --config-* to select suitable configuration"; - } + return nullptr; + }; - // If we didn't get the available package corresponding to the - // selected package, look for any that satisfies the constraint. + // First see if p1 satisfies p2's constraints. // - if (dap == nullptr) + if (auto c2 = test (p1, p2)) { - // And if we have no repository fragment to look in, then that - // means the package is an orphan (we delay this check until we - // actually need the repository fragment to allow orphans - // without prerequisites). + // If not, try the other way around. // - if (af == nullptr) + if (auto c1 = test (p2, p1)) { - assert (dr == nullptr); // Should fail on the "silent" run. + const package_name& n (i->first.name); + const string& d1 (c1->dependent); + const string& d2 (c2->dependent); - fail << "package " << pkg.available_name_version_db () - << " is orphaned" << - info << "explicitly upgrade it to a new version"; + fail << "unable to satisfy constraints on package " << n << + info << d1 << c1->db << " depends on (" << n << " " + << c1->value << ")" << + info << d2 << c2->db << " depends on (" << n << " " + << c2->value << ")" << + info << "available " << p1->available_name_version () << + info << "available " << p2->available_name_version () << + info << "explicitly specify " << n << " version to manually " + << "satisfy both constraints"; } + else + swap (p1, p2); + } - // We look for prerequisites only in the repositories of this - // package (and not in all the repositories of this - // configuration). At first this might look strange, but it - // also kind of makes sense: we only use repositories "approved" - // for this package version. Consider this scenario as an - // example: hello/1.0.0 and libhello/1.0.0 in stable and - // libhello/2.0.0 in testing. As a prerequisite of hello, which - // version should libhello resolve to? While one can probably - // argue either way, resolving it to 1.0.0 is the conservative - // choice and the user can always override it by explicitly - // building libhello. - // - // Note though, that if this is a test package, then its special - // test dependencies (main packages that refer to it) should be - // searched upstream through the complement repositories - // recursively, since the test packages may only belong to the - // main package's repository and its complements. - // - // @@ Currently we don't implement the reverse direction search - // for the test dependencies, effectively only supporting the - // common case where the main and test packages belong to the - // same repository. Will need to fix this eventually. + l4 ([&]{trace << "pick " << p1->available_name_version_db () + << " over " << p2->available_name_version_db ();}); + } + + // See if we are replacing the object. If not, then we don't need to + // collect its prerequisites since that should have already been + // done. Remember, p1 points to the object we want to keep. + // + bool replace (p1 != &i->second.package); + + if (replace) + { + swap (*p1, *p2); + swap (p1, p2); // Setup for merge below. + } + + p1->merge (move (*p2)); + + if (replace) + { + if (p1->available_version () != p2->available_version () || + p1->system != p2->system) + { + // See if in-place replacement is possible (no dependencies, + // etc) and set scratch to false if that's the case. // - // Note that this logic (naturally) does not apply if the - // package is already selected by the user (see above). + // Firstly, such a package should not participate in any + // configuration negotiation. // - // Also note that for the user-specified dependency version - // constraint we rely on the satisfying package version be - // present in repositories of the first dependent met. As a - // result, we may fail too early if such package version doesn't - // belong to its repositories, but belongs to the ones of some - // dependent that we haven't met yet. Can we just search all - // repositories for an available package of the appropriate - // version and just take it, if present? We could, but then - // which repository should we pick? The wrong choice can - // introduce some unwanted repositories and package versions - // into play. So instead, we will postpone collecting the - // problematic dependent, expecting that some other one will - // find the appropriate version in its repositories. + // Other than that, it looks like the only optimization we can + // do easily is if the package has no dependencies (and thus + // cannot impose any constraints). Anything more advanced would + // require analyzing our dependencies (which we currently cannot + // easily get) and (1) either dropping the dependency + // build_package altogether if we are the only dependent (so + // that it doesn't influence any subsequent dependent) or (2) + // making sure our constraint is a sub-constraint of any other + // constraint and removing it from the dependency build_package. + // Maybe/later. + // + // NOTE: remember to update collect_drop() if changing anything + // here. + // + bool scratch (true); + + // While checking if the package has any dependencies skip the + // toolchain build-time dependencies since they should be quite + // common. + // + if (!has_dependencies (options, p2->available->dependencies)) + scratch = false; + + l5 ([&]{trace << p2->available_name_version_db () + << " package version needs to be replaced " + << (!scratch ? "in-place " : "") << "with " + << p1->available_name_version_db ();}); + + if (scratch) + replace_ver (*p1); + } + else + { + // It doesn't seem possible that replacing the build object + // without changing the package version may result in changing + // the package configuration since the configuration always gets + // into the initial package build entry (potentially + // pre-entered, etc). If it wouldn't be true then we would also + // need to add the replacement version entry and re-collect from + // scratch. + } + } + else + return nullptr; + } + } + else + { + // Treat the replacement of the existing dependent that is + // participating in the configuration negotiation also as a version + // replacement. This way we will not be treating the dependent as an + // existing on the re-collection (see query_existing_dependents() for + // details). + // + // Note: an existing dependent may not be configured as system. + // + if (pkg.selected != nullptr && + (pkg.selected->version != pkg.available_version () || + pkg.system)) + { + + for (const postponed_configuration& cfg: postponed_cfgs) + { + auto i (cfg.dependents.find (pk)); + + if (i != cfg.dependents.end () && i->second.existing) + replace_ver (pkg); + } + } + + // This is the first time we are adding this package name to the map. + // + l4 ([&]{trace << "add " << pkg.available_name_version_db ();}); + + i = map_.emplace (move (pk), data_type {end (), move (pkg)}).first; + } + + build_package& p (i->second.package); + + if (vpb) + vpb (p, false /* scratch */); + + // Recursively collect build prerequisites, if requested. + // + // Note that detecting dependency cycles during the satisfaction phase + // would be premature since they may not be present in the final package + // list. Instead we check for them during the ordering phase. + // + // The question, of course, is whether we can still end up with an + // infinite recursion here? Note that for an existing map entry we only + // recurse after the entry replacement. The infinite recursion would + // mean that we may replace a package in the map with the same version + // multiple times: + // + // ... p1 -> p2 -> ... p1 + // + // Every replacement increases the entry version and/or tightens the + // constraints the next replacement will need to satisfy. It feels + // impossible that a package version can "return" into the map being + // replaced once. So let's wait until some real use case proves this + // reasoning wrong. + // + if (recursive) + collect_build_prerequisites (options, + p, + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + *dep_chain, + postponed_repo, + postponed_alts, + 0 /* max_alt_index */, + *postponed_deps, + postponed_cfgs, + *postponed_poss); + + return &p; + } + + // Collect prerequisites of the package being built recursively. + // + // But first "prune" this process if the package we build is a system one + // or is already configured, since that would mean all its prerequisites + // are configured as well. Note that this is not merely an optimization: + // the package could be an orphan in which case the below logic will fail + // (no repository fragment in which to search for prerequisites). By + // skipping the prerequisite check we are able to gracefully handle + // configured orphans. + // + // There are, however, some cases when we still need to re-collect + // prerequisites of a configured package: + // + // - For the repointed dependent we still need to collect its prerequisite + // replacements to make sure its dependency constraints are satisfied. + // + // - If configuration variables are specified for the dependent which has + // any buildfile clauses in the dependencies, then we need to + // re-evaluate them. This can result in a different set of dependencies + // required by this dependent (due to conditional dependencies, etc) + // and, potentially, for its reconfigured existing prerequisites, + // recursively. + // + // - For an existing dependent being re-evaluated to the specific + // dependency position. + // + // Note that for these cases, as it was said above, we can potentially + // fail if the dependent is an orphan, but this is exactly what we need to + // do in that case, since we won't be able to re-collect its dependencies. + // + // Only a single true dependency alternative can be selected per function + // call. Such an alternative can only be selected if its index in the + // postponed alternatives list is less than the specified maximum (used by + // the heuristics that determines in which order to process packages with + // alternatives; if 0 is passed, then no true alternative will be + // selected). + // + // The idea here is to postpone the true alternatives selection till the + // end of the packages collection and then try to optimize the overall + // resulting selection (over all the dependents) by selecting alternatives + // with the lower indexes first (see collect_build_postponed() for + // details). + // + // Always postpone recursive collection of dependencies for a dependent + // with configuration clauses, recording them in postponed_deps (see + // postponed_dependencies for details) and also recording the dependent in + // postponed_cfgs (see postponed_configurations for details). If it turns + // out that some dependency of such a dependent has already been collected + // via some other dependent without configuration clauses, then throw the + // postpone_dependency exception. This exception is handled via + // re-collecting packages from scratch, but now with the knowledge about + // premature dependency collection. If it turns out that some dependency + // configuration has already been negotiated between some other + // dependents, then up-negotiate the configuration and throw + // retry_configuration exception so that the configuration refinement can + // be performed (see the collect lambda implementation for details on the + // configuration refinement machinery). + // + // If the package is a dependency of a configured dependent with + // configuration clause and needs to be reconfigured (being upgraded, has + // configuration specified, etc), then postpone its recursive collection + // by recording it in postponed_cfgs as a single-dependency cluster with + // an existing dependent (see postponed_configurations for details). If + // this dependent already belongs to some (being) negotiated configuration + // cluster with a greater dependency position then record this dependency + // position in postponed_poss and throw postpone_position. This exception + // is handled by re-collecting packages from scratch, but now with the + // knowledge about position this dependent needs to be re-evaluated to. + // + struct postpone_dependency: scratch_collection + { + package_key package; + + explicit + postpone_dependency (package_key p) + : scratch_collection ("prematurely collected dependency"), + package (move (p)) + { + scratch_collection::package = &package; + } + }; + + struct postpone_position: scratch_collection + { + postpone_position () + : scratch_collection ("earlier dependency position") {} + }; + + void + collect_build_prerequisites (const pkg_build_options& options, + build_package& pkg, + const function& fdb, + const repointed_dependents& rpt_depts, + const function& apc, + bool initial_collection, + replaced_versions& replaced_vers, + build_package_refs& dep_chain, + postponed_packages* postponed_repo, + postponed_packages* postponed_alts, + size_t max_alt_index, + postponed_dependencies& postponed_deps, + postponed_configurations& postponed_cfgs, + postponed_positions& postponed_poss, + pair reeval_pos = + make_pair(0, 0)) + { + // NOTE: don't forget to update collect_build_postponed() if changing + // anything in this function. + // + tracer trace ("collect_build_prerequisites"); + + assert (pkg.action && *pkg.action == build_package::build); + + const package_name& nm (pkg.name ()); + database& pdb (pkg.db); + package_key pk (pdb, nm); + + bool reeval (reeval_pos.first != 0); + + // The being re-evaluated dependent cannot be recursively collected yet. + // Also, we don't expect it being configured as system. + // + // Note that the configured package can still be re-evaluated after + // collect_build_prerequisites() has been called but didn't end up with + // the recursive collection. + // + assert (!reeval || + ((!pkg.recursive_collection || + !pkg.recollect_recursively (rpt_depts)) && + !pkg.skeleton && !pkg.system)); + + // If this package is not being re-evaluated, is not yet collected + // recursively, needs to be reconfigured, and is not yet postponed, then + // check if it is a dependency of any dependent with configuration + // clause and postpone the collection if that's the case. + // + // The reason why we don't need to do this for the re-evaluated case is + // as follows: this logic is used for an existing dependent that is not + // otherwise built (e.g., reconfigured) which means its externally- + // imposed configuration (user, dependents) is not being changed. + // + if (!reeval && + !pkg.recursive_collection && + pkg.reconfigure () && + postponed_cfgs.find_dependency (pk) == nullptr) + { + // If the dependent is being built, then check if it was re-evaluated + // to the position greater than the dependency position. Return true + // if that's the case, so this package is added to the resulting list + // and we can handle this situation below. + // + // Note that we rely on "small function object" optimization here. + // + const function verify ( + [&postponed_cfgs] + (const package_key& pk, pair pos) + { + for (const postponed_configuration& cfg: postponed_cfgs) + { + if (cfg.negotiated) + { + if (const pair* p = + cfg.existing_dependent_position (pk)) + { + if (p->first > pos.first) + return true; + } + } + } + + return false; + }); + + // Note that there can be multiple existing dependents for a + // dependency. Strictly speaking, we only need to add the first one + // with the assumption that the remaining dependents will also be + // considered comes the time for the negotiation. Let's, however, + // process all of them to detect the potential "re-evaluation on the + // greater dependency index" situation earlier. And, generally, have + // as much information as possible up front. + // + vector eds ( + query_existing_dependents (trace, + pk.db, + pk.name, + replaced_vers, + rpt_depts, + verify)); + + if (!eds.empty ()) + { + for (existing_dependent& ed: eds) + { + package_key dpk (ed.db, ed.selected->name); + size_t& di (ed.dependency_position.first); + + const build_package* bp (&pkg); + + // Check if this dependent needs to be re-evaluated to an earlier + // dependency position and, if that's the case, create the + // configuration cluster with this dependency instead. + // + // Note that if the replace flag is false, we proceed normally + // with the assumption that the dependency referred by the entry + // will be collected later and its configuration cluster will be + // created normally and will be negotiated earlier than the + // cluster being created for the current dependency (see + // collect_build_postponed() for details). + // + { + auto pi (postponed_poss.find (dpk)); + + if (pi != postponed_poss.end () && pi->second.first < di) + { + // If requested, override the first encountered non-replace + // position to replace. See collect_build_postponed () for + // details. + // + if (!pi->second.replace && postponed_poss.replace) + { + pi->second.replace = true; + postponed_poss.replace = false; + } + + if (pi->second.replace) + { + // Overwrite the existing dependent dependency information + // and fall through to proceed as for the normal case. + // + bp = replace_existing_dependent_dependency ( + trace, + options, + ed, // Note: modified. + pi->second, + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + postponed_cfgs); + + pk = package_key (bp->db, bp->name ()); + + // Note that here we side-step the bogus logic (by not + // setting the skipped flag) because in this case + // (replace=true) our choices are either (potentially) bogus + // or pathological (where we have evaluated too far). In + // other words, the postponed entry may cause the depends + // entry that triggered it to disappear (and thus, strictly + // speaking, to become bogus) but if we cancel it, we will + // be back to square one. + } + } + } + + // Make sure that this existing dependent doesn't belong to any + // (being) negotiated configuration cluster with a greater + // dependency index. That would mean that this dependent has + // already been re-evaluated to this index and so cannot + // participate in the configuration negotiation of this earlier + // dependency. + // + for (const postponed_configuration& cfg: postponed_cfgs) + { + if (const pair* p = + cfg.existing_dependent_position (pk)) + { + size_t ei (p->first); + + if (di < ei && cfg.negotiated) + { + // Feels like there cannot be an earlier position. + // + postponed_position pp (ed.dependency_position, + false /* replace */); + + auto p (postponed_poss.emplace (move (pk), pp)); + if (!p.second) + { + assert (p.first->second > pp); + p.first->second = pp; + } + + l5 ([&]{trace << "cannot cfg-postpone dependency " + << bp->available_name_version_db () + << " of existing dependent " << *ed.selected + << ed.db << " (index " << di + << ") due to earlier dependency index " << ei + << " in " << cfg << ", throwing " + << "postpone_position";}); + + // Don't print the "while satisfying..." chain. + // + dep_chain.clear (); + + throw postpone_position (); + } + + if (di == ei) + { + // For the negotiated cluster all the dependency packages + // should have been added. For non-negotiated cluster we + // cannot add the missing dependencies at the moment and + // will do it as a part of the dependent re-evaluation. + // + assert (!cfg.negotiated); + } + } + } + + l5 ([&]{trace << "cfg-postpone dependency " + << bp->available_name_version_db () + << " of existing dependent " << *ed.selected + << ed.db;}); + + postponed_cfgs.add (move (dpk), ed.dependency_position, move (pk)); + } + + return; + } + } + + pkg.recursive_collection = true; + + if (pkg.system) + { + l5 ([&]{trace << "skip system " << pkg.available_name_version_db ();}); + return; + } + + const shared_ptr& ap (pkg.available); + assert (ap != nullptr); + + const shared_ptr& sp (pkg.selected); + + // True if this is an up/down-grade. + // + bool ud (sp != nullptr && sp->version != pkg.available_version ()); + + // If this is a repointed dependent, then it points to its prerequisite + // replacements flag map (see repointed_dependents for details). + // + const map* rpt_prereq_flags (nullptr); + + // Bail out if this is a configured non-system package and no recursive + // collection is required. + // + bool src_conf (sp != nullptr && + sp->state == package_state::configured && + sp->substate != package_substate::system); + + // The being re-evaluated dependent must be configured as a source + // package and should not be collected recursively (due to upgrade, + // etc). + // + assert (!reeval || (src_conf && !pkg.recollect_recursively (rpt_depts))); + + if (src_conf) + { + repointed_dependents::const_iterator i (rpt_depts.find (pk)); + + if (i != rpt_depts.end ()) + rpt_prereq_flags = &i->second; + + if (!reeval && !pkg.recollect_recursively (rpt_depts)) + { + l5 ([&]{trace << "skip configured " + << pkg.available_name_version_db ();}); + return; + } + } + + // Iterate over dependencies, trying to unambiguously select a + // satisfactory dependency alternative for each of them. Fail or + // postpone the collection if unable to do so. + // + const dependencies& deps (ap->dependencies); + + // The skeleton can be pre-initialized before the recursive collection + // starts (as a part of dependency configuration negotiation, etc). The + // dependencies and alternatives members must both be either present or + // not. + // + assert ((!pkg.dependencies || pkg.skeleton) && + pkg.dependencies.has_value () == pkg.alternatives.has_value ()); + + // Note that the selected alternatives list can be filled partially (see + // build_package::dependencies for details). In this case we continue + // collecting where we stopped previously. + // + if (!pkg.dependencies) + { + l5 ([&]{trace << (reeval ? "reeval " : "begin ") + << pkg.available_name_version_db ();}); + + pkg.dependencies = dependencies (); + pkg.alternatives = vector (); + + if (size_t n = deps.size ()) + { + pkg.dependencies->reserve (n); + pkg.alternatives->reserve (n); + } + + if (!pkg.skeleton) + pkg.init_skeleton (options); + } + else + l5 ([&]{trace << "resume " << pkg.available_name_version_db ();}); + + dependencies& sdeps (*pkg.dependencies); + vector& salts (*pkg.alternatives); + + assert (sdeps.size () == salts.size ()); // Must be parallel. + + // Check if there is nothing to collect anymore. + // + if (sdeps.size () == deps.size ()) + { + l5 ([&]{trace << "end " << pkg.available_name_version_db ();}); + return; + } + + // Show how we got here if things go wrong. + // + // To suppress printing this information clear the dependency chain + // before throwing an exception. + // + auto g ( + make_exception_guard ( + [&dep_chain] () + { + // Note that we also need to clear the dependency chain, to + // prevent the caller's exception guard from printing it. + // + while (!dep_chain.empty ()) + { + info << "while satisfying " + << dep_chain.back ().get ().available_name_version_db (); + + dep_chain.pop_back (); + } + })); + + dep_chain.push_back (pkg); + + assert (sdeps.size () < deps.size ()); + + package_skeleton& skel (*pkg.skeleton); + + auto fail_reeval = [&pkg] () + { + fail << "unable to re-create dependency information of already " + << "configured package " << pkg.available_name_version_db () << + info << "likely cause is change in external environment" << + info << "consider resetting the build configuration"; + }; + + bool postponed (false); + bool reevaluated (false); + + for (size_t di (sdeps.size ()); di != deps.size (); ++di) + { + // Fail if we missed the re-evaluation target position for any reason. + // + if (reeval && di == reeval_pos.first) // Note: reeval_pos is 1-based. + fail_reeval (); + + const dependency_alternatives_ex& das (deps[di]); + + // Add an empty alternatives list into the selected dependency list if + // this is a toolchain build-time dependency. + // + dependency_alternatives_ex sdas (das.buildtime, das.comment); + + if (toolchain_buildtime_dependency (options, das, &nm)) + { + sdeps.push_back (move (sdas)); + salts.push_back (0); // Keep parallel to sdeps. + continue; + } + + // Evaluate alternative conditions and filter enabled alternatives. + // Add an empty alternatives list into the selected dependency list if + // there are none. + // + build_package::dependency_alternatives_refs edas; + + if (pkg.postponed_dependency_alternatives) + { + edas = move (*pkg.postponed_dependency_alternatives); + pkg.postponed_dependency_alternatives = nullopt; + } + else + { + for (size_t i (0); i != das.size (); ++i) + { + const dependency_alternative& da (das[i]); + + if (!da.enable || + skel.evaluate_enable (*da.enable, make_pair (di, i))) + edas.push_back (make_pair (ref (da), i)); + } + } + + if (edas.empty ()) + { + sdeps.push_back (move (sdas)); + salts.push_back (0); // Keep parallel to sdeps. + continue; + } + + // Try to pre-collect build information (pre-builds) for the + // dependencies of an alternative. Optionally, issue diagnostics into + // the specified diag record. + // + // Note that rather than considering an alternative as unsatisfactory + // (returning no pre-builds) the function can fail in some cases + // (multiple possible configurations for a build-time dependency, + // orphan or broken selected package, etc). The assumption here is + // that the user would prefer to fix a dependency-related issue first + // instead of proceeding with the build which can potentially end up + // with some less preferable dependency alternative. + // + struct prebuild + { + bpkg::dependency dependency; + reference_wrapper db; + shared_ptr selected; + shared_ptr available; + lazy_shared_ptr repository_fragment; + bool system; + bool specified_dependency; + bool force; + + // True if the dependency package is either selected in the + // configuration or is already being built. + // + bool reused; + }; + using prebuilds = small_vector; + + class precollect_result + { + public: + // Nullopt if some dependencies cannot be resolved. + // + optional builds; + + // True if dependencies can all be resolved (builds is present) and + // are all reused (see above). + // + bool reused; + + // True if some of the dependencies cannot be resolved (builds is + // nullopt) and the dependent package prerequisites collection needs + // to be postponed due to inability to find a version satisfying the + // pre-entered constraint from repositories available to the + // dependent package. + // + bool repo_postpone; + + // Create precollect result containing dependency builds. + // + precollect_result (prebuilds&& bs, bool r) + : builds (move (bs)), reused (r), repo_postpone (false) {} + + // Create precollect result without builds (some dependency can't be + // satisfied, etc). + // + explicit + precollect_result (bool p): reused (false), repo_postpone (p) {} + }; + auto precollect = [&options, + &pkg, + &nm, + &pdb, + ud, + &fdb, + rpt_prereq_flags, + &apc, + postponed_repo, + &dep_chain, + &trace, + this] + (const dependency_alternative& da, + bool buildtime, + const package_prerequisites* prereqs, + diag_record* dr = nullptr) + -> precollect_result + { + prebuilds r; + bool reused (true); + + const lazy_shared_ptr& af ( + pkg.repository_fragment); + + for (const dependency& dp: da) + { + const package_name& dn (dp.name); + + if (buildtime && pdb.type == build2_config_type) + { + assert (dr == nullptr); // Should fail on the "silent" run. + + // Note that the dependent is not necessarily a build system + // module. + // + fail << "build-time dependency " << dn << " in build system " + << "module configuration" << + info << "build system modules cannot have build-time " + << "dependencies"; + } + + bool system (false); + bool specified (false); + + // If the user specified the desired dependency version + // constraint, then we will use it to overwrite the constraint + // imposed by the dependent package, checking that it is still + // satisfied. + // + // Note that we can't just rely on the execution plan refinement + // that will pick up the proper dependency version at the end of + // the day. We may just not get to the plan execution simulation, + // failing due to inability for dependency versions collected by + // two dependents to satisfy each other constraints (for an + // example see the + // pkg-build/dependency/apply-constraints/resolve-conflict{1,2} + // tests). + + // Points to the desired dependency version constraint, if + // specified, and is NULL otherwise. Can be used as boolean flag. + // + const version_constraint* dep_constr (nullptr); + + database* ddb (fdb (pdb, dn, buildtime)); + + auto i (ddb != nullptr + ? map_.find (*ddb, dn) + : map_.find_dependency (pdb, dn, buildtime)); + + if (i != map_.end ()) + { + const build_package& bp (i->second.package); + + specified = !bp.action; // Is pre-entered. + + if (specified && + // + // The version constraint is specified, + // + bp.hold_version && *bp.hold_version) + { + assert (bp.constraints.size () == 1); + + const build_package::constraint_type& c (bp.constraints[0]); + + dep_constr = &c.value; + system = bp.system; + + // If the user-specified dependency constraint is the wildcard + // version, then it satisfies any dependency constraint. + // + if (!wildcard (*dep_constr) && + !satisfies (*dep_constr, dp.constraint)) + { + if (dr != nullptr) + *dr << error << "unable to satisfy constraints on package " + << dn << + info << nm << pdb << " depends on (" << dn + << " " << *dp.constraint << ")" << + info << c.dependent << c.db << " depends on (" << dn + << " " << c.value << ")" << + info << "specify " << dn << " version to satisfy " + << nm << " constraint"; + + return precollect_result (false /* postpone */); + } + } + } + + const dependency& d (!dep_constr + ? dp + : dependency {dn, *dep_constr}); + + // First see if this package is already selected. If we already + // have it in the configuration and it satisfies our dependency + // version constraint, then we don't want to be forcing its + // upgrade (or, worse, downgrade). + // + // If the prerequisite configuration is explicitly specified by the + // user, then search for the prerequisite in this specific + // configuration. Otherwise, search recursively in the explicitly + // linked configurations of the dependent configuration. + // + // Note that for the repointed dependent we will always find the + // prerequisite replacement rather than the prerequisite being + // replaced. + // + pair, database*> spd ( + ddb != nullptr + ? make_pair (ddb->find (dn), ddb) + : find_dependency (pdb, dn, buildtime)); + + if (ddb == nullptr) + ddb = &pdb; + + shared_ptr& dsp (spd.first); + + if (prereqs != nullptr && + (dsp == nullptr || + find_if (prereqs->begin (), prereqs->end (), + [&dsp] (const auto& v) + { + return v.first.object_id () == dsp->name; + }) == prereqs->end ())) + return precollect_result (false /* postpone */); + + pair, + lazy_shared_ptr> rp; + + shared_ptr& dap (rp.first); + + bool force (false); + + if (dsp != nullptr) + { + // Switch to the selected package configuration. + // + ddb = spd.second; + + // If we are collecting prerequisites of the repointed + // dependent, then only proceed further if this is either a + // replacement or unamended prerequisite and we are + // up/down-grading (only for the latter). + // + if (rpt_prereq_flags != nullptr) + { + auto i (rpt_prereq_flags->find (package_key {*ddb, dn})); + + bool unamended (i == rpt_prereq_flags->end ()); + bool replacement (!unamended && i->second); + + // We can never end up with the prerequisite being replaced, + // since the fdb() function should always return the + // replacement instead (see above). + // + assert (unamended || replacement); + + if (!(replacement || (unamended && ud))) + continue; + } + + if (dsp->state == package_state::broken) + { + assert (dr == nullptr); // Should fail on the "silent" run. + + fail << "unable to build broken package " << dn << *ddb << + info << "use 'pkg-purge --force' to remove"; + } + + // If the constraint is imposed by the user we also need to make + // sure that the system flags are the same. + // + if (satisfies (dsp->version, d.constraint) && + (!dep_constr || dsp->system () == system)) + { + system = dsp->system (); + + version_constraint vc (dsp->version); + + // First try to find an available package for this exact + // version, falling back to ignoring version revision and + // iteration. In particular, this handles the case where a + // package moves from one repository to another (e.g., from + // testing to stable). For a system package we will try to + // find the available package that matches the selected + // package version (preferable for the configuration + // negotiation machinery) and, if fail, fallback to picking + // the latest one (its exact version doesn't really matter in + // this case). + // + // It seems reasonable to search for the package in the + // repositories explicitly added by the user if the selected + // package was explicitly specified on command line, and in + // the repository (and its complements/prerequisites) of the + // dependent being currently built otherwise. + // + if (dsp->hold_package) + { + linked_databases dbs (dependent_repo_configs (*ddb)); + + rp = find_available_one (dbs, + dn, + vc, + true /* prereq */, + true /* revision */); + + if (dap == nullptr) + rp = find_available_one (dbs, dn, vc); + + if (dap == nullptr && system) + rp = find_available_one (dbs, dn, nullopt); + } + else if (af != nullptr) + { + rp = find_available_one (dn, + vc, + af, + true /* prereq */, + true /* revision */); + + if (dap == nullptr) + rp = find_available_one (dn, vc, af); + + if (dap == nullptr && system) + rp = find_available_one (dn, nullopt, af); + } + + // A stub satisfies any version constraint so we weed them out + // (returning stub as an available package feels wrong). + // + if (dap == nullptr || dap->stub ()) + rp = make_available_fragment (options, *ddb, dsp); + } + else + // Remember that we may be forcing up/downgrade; we will deal + // with it below. + // + force = true; + } + + // If this is a build-time dependency and we build it for the + // first time, then we need to find a suitable configuration (of + // the host or build2 type) to build it in. + // + // If the current configuration (ddb) is of the suitable type, + // then we use that. Otherwise, we go through its immediate + // explicit links. If only one of them has the suitable type, then + // we use that. If there are multiple of them, then we fail + // advising the user to pick one explicitly. If there are none, + // then we create the private configuration and use that. If the + // current configuration is private, then search/create in the + // parent configuration instead. + // + // Note that if the user has explicitly specified the + // configuration for this dependency on the command line (using + // --config-*), then this configuration is used as the starting + // point for this search. + // + if (buildtime && + dsp == nullptr && + ddb->type != buildtime_dependency_type (dn)) + { + database* db (nullptr); + database& sdb (ddb->private_ () ? ddb->parent_config () : *ddb); + + const string& type (buildtime_dependency_type (dn)); + + // Skip the self-link. + // + const linked_configs& lcs (sdb.explicit_links ()); + for (auto i (lcs.begin_linked ()); i != lcs.end (); ++i) + { + database& ldb (i->db); + + if (ldb.type == type) + { + if (db == nullptr) + db = &ldb; + else + { + assert (dr == nullptr); // Should fail on the "silent" run. + + fail << "multiple possible " << type << " configurations " + << "for build-time dependency (" << dp << ")" << + info << db->config_orig << + info << ldb.config_orig << + info << "use --config-* to select the configuration"; + } + } + } + + // If no suitable configuration is found, then create and link + // it, unless the --no-private-config options is specified. In + // the latter case, print the dependency chain to stdout and + // exit with the specified code. + // + if (db == nullptr) + { + // The private config should be created on the "silent" run + // and so there always should be a suitable configuration on + // the diagnostics run. + // + assert (dr == nullptr); + + if (options.no_private_config_specified ()) + try + { + // Note that we don't have the dependency package version + // yet. We could probably rearrange the code and obtain the + // available dependency package by now, given that it comes + // from the main database and may not be specified as system + // (we would have the configuration otherwise). However, + // let's not complicate the code further and instead print + // the package name and the constraint, if present. + // + // Also, in the future, we may still need the configuration + // to obtain the available dependency package for some + // reason (may want to fetch repositories locally, etc). + // + cout << d << '\n'; + + // Note that we also need to clean the dependency chain, to + // prevent the exception guard from printing it to stderr. + // + for (build_package_refs dc (move (dep_chain)); + !dc.empty (); ) + { + const build_package& p (dc.back ()); + + cout << p.available_name_version () << ' ' + << p.db.get ().config << '\n'; + + dc.pop_back (); + } + + throw failed (options.no_private_config ()); + } + catch (const io_error&) + { + fail << "unable to write to stdout"; + } + + const strings mods {"cc"}; + + const strings vars { + "config.config.load=~" + type, + "config.config.persist+='config.*'@unused=drop"}; + + dir_path cd (bpkg_dir / dir_path (type)); + + // Wipe a potentially existing un-linked private configuration + // left from a previous faulty run. Note that trying to reuse + // it would be a bad idea since it can be half-prepared, with + // an outdated database schema version, etc. + // + cfg_create (options, + sdb.config_orig / cd, + optional (type) /* name */, + type /* type */, + mods, + vars, + false /* existing */, + true /* wipe */); + + // Note that we will copy the name from the configuration + // unless it clashes with one of the existing links. + // + shared_ptr lc ( + cfg_link (sdb, + sdb.config / cd, + true /* relative */, + nullopt /* name */, + true /* sys_rep */)); + + // Save the newly-created private configuration, together with + // the containing configuration database, for their subsequent + // re-link. + // + apc (sdb, move (cd)); + + db = &sdb.find_attached (*lc->id); + } + + ddb = db; // Switch to the dependency configuration. + } + + // Note that building a dependent which is not a build2 module in + // the same configuration with the build2 module it depends upon + // is an error. + // + if (buildtime && + !build2_module (nm) && + build2_module (dn) && + pdb == *ddb) + { + assert (dr == nullptr); // Should fail on the "silent" run. + + // Note that the dependent package information is printed by the + // above exception guard. + // + fail << "unable to build build system module " << dn + << " in its dependent package configuration " + << pdb.config_orig << + info << "use --config-* to select suitable configuration"; + } + + // If we didn't get the available package corresponding to the + // selected package, look for any that satisfies the constraint. + // + if (dap == nullptr) + { + // And if we have no repository fragment to look in, then that + // means the package is an orphan (we delay this check until we + // actually need the repository fragment to allow orphans + // without prerequisites). + // + if (af == nullptr) + { + assert (dr == nullptr); // Should fail on the "silent" run. + + fail << "package " << pkg.available_name_version_db () + << " is orphaned" << + info << "explicitly upgrade it to a new version"; + } + + // We look for prerequisites only in the repositories of this + // package (and not in all the repositories of this + // configuration). At first this might look strange, but it + // also kind of makes sense: we only use repositories "approved" + // for this package version. Consider this scenario as an + // example: hello/1.0.0 and libhello/1.0.0 in stable and + // libhello/2.0.0 in testing. As a prerequisite of hello, which + // version should libhello resolve to? While one can probably + // argue either way, resolving it to 1.0.0 is the conservative + // choice and the user can always override it by explicitly + // building libhello. + // + // Note though, that if this is a test package, then its special + // test dependencies (main packages that refer to it) should be + // searched upstream through the complement repositories + // recursively, since the test packages may only belong to the + // main package's repository and its complements. + // + // @@ Currently we don't implement the reverse direction search + // for the test dependencies, effectively only supporting the + // common case where the main and test packages belong to the + // same repository. Will need to fix this eventually. + // + // Note that this logic (naturally) does not apply if the + // package is already selected by the user (see above). + // + // Also note that for the user-specified dependency version + // constraint we rely on the satisfying package version be + // present in repositories of the first dependent met. As a + // result, we may fail too early if such package version doesn't + // belong to its repositories, but belongs to the ones of some + // dependent that we haven't met yet. Can we just search all + // repositories for an available package of the appropriate + // version and just take it, if present? We could, but then + // which repository should we pick? The wrong choice can + // introduce some unwanted repositories and package versions + // into play. So instead, we will postpone collecting the + // problematic dependent, expecting that some other one will + // find the appropriate version in its repositories. + // + // For a system package we will try to find the available + // package that matches the constraint (preferable for the + // configuration negotiation machinery) and, if fail, fallback + // to picking the latest one just to make sure the package is + // recognized. An unrecognized package means the broken/stale + // repository (see below). + // + rp = find_available_one (dn, d.constraint, af); + + if (dap == nullptr && system && d.constraint) + rp = find_available_one (dn, nullopt, af); + + if (dap == nullptr) + { + if (dep_constr && !system && postponed_repo != nullptr) + { + // We shouldn't be called in the diag mode for the postponed + // package builds. + // + assert (dr == nullptr); + + l5 ([&]{trace << "rep-postpone dependent " + << pkg.available_name_version_db () + << " due to dependency " << dp + << " and user-specified constraint " + << *dep_constr;}); + + postponed_repo->insert (&pkg); + return precollect_result (true /* postpone */); + } + + if (dr != nullptr) + { + *dr << error; + + // Issue diagnostics differently based on the presence of + // available packages for the unsatisfied dependency. + // + // Note that there can't be any stubs, since they satisfy + // any constraint and we won't be here if they were. + // + vector> aps ( + find_available (dn, nullopt /* version_constraint */, af)); + + if (!aps.empty ()) + { + *dr << "unable to satisfy dependency constraint (" << dn; + + // We need to be careful not to print the wildcard-based + // constraint. + // + if (d.constraint && + (!dep_constr || !wildcard (*dep_constr))) + *dr << ' ' << *d.constraint; + + *dr << ") of package " << nm << pdb << + info << "available " << dn << " versions:"; + + for (const shared_ptr& ap: aps) + *dr << ' ' << ap->version; + } + else + { + *dr << "no package available for dependency " << dn + << " of package " << nm << pdb; + } + + // Avoid printing this if the dependent package is external + // since it's more often confusing than helpful (they are + // normally not fetched manually). + // + if (!af->location.empty () && + !af->location.directory_based () && + (!dep_constr || system)) + *dr << info << "repository " << af->location << " appears " + << "to be broken" << + info << "or the repository state could be stale" << + info << "run 'bpkg rep-fetch' to update"; + } + + return precollect_result (false /* postpone */); + } + + // If all that's available is a stub then we need to make sure + // the package is present in the system repository and it's + // version satisfies the constraint. If a source package is + // available but there is a system package specified on the + // command line and it's version satisfies the constraint then + // the system package should be preferred. To recognize such a + // case we just need to check if the authoritative system + // version is set and it satisfies the constraint. If the + // corresponding system package is non-optional it will be + // preferred anyway. + // + if (dap->stub ()) + { + // Note that the constraint can safely be printed as it can't + // be a wildcard (produced from the user-specified dependency + // version constraint). If it were, then the system version + // wouldn't be NULL and would satisfy itself. + // + if (dap->system_version (*ddb) == nullptr) + { + if (dr != nullptr) + *dr << error << "dependency " << d << " of package " + << nm << " is not available in source" << + info << "specify ?sys:" << dn << " if it is available " + << "from the system"; + + return precollect_result (false /* postpone */); + } + + if (!satisfies (*dap->system_version (*ddb), d.constraint)) + { + if (dr != nullptr) + *dr << error << "dependency " << d << " of package " + << nm << " is not available in source" << + info << package_string (dn, + *dap->system_version (*ddb), + true /* system */) + << " does not satisfy the constrains"; + + return precollect_result (false /* postpone */); + } + + system = true; + } + else + { + auto p (dap->system_version_authoritative (*ddb)); + + if (p.first != nullptr && + p.second && // Authoritative. + satisfies (*p.first, d.constraint)) + system = true; + } + } + + // If the dependency package of a different version is already + // being built, then we also need to make sure that we will be + // able to choose one of them (either existing or new) which + // satisfies all the dependents. + // + // Note that collect_build() also performs this check but + // postponing it till then can end up in failing instead of + // selecting some other dependency alternative. + // + assert (dap != nullptr); // Otherwise we would fail earlier. + + if (i != map_.end () && d.constraint) + { + const build_package& bp (i->second.package); + + if (bp.action && *bp.action == build_package::build) + { + const version& v1 (system + ? *dap->system_version (*ddb) + : dap->version); + + const version& v2 (bp.available_version ()); + + if (v1 != v2) + { + using constraint_type = build_package::constraint_type; + + constraint_type c1 {pdb, nm.string (), *d.constraint}; + + if (!satisfies (v2, c1.value)) + { + for (const constraint_type& c2: bp.constraints) + { + if (!satisfies (v1, c2.value)) + { + if (dr != nullptr) + { + const package_name& n (d.name); + const string& d1 (c1.dependent); + const string& d2 (c2.dependent); + + *dr << error << "unable to satisfy constraints on " + << "package " << n << + info << d2 << c2.db << " depends on (" << n << ' ' + << c2.value << ")" << + info << d1 << c1.db << " depends on (" << n << ' ' + << c1.value << ")" << + info << "available " + << bp.available_name_version () << + info << "available " + << package_string (n, v1, system) << + info << "explicitly specify " << n << " version " + << "to manually satisfy both constraints"; + } + + return precollect_result (false /* postpone */); + } + } + } + } + } + } + + bool ru (i != map_.end () || dsp != nullptr); + + if (!ru) + reused = false; + + r.push_back (prebuild {d, + *ddb, + move (dsp), + move (dap), + move (rp.second), + system, + specified, + force, + ru}); + } + + return precollect_result (move (r), reused); + }; + + // Try to collect the previously collected pre-builds. + // + // Return false if the dependent has configuration clauses and is + // postponed until dependencies configuration negotiation. + // + auto collect = [&options, + &pkg, + &pdb, + &nm, + &pk, + &fdb, + &rpt_depts, + &apc, + initial_collection, + &replaced_vers, + &dep_chain, + postponed_repo, + postponed_alts, + &postponed_deps, + &postponed_cfgs, + &postponed_poss, + &di, + reeval, + &reeval_pos, + &reevaluated, + &fail_reeval, + &trace, + this] + (const dependency_alternative& da, + size_t dai, + prebuilds&& bs) + { + // Dependency alternative position. + // + pair dp (di + 1, dai + 1); + + if (reeval && + dp.first == reeval_pos.first && + dp.second != reeval_pos.second) + fail_reeval (); + + postponed_configuration::packages cfg_deps; + + for (prebuild& b: bs) + { + build_package bp { + build_package::build, + b.db, + b.selected, + b.available, + move (b.repository_fragment), + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + b.system, + false, // Keep output directory. + false, // Disfigure (from-scratch reconf). + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {pk}, // Required by (dependent). + true, // Required by dependents. + 0}; // State flags. + + const optional& constraint ( + b.dependency.constraint); + + // Add our constraint, if we have one. + // + // Note that we always add the constraint implied by the dependent. + // The user-implied constraint, if present, will be added when + // merging from the pre-entered entry. So we will have both + // constraints for completeness. + // + if (constraint) + bp.constraints.emplace_back (pdb, nm.string (), *constraint); + + // Now collect this prerequisite. If it was actually collected + // (i.e., it wasn't already there) and we are forcing a downgrade + // or upgrade, then refuse for a held version, warn for a held + // package, and print the info message otherwise, unless the + // verbosity level is less than two. + // + // Note though that while the prerequisite was collected it could + // have happen because it is an optional package and so not being + // pre-collected earlier. Meanwhile the package was specified + // explicitly and we shouldn't consider that as a + // dependency-driven up/down-grade enforcement. + // + // Here is an example of the situation we need to handle properly: + // + // repo: foo/2(->bar/2), bar/0+1 + // build sys:bar/1 + // build foo ?sys:bar/2 + // + // Pass the function which verifies we don't try to force + // up/downgrade of the held version and makes sure we don't print + // the dependency chain if replace_version will be thrown. + // + // Also note that we rely on "small function object" optimization + // here. + // + struct + { + const build_package& dependent; + const prebuild& prerequisite; + } dpn {pkg, b}; + + const function verify ( + [&dpn, &dep_chain] (const build_package& p, bool scratch) + { + const prebuild& prq (dpn.prerequisite); + const build_package& dep (dpn.dependent); + + if (prq.force && !prq.specified_dependency) + { + // Fail if the version is held. Otherwise, warn if the + // package is held. + // + bool f (prq.selected->hold_version); + bool w (!f && prq.selected->hold_package); + + // Note that there is no sense to warn or inform the user if + // we are about to start re-collection from scratch. + // + // @@ It seems that we may still warn/inform multiple times + // about the same package if we start from scratch. The + // intermediate diagnostics can probably be irrelevant to + // the final result. + // + // Perhaps what we should do is queue the diagnostics and + // then, if the run is not scratched, issues it. And if + // it is scratched, then drop it. + // + if (f || ((w || verb >= 2) && !scratch)) + { + const version& av (p.available_version ()); + + bool u (av > prq.selected->version); + bool c (prq.dependency.constraint); + + diag_record dr; + + (f ? dr << fail : + w ? dr << warn : + dr << info) + << "package " << dep.name () << dep.db + << " dependency on " << (c ? "(" : "") << prq.dependency + << (c ? ")" : "") << " is forcing " + << (u ? "up" : "down") << "grade of " << *prq.selected + << prq.db << " to "; + + // Print both (old and new) package names in full if the + // system attribution changes. + // + if (prq.selected->system ()) + dr << p.available_name_version (); + else + dr << av; // Can't be a system version so is never wildcard. + + if (prq.selected->hold_version) + dr << info << "package version " << *prq.selected + << prq.db<< " is held"; + + if (f) + dr << info << "explicitly request version " + << (u ? "up" : "down") << "grade to continue"; + } + } + + // Don't print the "while satisfying..." chain if we are about + // to re-collect the packages. + // + if (scratch) + dep_chain.clear (); + }); + + // Note: non-recursive. + // + build_package* p ( + collect_build (options, + move (bp), + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + postponed_cfgs, + nullptr /* dep_chain */, + nullptr /* postponed_repo */, + nullptr /* postponed_alts */, + nullptr /* postponed_deps */, + nullptr /* postponed_poss */, + verify)); + + package_key dpk (b.db, b.available->id.name); + + // Do not collect prerequisites recursively for dependent + // re-evaluation. Instead, if the re-evaluation position is + // reached, collect the dependency packages to add them to the + // existing dependent's cluster. + // + if (reeval) + { + if (dp == reeval_pos) + cfg_deps.push_back (move (dpk)); + + continue; + } + + // Do not recursively collect a dependency of a dependent with + // configuration clauses, which could be this or some other + // (indicated by the presence in postponed_deps) dependent. In the + // former case if the prerequisites were prematurely collected, + // throw postpone_dependency. + // + // Note that such a dependency will be recursively collected + // directly right after the configuration negotiation (rather than + // via the dependent). + // + bool collect_prereqs (p != nullptr); + + { + build_package* bp (entered_build (dpk)); + assert (bp != nullptr); + + if (da.prefer || da.require) + { + // Indicate that the dependent with configuration clauses is + // present. + // + { + auto i (postponed_deps.find (dpk)); + + // Do not override postponements recorded during postponed + // collection phase with those recorded during initial + // phase. + // + if (i == postponed_deps.end ()) + { + postponed_deps.emplace (dpk, + postponed_dependency { + false /* without_config */, + true /* with_config */, + initial_collection}); + } + else + i->second.with_config = true; + } + + // Prematurely collected before we saw any config clauses. + // + if (bp->recursive_collection && + postponed_cfgs.find_dependency (dpk) == nullptr) + { + l5 ([&]{trace << "cannot cfg-postpone dependency " + << bp->available_name_version_db () + << " of dependent " + << pkg.available_name_version_db () + << " (collected prematurely), " + << "throwing postpone_dependency";}); + + // Don't print the "while satisfying..." chain. + // + dep_chain.clear (); + + throw postpone_dependency (move (dpk)); + } + + // Postpone until (re-)negotiation. + // + l5 ([&]{trace << "cfg-postpone dependency " + << bp->available_name_version_db () + << " of dependent " + << pkg.available_name_version_db ();}); + + cfg_deps.push_back (move (dpk)); + + collect_prereqs = false; + } + else + { + // Indicate that the dependent without configuration clauses + // is also present. + // + auto i (postponed_deps.find (dpk)); + if (i != postponed_deps.end ()) + { + l5 ([&]{trace << "dep-postpone dependency " + << bp->available_name_version_db () + << " of dependent " + << pkg.available_name_version_db ();}); + + i->second.wout_config = true; + + collect_prereqs = false; + } + else + { + l5 ([&]{trace << "no cfg-clause for dependency " + << bp->available_name_version_db () + << " of dependent " + << pkg.available_name_version_db ();}); + } + } + } + + if (collect_prereqs) + collect_build_prerequisites (options, + *p, + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + dep_chain, + postponed_repo, + postponed_alts, + 0 /* max_alt_index */, + postponed_deps, + postponed_cfgs, + postponed_poss); + } + + // If this dependent has any dependencies with configurations + // clauses, then we need to deal with that. + // + // This is what we refer to as the "up-negotiation" where we + // negotiate the configuration of dependents that could not be + // postponed and handled all at once during "initial negotiation" in + // collect_build_postponed(). + // + if (!cfg_deps.empty ()) + { + // Re-evaluation is a special case (it happens during cluster + // negotiation; see collect_build_postponed()). + // + if (reeval) + { + reevaluated = true; + + // Note: the dependent may already exist in the cluster with a + // subset of dependencies. + // + postponed_configuration& cfg ( + postponed_cfgs.add (pk, + true /* existing */, + dp, + cfg_deps).first); + + // Can we merge clusters as a result? Seems so. + // + // - Simple case is if the cluster(s) being merged are not + // negotiated. Then perhaps we could handle this via the same + // logic that handles the addition of extra dependencies. + // + // - For the complex case, perhaps just making the resulting + // cluster shadow and rolling back, just like in the other + // case (non-existing dependent). + // + // Note: this is a special case of the below more general logic. + // + // Also note that we can distinguish the simple case by the fact + // that the resulting cluster is not negotiated. Note however, + // that in this case it is guaranteed that all the involved + // clusters will be merged into the cluster which the being + // re-evaluated dependent belongs to since this cluster (while + // not being negotiated) already has non-zero depth (see + // collect_build_postponed() for details). + // + assert (cfg.depth != 0); + + if (cfg.negotiated) + { + l5 ([&]{trace << "re-evaluating dependent " + << pkg.available_name_version_db () + << " involves negotiated configurations and " + << "results in " << cfg << ", throwing " + << "merge_configuration";}); + + // Don't print the "while satisfying..." chain. + // + dep_chain.clear (); + + throw merge_configuration {cfg.depth}; + } + + l5 ([&]{trace << "re-evaluating dependent " + << pkg.available_name_version_db () + << " results in " << cfg;}); + + return false; + } + + // As a first step add this dependent/dependencies to one of the + // new/existing postponed_configuration clusters, which could + // potentially cause some of them to be merged. Here are the + // possibilities and what we should do in each case. + // + // 1. Got added to a new cluster -- this dependent got postponed + // and we return false. + // + // 2. Got added to an existing non-yet-negotiated cluster (which + // could potentially involve merging a bunch of them) -- ditto. + // + // 3. Got added to an existing already-[being]-negotiated cluster + // (which could potentially involve merging a bunch of them, + // some negotiated, some being negotiated, and some not yet + // negotiated) -- see below logic. + // + // Note that if a dependent is postponed, it will be recursively + // recollected right after the configuration negotiation. + + // Note: don't move the argument from since may be needed for + // constructing exception. + // + pair> r ( + postponed_cfgs.add (pk, false /* existing */, dp, cfg_deps)); + + postponed_configuration& cfg (r.first); + + if (cfg.depth == 0) + return false; // Cases (1) or (2). + else + { + // Case (3). + // + // There is just one complication: + // + // If all the merged clusters are already negotiated, then all + // is good: all the dependencies in cfg_deps have been collected + // recursively as part of the configuration negotiation (because + // everything in this cluster is already negotiated) and we can + // return true (no need to postpone any further steps). + // + // But if we merged clusters not yet negotiated, or, worse, + // being in the middle of negotiation, then we need to get this + // merged cluster into the fully negotiated state. The way we do + // it is by throwing merge_configuration (see below). + // + // When we are back here after throwing merge_configuration, + // then all the clusters have been pre-merged and our call to + // add() shouldn't have added any new cluster. In this case the + // cluster can either be already negotiated or being negotiated + // and we can proceed as in the "everything is negotiated case" + // above (we just need to get the the dependencies that we care + // about into the recursively collected state). + // + + // To recap, r.second values mean: + // + // absent -- shadow cluster-based merge is/being negotiated + // false -- some non or being negotiated + // true -- all have been negotiated + // + if (r.second && !*r.second) + { + // The partially negotiated case. + // + // Handling this in a straightforward way is not easy due to + // the being negotiated cases -- we have code up the stack + // that is in the middle of the negotiation logic. + // + // Another idea is to again throw to the outer try/catch frame + // (thus unwinding all the being negotiated code) and complete + // the work there. The problem with this approach is that + // without restoring the state we may end up with unrelated + // clusters that will have no corresponding try-catch frames + // (because we may unwind them in the process). + // + // So the approach we will use is the "shadow" idea for + // merging clusters. Specifically, we throw + // merge_configuration to the outer try/catch. At the catch + // site we make the newly merged cluster a shadow of the + // restored cluster and retry the same steps similar to + // retry_configuration. As we redo these steps, we consult the + // shadow cluster and if the dependent/dependency entry is + // there, then instead of adding it to another (new/existing) + // cluster that would later be merged into this non-shadow + // cluster, we add it directly to the non-shadow cluster + // (potentially merging other cluster which it feels like by + // definition should all be already fully negotiated). The end + // result is that once we reach this point again, there will + // be nothing to merge. + // + // The shadow check is part of postponed_configs::add(). + // + l5 ([&]{trace << "cfg-postponing dependent " + << pkg.available_name_version_db () + << " merges non-negotiated and/or being " + << "negotiated configurations in and results in " + << cfg << ", throwing merge_configuration";}); + + // Don't print the "while satisfying..." chain. + // + dep_chain.clear (); + + throw merge_configuration {cfg.depth}; + } + + // Up-negotiate the configuration and if it has changed, throw + // retry_configuration to the try/catch frame corresponding to + // the negotiation of the outermost merged cluster in order to + // retry the same steps (potentially refining the configuration + // as we go along) and likely (but not necessarily) ending up + // here again, at which point we up-negotiate again with the + // expectation that the configuration won't change (but if it + // does, then we throw again and do another refinement pass). + // + // In a sense, semantically, we should act like a one more + // iteration of the initial negotiation loop with the exception + // acting like a request to restart the refinement process from + // the beginning. + // + bool changed; + { + // Similar to initial negotiation, resolve package skeletons + // for this dependent and its dependencies. + // + package_skeleton* dept; + { + build_package* b (entered_build (pk)); + assert (b != nullptr && b->skeleton); + dept = &*b->skeleton; + } + + // If a dependency has already been recursively collected, + // then we can no longer call reload_defaults() or + // verify_sensible() on its skeleton. We could reset it, but + // then we wouldn't be able to continue using it if + // negotiate_configuration() below returns false. So it seems + // the most sensible approach is to make a temporary copy and + // reset that. + // + small_vector, 1> depcs; + forward_list depcs_storage; // Ref stability. + { + depcs.reserve (cfg_deps.size ()); + for (const package_key& pk: cfg_deps) + { + build_package* b (entered_build (pk)); + assert (b != nullptr); + + package_skeleton* depc; + if (b->recursive_collection) + { + assert (b->skeleton); + + depcs_storage.push_front (*b->skeleton); + depc = &depcs_storage.front (); + depc->reset (); + } + else + depc = &(b->skeleton + ? *b->skeleton + : b->init_skeleton (options)); + + depcs.push_back (*depc); + } + } + + changed = negotiate_configuration ( + cfg.dependency_configurations, *dept, dp, depcs); + } + + // If the configuration hasn't changed, then we carry on. + // Otherwise, retry the negotiation from the beginning to + // refine the resulting configuration (see the catch block + // for retry_configuration). + // + if (changed) + { + l5 ([&]{trace << "cfg-postponing dependent " + << pkg.available_name_version_db () + << " involves (being) negotiated configurations " + << "and results in " << cfg + << ", throwing retry_configuration";}); + + // Don't print the "while satisfying..." chain. + // + dep_chain.clear (); + + throw retry_configuration {cfg.depth, move (pk)}; + } + + l5 ([&]{trace << "configuration for cfg-postponed " + << "dependencies of dependent " + << pkg.available_name_version_db () << " is " + << (r.second ? "" : "shadow-") << "negotiated";}); + + // Note that even in the fully negotiated case we may still add + // extra dependencies to this cluster which we still need to + // configure and recursively collect before indicating to the + // caller (returning true) that we are done with this depends + // value and the dependent is not postponed. + // + for (const package_key& p: cfg_deps) + { + build_package* b (entered_build (p)); + assert (b != nullptr); + + // Reconfigure the configured dependencies (see + // collect_build_postponed() for details). + // + if (b->selected != nullptr && + b->selected->state == package_state::configured) + b->flags |= build_package::adjust_reconfigure; + + if (!b->recursive_collection) + { + l5 ([&]{trace << "collecting cfg-postponed dependency " + << b->available_name_version_db () + << " of dependent " + << pkg.available_name_version_db ();}); + + // Similar to the inital negotiation case, verify and set + // the dependent configuration for this dependency. + // + { + assert (b->skeleton); // Should have been init'ed above. + + const package_configuration& pc ( + cfg.dependency_configurations[p]); + + pair pr (b->skeleton->available != nullptr + ? b->skeleton->verify_sensible (pc) + : make_pair (true, string ())); + + if (!pr.first) + { + diag_record dr (fail); + dr << "unable to negotiate sensible configuration for " + << "dependency " << p << '\n' + << " " << pr.second; + + dr << info << "negotiated configuration:\n"; + pc.print (dr, " "); + } + + b->skeleton->dependent_config (pc); + } + + collect_build_prerequisites (options, + *b, + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + dep_chain, + postponed_repo, + postponed_alts, + 0 /* max_alt_index */, + postponed_deps, + postponed_cfgs, + postponed_poss); + } + else + l5 ([&]{trace << "dependency " + << b->available_name_version_db () + << " of dependent " + << pkg.available_name_version_db () + << " is already (being) recursively " + << "collected, skipping";}); + } + + return true; + } + } + + return true; + }; + + // Select a dependency alternative, copying it alone into the + // resulting dependencies list and evaluating its reflect clause, if + // present. + // + bool selected (false); + auto select = [&sdeps, &salts, &sdas, &skel, di, &selected] + (const dependency_alternative& da, size_t dai) + { + assert (sdas.empty ()); + + // Avoid copying enable/reflect not to evaluate them repeatedly. + // + sdas.emplace_back (nullopt /* enable */, + nullopt /* reflect */, + da.prefer, + da.accept, + da.require, + da /* dependencies */); + + sdeps.push_back (move (sdas)); + salts.push_back (dai); + + if (da.reflect) + skel.evaluate_reflect (*da.reflect, make_pair (di, dai)); + + selected = true; + }; + + // Postpone the prerequisite builds collection, optionally inserting + // the package to the postponements set (can potentially already be + // there) and saving the enabled alternatives. + // + auto postpone = [&pkg, &edas, &postponed] + (postponed_packages* postpones) + { + if (postpones != nullptr) + postpones->insert (&pkg); + + pkg.postponed_dependency_alternatives = move (edas); + postponed = true; + }; + + // Iterate over the enabled dependencies and try to select a + // satisfactory alternative. + // + // If the package is already configured as source and is not + // up/downgraded, then we will try to resolve its dependencies to the + // current prerequisites. To achieve this we will first try to select + // an alternative in the "recreate dependency decisions" mode, + // filtering out all the alternatives where dependencies do not all + // belong to the list of current prerequisites. If we end up with no + // alternative selected, then we retry in the "make dependency + // decisions" mode and select the alternative ignoring the current + // prerequisites. + // + // Note though, that if we are re-evaluating an existing dependent + // then we fail if we didn't succeed in the "recreate dependency + // decisions" mode. + // + const package_prerequisites* prereqs (src_conf && !ud + ? &sp->prerequisites + : nullptr); + + // During the dependent re-evaluation we always try to reproduce the + // existing setup. + // + assert (!reeval || prereqs != nullptr); + + for (;;) + { + // The index and pre-collection result of the first satisfactory + // alternative. + // + optional> first_alt; + + // The number of satisfactory alternatives. + // + size_t alts_num (0); + + for (size_t i (0); i != edas.size (); ++i) + { + const dependency_alternative& da (edas[i].first); + + precollect_result r (precollect (da, das.buildtime, prereqs)); + + // If we didn't come up with satisfactory dependency builds, then + // skip this alternative and try the next one, unless the + // collecting is postponed in which case just bail out. + // + // Should we skip alternatives for which we are unable to satisfy + // the constraint? On one hand, this could be a user error: there + // is no package available from dependent's repositories that + // satisfies the constraint. On the other hand, it could be that + // it's other dependent's constraints that we cannot satisfy + // together with others. And in this case we may want some other + // alternative. Consider, as an example, something like this: + // + // depends: libfoo >= 2.0.0 | libfoo >= 1.0.0 libbar + // + if (!r.builds) + { + if (r.repo_postpone) + { + if (reeval) + fail_reeval (); + + postpone (nullptr); // Already inserted into postponed_repo. + break; + } + + continue; + } + + ++alts_num; + + // Note that when we see the first satisfactory alternative, we + // don't know yet if it is a single alternative or the first of + // the (multiple) true alternatives (those are handled + // differently). Thus, we postpone its processing until the second + // satisfactory alternative is encountered or the end of the + // alternatives list is reached. + // + if (!first_alt) + { + first_alt = make_pair (i, move (r)); + continue; + } + + // Try to select a true alternative, returning true if the + // alternative is selected or the selection is postponed. Return + // false if the alternative is ignored (not postponed and not all + // of it dependencies are reused). + // + auto try_select = [postponed_alts, &max_alt_index, + &edas, &pkg, + reeval, + &trace, + &postpone, &collect, &select] + (size_t index, precollect_result&& r) + { + const auto& eda (edas[index]); + const dependency_alternative& da (eda.first); + size_t dai (eda.second); + + // Postpone the collection if the alternatives maximum index is + // reached. + // + if (postponed_alts != nullptr && index >= max_alt_index) + { + // For a dependent re-evaluation max_alt_index is expected to + // be max size_t. + // + assert (!reeval); + + l5 ([&]{trace << "alt-postpone dependent " + << pkg.available_name_version_db () + << " since max index is reached: " << index << + info << "dependency alternative: " << da;}); + + postpone (postponed_alts); + return true; + } + + // Select this alternative if all its dependencies are reused + // and do nothing about it otherwise. + // + if (r.reused) + { + // On the diagnostics run there shouldn't be any alternatives + // that we could potentially select. + // + assert (postponed_alts != nullptr); + + if (!collect (da, dai, move (*r.builds))) + { + postpone (nullptr); // Already inserted into postponed_cfgs. + return true; + } + + select (da, dai); + + // Make sure no more true alternatives are selected during + // this function call unless we are re-evaluating a dependent. + // + if (!reeval) + max_alt_index = 0; + + return true; + } + else + return false; + }; + + // If we encountered the second satisfactory alternative, then + // this is the "multiple true alternatives" case. In this case we + // also need to process the first satisfactory alternative, which + // processing was delayed. + // + if (alts_num == 2) + { + assert (first_alt); + + if (try_select (first_alt->first, move (first_alt->second))) + break; + } + + if (try_select (i, move (r))) + break; + + // Not all of the alternative dependencies are reused, so go to + // the next alternative. + } + + // Bail out if the collection is postponed for any reason. + // + if (postponed) + break; + + // Select the single satisfactory alternative (regardless of its + // dependencies reuse). + // + if (!selected && alts_num == 1) + { + assert (first_alt && first_alt->second.builds); + + const auto& eda (edas[first_alt->first]); + const dependency_alternative& da (eda.first); + size_t dai (eda.second); + + if (!collect (da, dai, move (*first_alt->second.builds))) + { + postpone (nullptr); // Already inserted into postponed_cfgs. + break; + } + + select (da, dai); + } + + // If an alternative is selected, then we are done. + // + if (selected) + break; + + // Fail or postpone the collection if no alternative is selected, + // unless we are re-evaluating a dependent or are in the "recreate + // dependency decisions" mode. In the latter case fail for + // re-evaluation and fall back to the "make dependency decisions" + // mode and retry otherwise. + // + if (prereqs != nullptr) + { + if (reeval) + fail_reeval (); + + prereqs = nullptr; + continue; + } + + // Issue diagnostics and fail if there are no satisfactory + // alternatives. + // + if (alts_num == 0) + { + diag_record dr; + for (const auto& da: edas) + precollect (da.first, das.buildtime, nullptr /* prereqs */, &dr); + + assert (!dr.empty ()); + + dr.flush (); + throw failed (); + } + + // Issue diagnostics and fail if there are multiple alternatives + // with non-reused dependencies, unless the failure needs to be + // postponed. + // + assert (alts_num > 1); + + if (postponed_alts != nullptr) + { + if (verb >= 5) + { + diag_record dr (trace); + dr << "alt-postpone dependent " + << pkg.available_name_version_db () + << " due to ambiguous alternatives"; + + for (const auto& da: edas) + dr << info << "alternative: " << da.first; + } + + postpone (postponed_alts); + break; + } + + diag_record dr (fail); + dr << "unable to select dependency alternative for package " + << pkg.available_name_version_db () << + info << "explicitly specify dependency packages to manually " + << "select the alternative"; + + for (const auto& da: edas) + { + precollect_result r ( + precollect (da.first, das.buildtime, nullptr /* prereqs */)); + + if (r.builds) + { + assert (!r.reused); // We shouldn't be failing otherwise. + + dr << info << "alternative:"; + + // Only print the non-reused dependencies, which needs to be + // explicitly specified by the user. + // + for (const prebuild& b: *r.builds) + { + if (!b.reused) + dr << ' ' << b.dependency.name; + } + } + } + } + + if (postponed) + break; + } + + if (reeval) + { + if (!reevaluated) + fail_reeval (); + + assert (postponed); + } + + dep_chain.pop_back (); + + l5 ([&]{trace << (!postponed ? "end " : + reeval ? "re-evaluated " : + "postpone ") + << pkg.available_name_version_db ();}); + } + + void + collect_build_prerequisites (const pkg_build_options& o, + database& db, + const package_name& name, + const function& fdb, + const repointed_dependents& rpt_depts, + const function& apc, + bool initial_collection, + replaced_versions& replaced_vers, + postponed_packages& postponed_repo, + postponed_packages& postponed_alts, + size_t max_alt_index, + postponed_dependencies& postponed_deps, + postponed_configurations& postponed_cfgs, + postponed_positions& postponed_poss) + { + auto mi (map_.find (db, name)); + assert (mi != map_.end ()); + + build_package_refs dep_chain; + + collect_build_prerequisites (o, + mi->second.package, + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + dep_chain, + &postponed_repo, + &postponed_alts, + max_alt_index, + postponed_deps, + postponed_cfgs, + postponed_poss); + } + + // Collect the repointed dependents and their replaced prerequisites, + // recursively. + // + // If a repointed dependent is already pre-entered or collected with an + // action other than adjustment, then just mark it for reconfiguration + // unless it is already implied. Otherwise, collect the package build with + // the repoint sub-action and reconfigure adjustment flag. + // + void + collect_repointed_dependents ( + const pkg_build_options& o, + const repointed_dependents& rpt_depts, + replaced_versions& replaced_vers, + postponed_packages& postponed_repo, + postponed_packages& postponed_alts, + postponed_dependencies& postponed_deps, + postponed_configurations& postponed_cfgs, + postponed_positions& postponed_poss, + const function& fdb, + const function& apc) + { + for (const auto& rd: rpt_depts) + { + database& db (rd.first.db); + const package_name& nm (rd.first.name); + + auto i (map_.find (db, nm)); + if (i != map_.end ()) + { + build_package& b (i->second.package); + + if (!b.action || *b.action != build_package::adjust) + { + if (!b.action || + (*b.action != build_package::drop && !b.reconfigure ())) + b.flags |= build_package::adjust_reconfigure; + + continue; + } + } + + shared_ptr sp (db.load (nm)); + + // The repointed dependent can be an orphan, so just create the + // available package from the selected package. + // + auto rp (make_available_fragment (o, db, sp)); + + // Add the prerequisite replacements as the required-by packages. + // + set required_by; + for (const auto& prq: rd.second) + { + if (prq.second) // Prerequisite replacement? + { + const package_key& pk (prq.first); + required_by.emplace (pk.db, pk.name); + } + } + + build_package p { + build_package::build, + db, + sp, + move (rp.first), + move (rp.second), + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + sp->system (), + false, // Keep output directory. + false, // Disfigure (from-scratch reconf). + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + move (required_by), // Required by (dependencies). + false, // Required by dependents. + build_package::adjust_reconfigure | build_package::build_repoint}; + + build_package_refs dep_chain; + + // Note: recursive. + // + collect_build (o, + move (p), + fdb, + rpt_depts, + apc, + true /* initial_collection */, + replaced_vers, + postponed_cfgs, + &dep_chain, + &postponed_repo, + &postponed_alts, + &postponed_deps, + &postponed_poss); + } + } + + // Collect the package being dropped. + // + // Add entry to replaced_vers and throw replace_version if the existing + // version needs to be dropped but this can't be done in-place (see + // replaced_versions for details). + // + void + collect_drop (const pkg_build_options& options, + database& db, + shared_ptr sp, + replaced_versions& replaced_vers) + { + tracer trace ("collect_drop"); + + package_key pk (db, sp->name); + + // If there is an entry for building specific version of the package + // (the available member is not NULL), then it wasn't created to prevent + // out drop (see replaced_versions for details). This rather mean that + // the replacement version is not being built anymore due to the plan + // refinement. Thus, just erase the entry in this case and continue. + // + auto vi (replaced_vers.find (pk)); + if (vi != replaced_vers.end () && !vi->second.replaced) + { + replaced_version& v (vi->second); + const shared_ptr& ap (v.available); + + if (ap != nullptr) + { + if (verb >= 5) + { + bool s (v.system); + const version& av (s ? *ap->system_version (db) : ap->version); + + l5 ([&]{trace << "erase version replacement for " + << package_string (ap->id.name, av, s) << db;}); + } + + replaced_vers.erase (vi); + vi = replaced_vers.end (); // Keep it valid for the below check. + } + else + v.replaced = true; + } + + build_package p { + build_package::drop, + db, + move (sp), + nullptr, + nullptr, + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + false, // System package. + false, // Keep output directory. + false, // Disfigure (from-scratch reconf). + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {}, // Required by. + false, // Required by dependents. + 0}; // State flags. + + auto i (map_.find (pk)); + + if (i != map_.end ()) + { + build_package& bp (i->second.package); + + if (bp.available != nullptr) + { + // Similar to the version replacement in collect_build(), see if + // in-place drop is possible (no dependencies, etc) and set scratch + // to false if that's the case. + // + bool scratch (true); + + // While checking if the package has any dependencies skip the + // toolchain build-time dependencies since they should be quite + // common. + // + if (!has_dependencies (options, bp.available->dependencies)) + scratch = false; + + l5 ([&]{trace << bp.available_name_version_db () + << " package version needs to be replaced " + << (!scratch ? "in-place " : "") << "with drop";}); + + if (scratch) + { + if (vi != replaced_vers.end ()) + vi->second = replaced_version (); + else + replaced_vers.emplace (move (pk), replaced_version ()); + + throw replace_version (); + } + } + + // Overwrite the existing (possibly pre-entered, adjustment, or + // repoint) entry. + // + l4 ([&]{trace << "overwrite " << pk;}); + + bp = move (p); + } + else + { + l4 ([&]{trace << "add " << pk;}); + + map_.emplace (move (pk), data_type {end (), move (p)}); + } + } + + // Collect the package being unheld. + // + void + collect_unhold (database& db, const shared_ptr& sp) + { + auto i (map_.find (db, sp->name)); + + // Currently, it must always be pre-entered. + // + assert (i != map_.end ()); + + build_package& bp (i->second.package); + + if (!bp.action) // Pre-entered. + { + build_package p { + build_package::adjust, + db, + sp, + nullptr, + nullptr, + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + sp->system (), + false, // Keep output directory. + false, // Disfigure (from-scratch reconf). + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {}, // Required by. + false, // Required by dependents. + build_package::adjust_unhold}; + + p.merge (move (bp)); + bp = move (p); + } + else + bp.flags |= build_package::adjust_unhold; + } + + void + collect_build_postponed (const pkg_build_options& o, + replaced_versions& replaced_vers, + postponed_packages& postponed_repo, + postponed_packages& postponed_alts, + postponed_dependencies& postponed_deps, + postponed_configurations& postponed_cfgs, + strings& postponed_cfgs_history, + postponed_positions& postponed_poss, + const function& fdb, + const repointed_dependents& rpt_depts, + const function& apc, + postponed_configuration* pcfg = nullptr) + { + // Snapshot of the package builds collection state. + // + // Note: should not include postponed_cfgs_history. + // + class snapshot + { + public: + snapshot (const build_packages& pkgs, + const postponed_packages& postponed_repo, + const postponed_packages& postponed_alts, + const postponed_dependencies& postponed_deps, + const postponed_configurations& postponed_cfgs) + : pkgs_ (pkgs), + postponed_deps_ (postponed_deps), + postponed_cfgs_ (postponed_cfgs) + { + auto save = [] (vector& d, + const postponed_packages& s) + { + d.reserve (s.size ()); + + for (const build_package* p: s) + d.emplace_back (p->db, p->name ()); + }; + + save (postponed_repo_, postponed_repo); + save (postponed_alts_, postponed_alts); + } + + void + restore (build_packages& pkgs, + postponed_packages& postponed_repo, + postponed_packages& postponed_alts, + postponed_dependencies& postponed_deps, + postponed_configurations& postponed_cfgs) + { + pkgs = move (pkgs_); + postponed_cfgs = move (postponed_cfgs_); + postponed_deps = move (postponed_deps_); + + auto restore = [&pkgs] (postponed_packages& d, + const vector& s) + { + d.clear (); + + for (const package_key& p: s) + { + build_package* b (pkgs.entered_build (p)); + assert (b != nullptr); + d.insert (b); + } + }; + + restore (postponed_repo, postponed_repo_); + restore (postponed_alts, postponed_alts_); + } + + private: + // Note: try to use vectors instead of sets for storage to save + // memory. We could probably optimize this some more if + // necessary (there are still sets/maps inside). + // + build_packages pkgs_; + vector postponed_repo_; + vector postponed_alts_; + postponed_dependencies postponed_deps_; + postponed_configurations postponed_cfgs_; + }; + + // This exception is thrown if negotiation of the current cluster needs + // to be skipped until later. This is normally required if this cluster + // contains some existing dependent which needs to be re-evaluated to a + // dependency position greater than some other not yet negotiated + // cluster will re-evaluate this dependent to. Sometimes this another + // cluster yet needs to be created in which case the exception carries + // the information required for that (see the postponed_position's + // replace flag for details). + // + struct skip_configuration + { + optional dependent; + pair new_position; + + skip_configuration () = default; + + skip_configuration (existing_dependent&& d, pair n) + : dependent (move (d)), new_position (n) {} + }; + + size_t depth (pcfg != nullptr ? pcfg->depth : 0); + + string t ("collect_build_postponed (" + to_string (depth) + ")"); + tracer trace (t.c_str ()); + + string trace_suffix (verb >= 5 && pcfg != nullptr + ? " " + pcfg->string () + : ""); + + l5 ([&]{trace << "begin" << trace_suffix;}); + + if (pcfg != nullptr) + { + // This is what we refer to as the "initial negotiation" where we + // negotiate the configuration of dependents that could be postponed. + // Those that could not we "up-negotiate" in the collect() lambda of + // collect_build_prerequisites(). + // + using packages = postponed_configuration::packages; + + assert (!pcfg->negotiated); + + // Re-evaluate existing dependents with configuration clause for + // dependencies in this configuration cluster up to these + // dependencies. Omit dependents which are already being built or + // dropped. Note that these dependents, potentially with additional + // dependencies, will be added to this cluster with the `existing` + // flag as a part of the dependents' re-evaluation (see the collect + // lambda in collect_build_prerequisites() for details). + // + // After being re-evaluated the existing dependents are recursively + // collected in the same way as the new dependents. + // + { + // Map existing dependents to the dependencies they apply a + // configuration to. Also, collect the information which is required + // for a dependent re-evaluation and its subsequent recursive + // collection (selected package, etc). + // + // As mentioned earlier, we may end up adding additional + // dependencies to pcfg->dependencies which in turn may have + // additional existing dependents which we need to process. Feels + // like doing this iteratively is the best option. + // + // Note that we need to make sure we don't re-process the same + // existing dependents. + // + struct existing_dependent_ex: existing_dependent + { + packages dependencies; + bool reevaluated = false; + + existing_dependent_ex (existing_dependent&& ed) + : existing_dependent (move (ed)) {} + }; + map dependents; + + const packages& deps (pcfg->dependencies); + + // Note that the below collect_build_prerequisites() call can only + // add new dependencies to the end of the cluster's dependencies + // list. Thus on each iteration we will only add existing dependents + // of unprocessed/new dependencies. We will also skip the already + // re-evaluated existing dependents. + // + for (size_t i (0); i != deps.size (); ) + { + size_t n (dependents.size ()); + + for (; i != deps.size (); ++i) + { + // Note: this reference is only used while deps is unchanged. + // + const package_key& p (deps[i]); + + // If the dependent is being built, then check if it was + // re-evaluated to the position greater than the dependency + // position. Return true if that's the case, so this package is + // added to the resulting list and we can handle this situation. // - // For a system package we pick the latest version just to make - // sure the package is recognized. An unrecognized package means - // the broken/stale repository (see below). + // Note that we rely on "small function object" optimization + // here. // - rp = find_available_one (dn, - !system ? d.constraint : nullopt, - af); + const function verify ( + [&postponed_cfgs, pcfg] + (const package_key& pk, pair pos) + { + for (const postponed_configuration& cfg: postponed_cfgs) + { + if (&cfg == pcfg || cfg.negotiated) + { + if (const pair* p = + cfg.existing_dependent_position (pk)) + { + if (p->first > pos.first) + return true; + } + } + } - if (dap == nullptr) + return false; + }); + + for (existing_dependent& ed: + query_existing_dependents (trace, + p.db, + p.name, + replaced_vers, + rpt_depts, + verify)) { - if (dep_constr && !system && postponed_repo != nullptr) + package_key pk (ed.db, ed.selected->name); + + // If this dependent is present in postponed_deps, then it + // means someone depends on it with configuration and it's no + // longer considered an existing dependent (it will be + // reconfigured). However, this fact may not be reflected yet. + // And it can actually turn out bogus. + // + auto pi (postponed_deps.find (pk)); + if (pi != postponed_deps.end ()) { - // We shouldn't be called in the diag mode for the postponed - // package builds. + l5 ([&]{trace << "skip dep-postponed existing dependent " + << pk << " of dependency " << p;}); + + // Note that here we would re-evaluate the existing dependent + // without specifying any configuration for it. // - assert (dr == nullptr); + pi->second.wout_config = true; - postponed_repo->insert (&pkg); - return precollect_result (true /* postpone */); + continue; } - if (dr != nullptr) + auto i (dependents.find (pk)); + size_t di (ed.dependency_position.first); + + // Skip re-evaluated dependent if the dependency index is + // greater than the one we have already re-evaluated to. If it + // is earlier, then add the entry to postponed_poss and throw + // postpone_position to recollect from scratch. Note that this + // entry in postponed_poss is with replacement. + // + if (i != dependents.end () && i->second.reevaluated) { - *dr << error; + size_t ci (i->second.dependency_position.first); - // Issue diagnostics differently based on the presence of - // available packages for the unsatisfied dependency. - // - // Note that there can't be any stubs, since they satisfy - // any constraint and we won't be here if they were. - // - vector> aps ( - find_available (dn, nullopt /* version_constraint */, af)); + if (di > ci) + continue; - if (!aps.empty ()) - { - *dr << "unable to satisfy dependency constraint (" << dn; + // The newly-introduced dependency must belong to the + // depends value other then the one we have re-evaluated to. + // + assert (di < ci); - // We need to be careful not to print the wildcard-based - // constraint. - // - if (d.constraint && - (!dep_constr || !wildcard (*dep_constr))) - *dr << ' ' << *d.constraint; + postponed_position pp (ed.dependency_position, + true /* replace */); - *dr << ") of package " << pkg.name () << pdb << - info << "available " << dn << " versions:"; + auto p (postponed_poss.emplace (pk, pp)); - for (const shared_ptr& ap: aps) - *dr << ' ' << ap->version; - } - else + if (!p.second) { - *dr << "no package available for dependency " << dn - << " of package " << pkg.name () << pdb; + assert (p.first->second > pp); + p.first->second = pp; } - // Avoid printing this if the dependent package is external - // since it's more often confusing than helpful (they are - // normally not fetched manually). - // - if (!af->location.empty () && - !af->location.directory_based () && - (!dep_constr || system)) - *dr << info << "repository " << af->location << " appears " - << "to be broken" << - info << "or the repository state could be stale" << - info << "run 'bpkg rep-fetch' to update"; - } + l5 ([&]{trace << "cannot re-evaluate dependent " + << pk << " to dependency index " << di + << " since it is already re-evaluated to " + << "greater index " << ci << " in " << *pcfg + << ", throwing postpone_position";}); - return precollect_result (false /* postpone */); - } + throw postpone_position (); + } - // If all that's available is a stub then we need to make sure - // the package is present in the system repository and it's - // version satisfies the constraint. If a source package is - // available but there is a system package specified on the - // command line and it's version satisfies the constraint then - // the system package should be preferred. To recognize such a - // case we just need to check if the authoritative system - // version is set and it satisfies the constraint. If the - // corresponding system package is non-optional it will be - // preferred anyway. - // - if (dap->stub ()) - { - // Note that the constraint can safely be printed as it can't - // be a wildcard (produced from the user-specified dependency - // version constraint). If it were, then the system version - // wouldn't be NULL and would satisfy itself. + // If the existing dependent is not in the map yet, then add + // it. Otherwise, if the dependency position is greater than + // that one in the existing map entry then skip it (this + // position will be up-negotiated, if it's still present). + // Otherwise, if the position is less then overwrite the + // existing entry. Otherwise (the position is equal), just + // add the dependency to the existing entry. // - if (dap->system_version (*ddb) == nullptr) + // Note that we want to re-evaluate the dependent up to the + // earliest dependency position and continue with the regular + // prerequisites collection (as we do for new dependents) + // afterwards. + // + if (i == dependents.end ()) { - if (dr != nullptr) - *dr << error << "dependency " << d << " of package " - << pkg.name () << " is not available in source" << - info << "specify ?sys:" << dn << " if it is available " - << "from the system"; - - return precollect_result (false /* postpone */); + i = dependents.emplace ( + move (pk), existing_dependent_ex (move (ed))).first; } - - if (!satisfies (*dap->system_version (*ddb), d.constraint)) + else { - if (dr != nullptr) - *dr << error << "dependency " << d << " of package " - << pkg.name () << " is not available in source" << - info << package_string (dn, - *dap->system_version (*ddb), - true /* system */) - << " does not satisfy the constrains"; + size_t ci (i->second.dependency_position.first); - return precollect_result (false /* postpone */); + if (ci < di) + continue; + else if (ci > di) + i->second = existing_dependent_ex (move (ed)); + //else if (ci == di) + // ; } - system = true; - } - else - { - auto p (dap->system_version_authoritative (*ddb)); - - if (p.first != nullptr && - p.second && // Authoritative. - satisfies (*p.first, d.constraint)) - system = true; + i->second.dependencies.push_back (p); } } - // If the dependency package of a different version is already - // being built, then we also need to make sure that we will be - // able to choose one of them (either existing or new) which - // satisfies all the dependents. - // - // Note that collect_build() also performs this check but - // postponing it till then can end up in failing instead of - // selecting some other dependency alternative. + // Re-evaluate the newly added existing dependents, if any. // - assert (dap != nullptr); // Otherwise we would fail earlier. - - if (i != map_.end () && d.constraint) + if (dependents.size () != n) { - const build_package& bp (i->second.package); + l5 ([&]{trace << "re-evaluate existing dependents for " << *pcfg;}); - if (bp.action && *bp.action == build_package::build) + for (auto& d: dependents) { - const version& v1 (system - ? *dap->system_version (*ddb) - : dap->version); - - const version& v2 (bp.available_version ()); - - if (v1 != v2) - { - using constraint_type = build_package::constraint_type; - - constraint_type c1 { - pkg.db, pkg.name ().string (), *d.constraint}; - - if (!satisfies (v2, c1.value)) - { - for (const constraint_type& c2: bp.constraints) - { - if (!satisfies (v1, c2.value)) - { - if (dr != nullptr) - { - const package_name& n (d.name); - const string& d1 (c1.dependent); - const string& d2 (c2.dependent); - - *dr << error << "unable to satisfy constraints on " - << "package " << n << - info << d2 << c2.db << " depends on (" << n << ' ' - << c2.value << ")" << - info << d1 << c1.db << " depends on (" << n << ' ' - << c1.value << ")" << - info << "available " - << bp.available_name_version () << - info << "available " - << package_string (n, v1, system) << - info << "explicitly specify " << n << " version " - << "to manually satisfy both constraints"; - } - - return precollect_result (false /* postpone */); - } - } - } - } - } - } - - bool ru (i != map_.end () || dsp != nullptr); - - if (!ru) - reused = false; - - r.push_back (prebuild {d, - *ddb, - move (dsp), - move (dap), - move (rp.second), - system, - specified, - force, - ru}); - } - - return precollect_result (move (r), reused); - }; - - // Collect the previously collected pre-builds. - // - auto collect = [&options, - &pkg, - &pdb, - &fdb, - &rpt_depts, - &apc, - postponed_repo, - postponed_alts, - &dep_chain, - this] - (prebuilds&& bs) - { - for (prebuild& b: bs) - { - build_package bp { - build_package::build, - b.db, - b.selected, - b.available, - move (b.repository_fragment), - nullopt, // Dependencies. - nullopt, // Package skeleton. - nullopt, // Postponed dependency alternatives. - nullopt, // Hold package. - nullopt, // Hold version. - {}, // Constraints. - b.system, - false, // Keep output directory. - false, // Disfigure (from-scratch reconf). - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - {config_package {pdb, pkg.name ()}}, // Required by (dependent). - true, // Required by dependents. - 0}; // State flags. + existing_dependent_ex& ed (d.second); - const optional& constraint ( - b.dependency.constraint); + // Skip re-evaluated. + // + if (ed.reevaluated) + continue; - // Add our constraint, if we have one. - // - // Note that we always add the constraint implied by the dependent. - // The user-implied constraint, if present, will be added when - // merging from the pre-entered entry. So we will have both - // constraints for completeness. - // - if (constraint) - bp.constraints.emplace_back (pdb, - pkg.name ().string (), - *constraint); + size_t di (ed.dependency_position.first); + const package_key& pk (d.first); - // Now collect this prerequisite. If it was actually collected - // (i.e., it wasn't already there) and we are forcing a downgrade - // or upgrade, then refuse for a held version, warn for a held - // package, and print the info message otherwise, unless the - // verbosity level is less than two. - // - // Note though that while the prerequisite was collected it could - // have happen because it is an optional package and so not being - // pre-collected earlier. Meanwhile the package was specified - // explicitly and we shouldn't consider that as a - // dependency-driven up/down-grade enforcement. - // - // Here is an example of the situation we need to handle properly: - // - // repo: foo/2(->bar/2), bar/0+1 - // build sys:bar/1 - // build foo ?sys:bar/2 - // - // Note: recursive. - // - const build_package* p ( - collect_build (options, - move (bp), - fdb, - rpt_depts, - apc, - postponed_repo, - postponed_alts, - &dep_chain)); + // Check if there is an earlier dependency position for this + // dependent that will be participating in a configuration + // negotiation and skip this cluster if that's the case. There + // are two places to check: postponed_poss and other clusters. + // + auto pi (postponed_poss.find (pk)); + if (pi != postponed_poss.end () && pi->second.first < di) + { + l5 ([&]{trace << "pos-postpone existing dependent " + << pk << " re-evaluation to dependency " + << "index " << di << " due to recorded index " + << pi->second.first << ", skipping " << *pcfg;}); - if (p != nullptr && b.force && !b.specified_dependency) - { - // Fail if the version is held. Otherwise, warn if the package is - // held. - // - bool f (b.selected->hold_version); - bool w (!f && b.selected->hold_package); + pi->second.skipped = true; - if (f || w || verb >= 2) - { - const version& av (p->available_version ()); + // If requested, override the first encountered non-replace + // position to replace (see below for details). + // + if (!pi->second.replace && postponed_poss.replace) + { + pi->second.replace = true; + postponed_poss.replace = false; + } + + if (pi->second.replace) + throw skip_configuration (move (ed), pi->second); + else + throw skip_configuration (); + } + + // The other clusters check is a bit more complicated: if the + // other cluster (with the earlier position) is not yet + // negotiated, then we skip. Otherwise, we have to add an + // entry to postponed_poss and backtrack. + // + bool skip (false); + for (const postponed_configuration& cfg: postponed_cfgs) + { + // Skip the current cluster. + // + if (&cfg == pcfg) + continue; - bool u (av > b.selected->version); - bool c (constraint); + if (const pair* p = + cfg.existing_dependent_position (pk)) + { + size_t ei (p->first); // Other position. + + if (!cfg.negotiated) + { + if (ei < di) + { + l5 ([&]{trace << "cannot re-evaluate dependent " + << pk << " to dependency index " << di + << " due to earlier dependency index " + << ei << " in " << cfg << ", skipping " + << *pcfg;}); + + skip = true; + } + } + else + { + // If this were not the case, then this dependent + // wouldn't have been considered as an existing by + // query_existing_dependents() since as it is (being) + // negotiated then it is already re-evaluated and so is + // being built (see the verify lambda above). + // + assert (ei > di); + + // Feels like there cannot be an earlier position. + // + postponed_position pp (ed.dependency_position, + false /* replace */); + + auto p (postponed_poss.emplace (pk, pp)); + if (!p.second) + { + assert (p.first->second > pp); + p.first->second = pp; + } - diag_record dr; + l5 ([&]{trace << "cannot re-evaluate dependent " + << pk << " to dependency index " << di + << " due to greater dependency " + << "index " << ei << " in " << cfg + << ", throwing postpone_position";}); + + throw postpone_position (); + } + } + } - (f ? dr << fail : - w ? dr << warn : - dr << info) - << "package " << pkg.name () << pdb << " dependency on " - << (c ? "(" : "") << b.dependency << (c ? ")" : "") - << " is forcing " << (u ? "up" : "down") << "grade of " - << *b.selected << b.db << " to "; + if (skip) + throw skip_configuration (); - // Print both (old and new) package names in full if the system - // attribution changes. + // Finally, re-evaluate the dependent. // - if (b.selected->system ()) - dr << p->available_name_version (); - else - dr << av; // Can't be a system version so is never wildcard. + packages& ds (ed.dependencies); + + pair, + lazy_shared_ptr> rp ( + find_available_fragment (o, pk.db, ed.selected)); + + build_package p { + build_package::build, + pk.db, + move (ed.selected), + move (rp.first), + move (rp.second), + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + false, // System. + false, // Keep output directory. + false, // Disfigure (from-scratch reconf). + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + set ( // Required by (dependency). + ds.begin (), ds.end ()), + false, // Required by dependents. + build_package::adjust_reconfigure}; + + // Note: not recursive. + // + collect_build (o, + move (p), + fdb, + rpt_depts, + apc, + false /* initial_collection */, + replaced_vers, + postponed_cfgs); + + build_package* b (entered_build (pk)); + assert (b != nullptr); + + // Re-evaluate up to the earliest position. + // + assert (ed.dependency_position.first != 0); - if (b.selected->hold_version) - dr << info << "package version " << *b.selected << b.db - << " is held"; + build_package_refs dep_chain; + collect_build_prerequisites (o, + *b, + fdb, + rpt_depts, + apc, + false /* initial_collection */, + replaced_vers, + dep_chain, + &postponed_repo, + &postponed_alts, + numeric_limits::max (), + postponed_deps, + postponed_cfgs, + postponed_poss, + ed.dependency_position); + + ed.reevaluated = true; + + if (pi != postponed_poss.end ()) + { + // Otherwise we should have thrown skip_configuration above. + // + assert (di <= pi->second.first); - if (f) - dr << info << "explicitly request version " - << (u ? "up" : "down") << "grade to continue"; + pi->second.reevaluated = true; + } } } } - }; + } - // Select a dependency alternative, copying it alone into the - // resulting dependencies list, evaluating its reflect clause, if - // present, and collecting its dependency builds. + l5 ([&]{trace << "cfg-negotiate begin " << *pcfg;}); + + // Negotiate the configuration. // - bool selected (false); - auto select = [&sdeps, &sdas, &skel, di, &collect, &selected] - (const dependency_alternative& da, - prebuilds&& bs) + // The overall plan is as follows: continue refining the configuration + // until there are no more changes by giving each dependent a chance + // to make further adjustments. + // + for (auto b (pcfg->dependents.begin ()), + i (b), + e (pcfg->dependents.end ()); i != e; ) { - assert (sdas.empty ()); - - // Avoid copying enable/reflect not to evaluate them repeatedly. + // Resolve package skeletons for the dependent and its dependencies. // - sdas.emplace_back (nullopt /* enable */, - nullopt /* reflect */, - da.prefer, - da.accept, - da.require, - da /* dependencies */); + // For the dependent, the skeleton should be already there (since we + // should have started recursively collecting it). For a dependency, + // it should not already be there (since we haven't yet started + // recursively collecting it). But we could be re-resolving the same + // dependency multiple times. + // + package_skeleton* dept; + { + build_package* b (entered_build (i->first)); + assert (b != nullptr && b->skeleton); + dept = &*b->skeleton; + } - sdeps.push_back (move (sdas)); + pair pos; + small_vector, 1> depcs; + { + // A non-negotiated cluster must only have one depends position + // for each dependent. + // + assert (i->second.dependencies.size () == 1); - if (da.reflect) - skel.evaluate_reflect (*da.reflect, di); + const postponed_configuration::dependency& ds ( + i->second.dependencies.front ()); - collect (move (bs)); + pos = ds.position; - selected = true; - }; + depcs.reserve (ds.size ()); + for (const package_key& pk: ds) + { + build_package* b (entered_build (pk)); + assert (b != nullptr); - // Postpone the prerequisite builds collection, optionally inserting - // the package to the postpones set (can potentially already be there) - // and saving the enabled alternatives. + depcs.push_back (b->skeleton + ? *b->skeleton + : b->init_skeleton (o /* options */)); + } + } + + if (negotiate_configuration ( + pcfg->dependency_configurations, *dept, pos, depcs)) + { + if (i != b) + { + i = b; // Restart from the beginning. + continue; + } + } + + ++i; + } + + // Being negotiated (so can only be up-negotiated). // - bool postponed (false); - auto postpone = [&pkg, &edas, &postponed] - (postponed_packages* postpones) - { - if (postpones != nullptr) - postpones->insert (&pkg); + pcfg->negotiated = false; - pkg.postponed_dependency_alternatives = move (edas); - postponed = true; - }; + // Note that we can be adding new packages to the being negotiated + // cluster by calling collect_build_prerequisites() for its + // dependencies and dependents. Thus, we need to stash the current + // list of dependencies and dependents and iterate over them. + // + // Note that whomever is adding new packages is expected to process + // them (they may also process existing packages, which we are + // prepared to ignore). + // + packages dependencies (pcfg->dependencies); - // Iterate over the enabled dependencies and try to select a - // satisfactory alternative. + packages dependents; + dependents.reserve (pcfg->dependents.size ()); + + for (const auto& p: pcfg->dependents) + dependents.push_back (p.first); + + // Process dependencies recursively with this config. // - // If the package is already configured as source and is not - // up/downgraded, then we will try to resolve its dependencies to the - // current prerequisites. To achieve this we will first try to select - // an alternative in the "recreate dependency decisions" mode, - // filtering out all the alternatives where dependencies do not all - // belong to the list of current prerequisites. If we end up with no - // alternative selected, then we retry in the "make dependency - // decisions" mode and select the alternative ignoring the current - // prerequisites. + // Note that there could be inter-dependecies between these packages, + // which means the configuration can only be up-negotiated. // - const package_prerequisites* prereqs (src_conf && !ud - ? &sp->prerequisites - : nullptr); + l5 ([&]{trace << "recursively collect cfg-negotiated dependencies";}); - for (;;) + for (const package_key& p: dependencies) { - // The index and pre-collection result of the first satisfactory - // alternative. - // - optional> first_alt; + build_package* b (entered_build (p)); + assert (b != nullptr); - // The number of satisfactory alternatives. + // Reconfigure the configured dependencies. // - size_t alts_num (0); + // Note that potentially this can be an overkill if the dependency + // configuration doesn't really change. Later we can implement some + // precise detection for that using configuration checksum or + // similar. + // + // Also note that for configured dependents which belong to the + // configuration cluster this flag is already set (see above). + // + if (b->selected != nullptr && + b->selected->state == package_state::configured) + b->flags |= build_package::adjust_reconfigure; - for (size_t i (0); i != edas.size (); ++i) + // Skip the dependencies which are already collected recursively. + // + if (!b->recursive_collection) { - const dependency_alternative& da (edas[i]); - - precollect_result r (precollect (da, das.buildtime, prereqs)); - - // If we didn't come up with satisfactory dependency builds, then - // skip this alternative and try the next one, unless the - // collecting is postponed in which case just bail out. - // - // Should we skip alternatives for which we are unable to satisfy - // the constraint? On one hand, this could be a user error: there - // is no package available from dependent's repositories that - // satisfies the constraint. On the other hand, it could be that - // it's other dependent's constraints that we cannot satisfy - // together with others. And in this case we may want some other - // alternative. Consider, as an example, something like this: + // Verify and set the dependent configuration for this dependency. // - // depends: libfoo >= 2.0.0 | libfoo >= 1.0.0 libbar + // Note: see similar code for the up-negotiation case. // - if (!r.builds) { - if (r.repo_postpone) - { - postpone (nullptr); // Already inserted into postponed_repo. - break; - } - - continue; - } - - ++alts_num; + assert (b->skeleton); // Should have been init'ed above. - // Note that when we see the first satisfactory alternative, we - // don't know yet if it is a single alternative or the first of - // the (multiple) true alternatives (those are handled - // differently). Thus, we postpone its processing until the - // second satisfactory alternative is encountered or the end of - // the alternatives list is reached. - // - if (!first_alt) - { - first_alt = make_pair (i, move (r)); - continue; - } + const package_configuration& pc ( + pcfg->dependency_configurations[p]); - // Try to select a true alternative, returning true if the - // alternative is selected or the selection is postponed. Return - // false if the alternative is ignored (not postponed and not all - // of it dependencies are reused). - // - auto try_select = [postponed_alts, &max_alt_index, - &edas, - &postpone, &select] - (size_t index, precollect_result&& r) - { - // Postpone the collection if the alternatives maximum index is - // reached. + // Skip the verification if this is a system package + // without skeleton info. // - if (postponed_alts != nullptr && index >= max_alt_index) - { - postpone (postponed_alts); - return true; - } + pair pr (b->skeleton->available != nullptr + ? b->skeleton->verify_sensible (pc) + : make_pair (true, string ())); - // Select this alternative if all its dependencies are reused - // and do nothing about it otherwise. - // - if (r.reused) + if (!pr.first) { - // On the diagnostics run there shouldn't be any alternatives - // that we could potentially select. + // Note that the diagnostics from the dependency will most + // likely be in the "error ..." form (potentially with + // additional info lines) and by printing it with a two-space + // indentation we make it "fit" into our diag record. // - assert (postponed_alts != nullptr); - - select (edas[index], move (*r.builds)); + diag_record dr (fail); + dr << "unable to negotiate sensible configuration for " + << "dependency " << p << '\n' + << " " << pr.second; - // Make sure no more true alternatives are selected during - // this function call. - // - max_alt_index = 0; - return true; + dr << info << "negotiated configuration:\n"; + pc.print (dr, " "); // Note 4 spaces since in nested info. } - else - return false; - }; - - // If we encountered the second satisfactory alternative, then - // this is the "multiple true alternatives" case. In this case we - // also need to process the first satisfactory alternative, which - // processing was delayed. - // - if (alts_num == 2) - { - assert (first_alt); - if (try_select (first_alt->first, move (first_alt->second))) - break; + b->skeleton->dependent_config (pc); } - if (try_select (i, move (r))) - break; - - // Not all of the alternative dependencies are reused, so go to - // the next alternative. + build_package_refs dep_chain; + collect_build_prerequisites (o, + *b, + fdb, + rpt_depts, + apc, + false /* initial_collection */, + replaced_vers, + dep_chain, + &postponed_repo, + &postponed_alts, + 0 /* max_alt_index */, + postponed_deps, + postponed_cfgs, + postponed_poss); } + else + l5 ([&]{trace << "dependency " << b->available_name_version_db () + << " is already (being) recursively collected, " + << "skipping";}); + } - // Bail out if the collection is postponed for any reason. + // Continue processing dependents with this config. + // + l5 ([&]{trace << "recursively collect cfg-negotiated dependents";}); + + for (const auto& p: dependents) + { + // Select the dependency alternative for which configuration has + // been negotiated and collect this dependent starting from the next + // depends value. // - if (postponed) - break; + build_package* b (entered_build (p)); - // Select the single satisfactory alternative (regardless of its - // dependencies reuse). + // We should have been started recursively collecting the dependent + // and it should have been postponed. + // + assert (b != nullptr && + b->available != nullptr && + b->dependencies && + b->skeleton && + b->postponed_dependency_alternatives); + + // Select the dependency alternative (evaluate reflect if present, + // etc) and position to the next depends value (see + // collect_build_prerequisites() for details). // - if (!selected && alts_num == 1) { - assert (first_alt && first_alt->second.builds); + const bpkg::dependencies& deps (b->available->dependencies); + bpkg::dependencies& sdeps (*b->dependencies); + vector& salts (*b->alternatives); - select (edas[first_alt->first], move (*first_alt->second.builds)); - } + size_t di (sdeps.size ()); - // If an alternative is selected, then we are done. - // - if (selected) - break; + // Skip the dependent if it has been already collected as some + // package's dependency or some such. + // + if (di == deps.size ()) + { + l5 ([&]{trace << "dependent " << b->available_name_version_db () + << " is already recursively collected, skipping";}); - // Fail or postpone the collection if no alternative is selected, - // unless we are in the "recreate dependency decisions" mode. In the - // latter case fall back to the "make dependency decisions" mode and - // retry. - // - if (prereqs != nullptr) - { - prereqs = nullptr; - continue; - } + continue; + } - // Issue diagnostics and fail if there are no satisfactory - // alternatives. - // - if (alts_num == 0) - { - diag_record dr; - for (const dependency_alternative& da: edas) - precollect (da, das.buildtime, nullptr /* prereqs */, &dr); + l5 ([&]{trace << "select cfg-negotiated dependency alternative " + << "for dependent " + << b->available_name_version_db ();}); - assert (!dr.empty ()); + // Find the postponed dependency alternative. + // + auto i (pcfg->dependents.find (p)); - dr.flush (); - throw failed (); - } + assert (i != pcfg->dependents.end () && + i->second.dependencies.size () == 1); - // Issue diagnostics and fail if there are multiple alternatives - // with non-reused dependencies, unless the failure needs to be - // postponed. - // - assert (alts_num > 1); + pair dp (i->second.dependencies[0].position); + assert (dp.first == sdeps.size () + 1); - if (postponed_alts != nullptr) - { - postpone (postponed_alts); - break; - } + build_package::dependency_alternatives_refs pdas ( + move (*b->postponed_dependency_alternatives)); - diag_record dr (fail); - dr << "unable to select dependency alternative for package " - << pkg.available_name_version_db () << - info << "explicitly specify dependency packages to manually " - << "select the alternative"; + b->postponed_dependency_alternatives = nullopt; - for (const dependency_alternative& da: edas) - { - precollect_result r ( - precollect (da, das.buildtime, nullptr /* prereqs */)); + auto j (find_if (pdas.begin (), pdas.end (), + [&dp] (const auto& da) + { + return da.second + 1 == dp.second; + })); + + assert (j != pdas.end ()); + + const dependency_alternative& da (j->first); + size_t dai (j->second); + + // Select the dependency alternative and position to the next + // depends value. + // + const dependency_alternatives_ex& das (deps[di]); + dependency_alternatives_ex sdas (das.buildtime, das.comment); - if (r.builds) - { - assert (!r.reused); // We shouldn't be failing otherwise. + sdas.emplace_back (nullopt /* enable */, + nullopt /* reflect */, + da.prefer, + da.accept, + da.require, + da /* dependencies */); - dr << info << "alternative:"; + sdeps.push_back (move (sdas)); + salts.push_back (dai); - // Only print the non-reused dependencies, which needs to be - // explicitly specified by the user. - // - for (const prebuild& b: *r.builds) - { - if (!b.reused) - dr << ' ' << b.dependency.name; - } - } + // Evaluate reflect, if present. + // + if (da.reflect) + b->skeleton->evaluate_reflect (*da.reflect, make_pair (di, dai)); } + + // Continue recursively collecting the dependent. + // + build_package_refs dep_chain; + + collect_build_prerequisites ( + o, + *b, + fdb, + rpt_depts, + apc, + false /* initial_collection */, + replaced_vers, + dep_chain, + &postponed_repo, + &postponed_alts, + 0 /* max_alt_index */, + postponed_deps, + postponed_cfgs, + postponed_poss); } - if (postponed) - break; + // Negotiated (so can only be rolled back). + // + pcfg->negotiated = true; + + l5 ([&]{trace << "cfg-negotiate end " << *pcfg;}); + + // Fall through (to start another iteration of the below loop). } - dep_chain.pop_back (); - } + // Try collecting postponed packages for as long as we are making + // progress. + // + vector spas; // Reuse. - // Collect the repointed dependents and their replaced prerequisites, - // recursively. - // - // If a repointed dependent is already pre-entered or collected with an - // action other than adjustment, then just mark it for reconfiguration - // unless it is already implied. Otherwise, collect the package build with - // the repoint sub-action and reconfigure adjustment flag. - // - void - collect_repointed_dependents ( - const pkg_build_options& o, - const repointed_dependents& rpt_depts, - build_packages::postponed_packages& postponed_repo, - build_packages::postponed_packages& postponed_alts, - const function& fdb, - const function& apc) - { - for (const auto& rd: rpt_depts) + for (bool prog (!postponed_repo.empty () || + !postponed_cfgs.negotiated () || + !postponed_alts.empty () || + postponed_deps.has_bogus ()); + prog; ) { - database& db (rd.first.db); - const package_name& nm (rd.first.name); + postponed_packages prs; + postponed_packages pas; - auto i (map_.find (db, nm)); - if (i != map_.end ()) + // Try to collect the repository-related postponments first. + // + for (build_package* p: postponed_repo) { - build_package& b (i->second.package); + l5 ([&]{trace << "collect rep-postponed " + << p->available_name_version_db ();}); - if (!b.action || *b.action != build_package::adjust) - { - if (!b.action || - (*b.action != build_package::drop && !b.reconfigure ())) - b.flags |= build_package::adjust_reconfigure; + build_package_refs dep_chain; - continue; - } + collect_build_prerequisites (o, + *p, + fdb, + rpt_depts, + apc, + false /* initial_collection */, + replaced_vers, + dep_chain, + &prs, + &pas, + 0 /* max_alt_index */, + postponed_deps, + postponed_cfgs, + postponed_poss); } - shared_ptr sp (db.load (nm)); - - // The repointed dependent can be an orphan, so just create the - // available package from the selected package. + // Save the potential new dependency alternative-related postponements. // - auto rp (make_available_fragment (o, db, sp)); + postponed_alts.insert (pas.begin (), pas.end ()); - // Add the prerequisite replacements as the required-by packages. + prog = (prs != postponed_repo); + + if (prog) + { + postponed_repo.swap (prs); + continue; + } + + // Now, as there is no more progress made in collecting repository- + // related postponements, collect the dependency configuration-related + // postponements. // - set required_by; - for (const auto& prq: rd.second) + // Note that we do it before alternatives since configurations we do + // perfectly (via backtracking) while alternatives -- heuristically. + // + // Note that since the potential snapshot restore replaces all the + // list entries we cannot iterate using the iterator here. Also note + // that the list size may change during iterating. + // + for (size_t ci (0); ci != postponed_cfgs.size (); ++ci) { - if (prq.second) // Prerequisite replacement? + postponed_configuration* pc (&postponed_cfgs[ci]); + + // Find the next configuration to try to negotiate, skipping the + // already negotiated ones. + // + if (pc->negotiated) + continue; + + size_t pcd (depth + 1); + pc->depth = pcd; + + // Either return or retry the same cluster or skip this cluster and + // proceed to the next one. + // + for (;;) { - const config_package& cp (prq.first); - required_by.emplace (cp.db, cp.name); - } - } + // First assume we can negotiate this configuration rolling back + // if this doesn't pan out. + // + snapshot s (*this, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs); - build_package p { - build_package::build, - db, - sp, - move (rp.first), - move (rp.second), - nullopt, // Dependencies. - nullopt, // Package skeleton. - nullopt, // Postponed dependency alternatives. - nullopt, // Hold package. - nullopt, // Hold version. - {}, // Constraints. - sp->system (), - false, // Keep output directory. - false, // Disfigure (from-scratch reconf). - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - move (required_by), // Required by (dependencies). - false, // Required by dependents. - build_package::adjust_reconfigure | build_package::build_repoint}; + try + { + collect_build_postponed (o, + replaced_vers, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs, + postponed_cfgs_history, + postponed_poss, + fdb, + rpt_depts, + apc, + pc); - build_package_refs dep_chain; + // If collect() returns (instead of throwing), this means it + // processed everything that was postponed. + // + assert (postponed_repo.empty () && + postponed_cfgs.negotiated () && + postponed_alts.empty () && + !postponed_deps.has_bogus ()); - // Note: recursive. - // - collect_build (o, - move (p), - fdb, - rpt_depts, - apc, - &postponed_repo, - &postponed_alts, - &dep_chain); - } - } + l5 ([&]{trace << "end" << trace_suffix;}); - // Collect the package being dropped. - // - void - collect_drop (database& db, shared_ptr sp) - { - const package_name& nm (sp->name); + return; + } + catch (skip_configuration& e) + { + // Restore the state from snapshot. + // + // Note: postponed_cfgs is re-assigned. + // + s.restore (*this, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs); - build_package p { - build_package::drop, - db, - move (sp), - nullptr, - nullptr, - nullopt, // Dependencies. - nullopt, // Package skeleton. - nullopt, // Postponed dependency alternatives. - nullopt, // Hold package. - nullopt, // Hold version. - {}, // Constraints. - false, // System package. - false, // Keep output directory. - false, // Disfigure (from-scratch reconf). - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - {}, // Required by. - false, // Required by dependents. - 0}; // State flags. + pc = &postponed_cfgs[ci]; - auto i (map_.find (db, nm)); + // Note that in this case we keep the accumulated configuration, + // if any. - if (i != map_.end ()) - { - build_package& bp (i->second.package); + pc->depth = 0; - // Overwrite the existing (possibly pre-entered, adjustment, or - // repoint) entry. - // - bp = move (p); - } - else - map_.emplace (config_package {db, nm}, - data_type {end (), move (p)}); - } + // If requested, "replace" the "later" dependent-dependency + // cluster with an earlier. + // + if (e.dependent) + { + existing_dependent& ed (*e.dependent); + pair pos (e.new_position); - // Collect the package being unheld. - // - void - collect_unhold (database& db, const shared_ptr& sp) - { - auto i (map_.find (db, sp->name)); + const build_package* bp ( + replace_existing_dependent_dependency ( + trace, + o, + ed, // Note: modified. + pos, + fdb, + rpt_depts, + apc, + false /* initial_collection */, + replaced_vers, + postponed_cfgs)); + + postponed_cfgs.add (package_key (ed.db, ed.selected->name), + pos, + package_key (bp->db, bp->selected->name)); + } - // Currently, it must always be pre-entered. - // - assert (i != map_.end ()); + l5 ([&]{trace << "postpone cfg-negotiation of " << *pc;}); - build_package& bp (i->second.package); + break; + } + catch (const retry_configuration& e) + { + // If this is not "our problem", then keep looking. + // + if (e.depth != pcd) + throw; - if (!bp.action) // Pre-entered. - { - build_package p { - build_package::adjust, - db, - sp, - nullptr, - nullptr, - nullopt, // Dependencies. - nullopt, // Package skeleton. - nullopt, // Postponed dependency alternatives. - nullopt, // Hold package. - nullopt, // Hold version. - {}, // Constraints. - false, // System package. - false, // Keep output directory. - false, // Disfigure (from-scratch reconf). - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - {}, // Required by. - false, // Required by dependents. - build_package::adjust_unhold}; + package_configurations cfgs ( + move (pc->dependency_configurations)); - p.merge (move (bp)); - bp = move (p); - } - else - bp.flags |= build_package::adjust_unhold; - } + // Restore the state from snapshot. + // + // Note: postponed_cfgs is re-assigned. + // + s.restore (*this, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs); - void - collect_build_prerequisites (const pkg_build_options& o, - database& db, - const package_name& name, - postponed_packages& postponed_repo, - postponed_packages& postponed_alts, - size_t max_alt_index, - const function& fdb, - const repointed_dependents& rpt_depts, - const function& apc) - { - auto mi (map_.find (db, name)); - assert (mi != map_.end ()); + pc = &postponed_cfgs[ci]; - build_package_refs dep_chain; + l5 ([&]{trace << "cfg-negotiation of " << *pc << " failed due " + << "to dependent " << e.dependent << ", refining " + << "configuration";}); - collect_build_prerequisites (o, - mi->second.package, - fdb, - rpt_depts, - apc, - &postponed_repo, - &postponed_alts, - max_alt_index, - dep_chain); - } + // Copy over the configuration for further refinement. + // + // Note that there is also a possibility of ending up with + // "bogus" configuration variables that were set by a dependent + // during up-negotiation but, due to changes to the overall + // configuration, such a dependent were never re-visited. + // + // The way we are going to deal with this is by detecting such + // bogus variables based on the confirmed flag, cleaning them + // out, and doing another retry. Here we clear the confirmed + // flag and the detection happens in collect_build_postponed() + // after we have processed everything postponed (since that's + // the only time we can be certain there could no longer be a + // re-visit). + // + for (package_configuration& cfg: cfgs) + for (config_variable_value& v: cfg) + if (v.dependent) + v.confirmed = false; + + pc->dependency_configurations = move (cfgs); + } + catch (merge_configuration& e) + { + // If this is not "our problem", then keep looking. + // + if (e.depth != pcd) + throw; + + postponed_configuration shadow (move (*pc)); + + // Restore the state from snapshot. + // + // Note: postponed_cfgs is re-assigned. + // + s.restore (*this, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs); + + pc = &postponed_cfgs[ci]; + + assert (!pc->negotiated); + + // Drop any accumulated configuration (which could be carried + // over from retry_configuration logic). + // + pc->dependency_configurations.clear (); - void - collect_build_postponed (const pkg_build_options& o, - postponed_packages& postponed_repo, - postponed_packages& postponed_alts, - const function& fdb, - const repointed_dependents& rpt_depts, - const function& apc) - { - // Try collecting postponed packages for as long as we are making - // progress. - // - vector spas; // Reuse. + l5 ([&]{trace << "cfg-negotiation of " << *pc << " failed due " + << "to non-negotiated clusters, force-merging " + << "based on shadow cluster " << shadow;}); - for (bool prog (!postponed_repo.empty () || !postponed_alts.empty ()); - prog; ) - { - postponed_packages prs; - postponed_packages pas; + // Pre-merge into this cluster those non-negotiated clusters + // which were merged into the shadow cluster. + // + for (size_t id: shadow.merged_ids) + { + postponed_configuration* c (postponed_cfgs.find (id)); - // Try to collect the repository-related postponments first. - // - for (build_package* p: postponed_repo) - { - build_package_refs dep_chain; + if (c != nullptr) + { + // Otherwise we would be handling the exception in the + // higher stack frame. + // + assert (!c->negotiated); - collect_build_prerequisites (o, - *p, - fdb, - rpt_depts, - apc, - &prs, - &pas, - 0 /* max_alt_index */, - dep_chain); - } + l5 ([&]{trace << "force-merge " << *c << " into " << *pc;}); - // Save the potential new dependency alternative-related postpones. - // - postponed_alts.insert (pas.begin (), pas.end ()); + pc->merge (move (*c)); - prog = (prs != postponed_repo); + // Mark configuration as the one being merged from for + // subsequent erasing from the list. + // + c->dependencies.clear (); + } + } - if (prog) - { - postponed_repo.swap (prs); - continue; + // Erase clusters which we have merged from. Also re-translate + // the current cluster address into index which may change as a + // result of the merge. + // + auto i (postponed_cfgs.begin ()); + auto j (postponed_cfgs.before_begin ()); // Precedes iterator i. + + for (size_t k (0); i != postponed_cfgs.end (); ) + { + if (!i->dependencies.empty ()) + { + if (&*i == pc) + ci = k; + + ++i; + ++j; + ++k; + } + else + i = postponed_cfgs.erase_after (j); + } + + pc->set_shadow_cluster (move (shadow)); + } + } } - // Now, as there is no more progress made in collecting repository- - // related postpones, try to collect the dependency alternative- - // related postpones. + // Note that we only get here if we didn't make any progress on the + // previous loop (the only "progress" path ends with return). + + // Now, try to collect the dependency alternative-related + // postponements. // if (!postponed_alts.empty ()) { - // Sort the postpones in the unprocessed dependencies count + // Sort the postponments in the unprocessed dependencies count // descending order. // // The idea here is to preferably handle those postponed packages @@ -2901,19 +6520,28 @@ namespace bpkg build_package_refs dep_chain; + l5 ([&]{trace << "index " << i << " collect alt-postponed " + << p->available_name_version_db ();}); + collect_build_prerequisites (o, *p, fdb, rpt_depts, apc, + false /* initial_collection */, + replaced_vers, + dep_chain, &prs, &pas, i, - dep_chain); + postponed_deps, + postponed_cfgs, + postponed_poss); - prog = (ndep != p->dependencies->size ()); + prog = (pas.find (p) == pas.end () || + ndep != p->dependencies->size ()); - // Save the potential new postpones. + // Save the potential new postponements. // if (prog) { @@ -2924,9 +6552,14 @@ namespace bpkg size_t npr (postponed_repo.size ()); postponed_repo.insert (prs.begin (), prs.end ()); - // Note that not collecting any alternative-relative postpones - // but producing new repository-related postpones is progress - // nevertheless. + // Note that not collecting any alternative-relative + // postponements but producing new repository-related + // postponements is progress nevertheless. + // + // Note that we don't need to check for new configuration- + // related postponements here since if they are present, then + // this package wouldn't be in pas and so prog would be true + // (see above for details). // if (!prog) prog = (npr != postponed_repo.size ()); @@ -2935,10 +6568,194 @@ namespace bpkg break; } } + + if (prog) + continue; + } + + assert (!prog); + + // If we still have any non-negotiated clusters and non-replace + // postponed positions, then it's possible one of them is the cross- + // dependent pathological case where we will never hit it unless we + // force the re-evaluation to earlier position (similar to the + // single-dependent case, which we handle accurately). For example: + // + // tex: depends: libbar(c) + // depends: libfoo(c) + // + // tix: depends: libbar(c) + // depends: tex(c) + // + // Here tex and tix are existing dependent and we are upgrading tex. + // + // While it would be ideal to handle such cases accurately, it's not + // trivial. So for now we resort to the following heuristics: when + // left with no other option, we treat the first encountered non- + // replace position as replace and see if that helps move things + // forward. + // + if (!postponed_cfgs.negotiated () && + find_if (postponed_poss.begin (), postponed_poss.end (), + [] (const auto& v) {return !v.second.replace;}) != + postponed_poss.end () && + !postponed_poss.replace) + { + l5 ([&]{trace << "non-negotiated clusters left and non-replace " + << "postponed positions are present, overriding first " + << "encountered non-replace position to replace";}); + + postponed_poss.replace = true; + prog = true; + continue; // Go back to negotiating skipped cluster. + } + + // Finally, erase the bogus postponements and re-collect from scratch, + // if any (see postponed_dependencies for details). + // + // Note that we used to re-collect such postponements in-place but + // re-doing from scratch feels more correct (i.e., we may end up doing + // it earlier which will affect dependency alternatives). + // + postponed_deps.cancel_bogus (trace, false /* initial_collection */); + } + + // Check if any negotiatiated configurations ended up with any bogus + // variables (see retry_configuration catch block for background). + // + // Note that we could potentially end up yo-yo'ing: we remove a bogus + // and that causes the original dependent to get re-visited which in + // turn re-introduces the bogus. In other words, one of the bogus + // variables which we have removed are actually the cause of no longer + // needing the dependent that introduced it. Feels like the correct + // outcome of this should be keeping the bogus variable that triggered + // yo-yo'ing. Of course, there could be some that we should keep and + // some that we should drop and figuring this out would require retrying + // all possible combinations. An alternative solution would be to detect + // yo-yo'ing, print the bogus variables involved, and ask the user to + // choose (with an override) which ones to keep. Let's go with this for + // now. + // + { + // On the first pass see if we have anything bogus. + // + bool bogus (false); + for (postponed_configuration& pcfg: postponed_cfgs) + { + if (pcfg.negotiated && *pcfg.negotiated) // Negotiated. + { + for (package_configuration& cfg: pcfg.dependency_configurations) + { + for (config_variable_value& v: cfg) + { + if (v.dependent && !v.confirmed) + { + bogus = true; + break; + } + } + if (bogus) break; + } + if (bogus) break; + } + } + + if (bogus) + { + // On the second pass calculate the checksum of all the negotiated + // clusters. + // + sha256 cs; + for (postponed_configuration& pcfg: postponed_cfgs) + { + if (pcfg.negotiated && *pcfg.negotiated) + { + for (package_configuration& cfg: pcfg.dependency_configurations) + { + for (config_variable_value& v: cfg) + { + if (v.dependent) + to_checksum (cs, v); + } + } + } + } + + bool cycle; + { + string s (cs.string ()); + if (find (postponed_cfgs_history.begin (), + postponed_cfgs_history.end (), + s) == postponed_cfgs_history.end ()) + { + postponed_cfgs_history.push_back (move (s)); + cycle = false; + } + else + cycle = true; + } + + // On the third pass we either retry or diagnose. + // + diag_record dr; + if (cycle) + { + dr << + fail << "unable to remove bogus configuration values without " + << "causing configuration refinement cycle" << + info << "consider manually specifying one or more of the " + << "following variables as user configuration"; + } + + for (postponed_configuration& pcfg: postponed_cfgs) + { + optional dept; // Bogus dependent. + + if (pcfg.negotiated && *pcfg.negotiated) + { + for (package_configuration& cfg: pcfg.dependency_configurations) + { + // Note that the entire dependency configuration may end up + // being "bogus" (i.e., it does not contain any configuration + // variables with a confirmed dependent). But that will be + // handled naturally: we will either no longer have this + // dependency in the cluster and thus never call its + // skeleton's dependent_config() or this call will be no-op + // since it won't find any dependent variables. + // + for (config_variable_value& v: cfg) + { + if (v.dependent && !v.confirmed) + { + if (!dept) + dept = move (v.dependent); + + if (cycle) + dr << "\n " << v.serialize_cmdline (); + else + v.undefine (); + } + } + } + + if (dept) + { + if (cycle) + break; + else + throw retry_configuration {pcfg.depth, move (*dept)}; + } + } + + if (dept) + break; + } } } - // If any postponed builds remained, then perform the diagnostics run. + // If any postponed_{repo,alts} builds remained, then perform the + // diagnostics run. Naturally we shouldn't have any postponed_cfgs + // without one of the former. // if (!postponed_repo.empty ()) { @@ -2949,10 +6766,15 @@ namespace bpkg fdb, rpt_depts, apc, + false /* initial_collection */, + replaced_vers, + dep_chain, nullptr, nullptr, 0, - dep_chain); + postponed_deps, + postponed_cfgs, + postponed_poss); assert (false); // Can't be here. } @@ -2966,13 +6788,35 @@ namespace bpkg fdb, rpt_depts, apc, + false /* initial_collection */, + replaced_vers, + dep_chain, nullptr, nullptr, 0, - dep_chain); + postponed_deps, + postponed_cfgs, + postponed_poss); assert (false); // Can't be here. } + + // While the assumption is that we shouldn't leave any non-negotiated + // clusters, we can potentially miss some corner cases in the above + // "skip configuration" logic. Let's thus trace the non-negotiated + // clusters before the assertion. + // +#ifndef NDEBUG + for (const postponed_configuration& cfg: postponed_cfgs) + { + if (!cfg.negotiated || !*cfg.negotiated) + trace << "unexpected non-negotiated cluster " << cfg; + } + + assert (postponed_cfgs.negotiated ()); +#endif + + l5 ([&]{trace << "end" << trace_suffix;}); } // Order the previously-collected package with the specified name @@ -2982,7 +6826,7 @@ namespace bpkg // only the specified configuration. Otherwise, treat the package as a // dependency and use the custom search function to find its build // configuration. Failed that, search for it recursively (see - // config_package_map::find_dependency() for details). + // package_map::find_dependency() for details). // // Recursively order the package dependencies being ordered failing if a // dependency cycle is detected. If reorder is true, then reorder this @@ -2995,7 +6839,7 @@ namespace bpkg const function& fdb, bool reorder = true) { - config_package_names chain; + package_refs chain; return order (db, name, buildtime, chain, fdb, reorder); } @@ -3070,8 +6914,8 @@ namespace bpkg auto i (map_.find (ddb, dn)); // Make sure the up/downgraded package still satisfies this - // dependent. But first "prune" if this is a replaced prerequisite - // of the repointed dependent. + // dependent. But first "prune" if the dependent is being dropped or + // this is a replaced prerequisite of the repointed dependent. // // Note that the repointed dependents are always collected and have // all their collected prerequisites ordered (including new and old @@ -3081,37 +6925,34 @@ namespace bpkg if (i != map_.end () && i->second.position != end ()) { + build_package& dp (i->second.package); + + // Skip the droped dependent. + // + if (dp.action && *dp.action == build_package::drop) + continue; + repointed_dependents::const_iterator j ( - rpt_depts.find (config_package {ddb, dn})); + rpt_depts.find (package_key {ddb, dn})); if (j != rpt_depts.end ()) { - const map& prereqs_flags (j->second); + const map& prereqs_flags (j->second); - auto k (prereqs_flags.find (config_package {pdb, n})); + auto k (prereqs_flags.find (package_key {pdb, n})); if (k != prereqs_flags.end () && !k->second) continue; } - build_package& dp (i->second.package); - const shared_ptr& dsp (dp.selected); - // There is one tricky aspect: the dependent could be in the - // process of being up/downgraded as well. In this case all we - // need to do is detect this situation and skip the test since all - // the (new) contraints of this package have been satisfied in - // collect_build(). + // process of being reconfigured or up/downgraded as well. In this + // case all we need to do is detect this situation and skip the + // test since all the (new) constraints of this package have been + // satisfied in collect_build(). // if (check) - { - check = dp.available == nullptr || - (dsp->system () == dp.system && - dsp->version == dp.available_version () && - (dp.system || - dp.config_vars.empty () || - !has_buildfile_clause (dp.available->dependencies))); - } + check = !dp.dependencies; } if (check) @@ -3140,8 +6981,8 @@ namespace bpkg string rb; if (!p.user_selection ()) { - for (const config_package& cp: p.required_by) - rb += (rb.empty () ? " " : ", ") + cp.string (); + for (const package_key& pk: p.required_by) + rb += (rb.empty () ? " " : ", ") + pk.string (); } if (!rb.empty ()) @@ -3164,7 +7005,9 @@ namespace bpkg { shared_ptr dsp (ddb.load (dn)); - bool system (dsp->system ()); // Save before the move(dsp) call. + // A system package cannot be a dependent. + // + assert (!dsp->system ()); return build_package { build_package::adjust, @@ -3173,19 +7016,21 @@ namespace bpkg nullptr, // No available pkg/repo fragment. nullptr, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. + false, // Recursive collection. nullopt, // Hold package. nullopt, // Hold version. {}, // Constraints. - system, + false, // System. false, // Keep output directory. false, // Disfigure (from-scratch reconf). false, // Configure-only. nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - {config_package {pdb, n}}, // Required by (dependency). + {package_key {pdb, n}}, // Required by (dependency). false, // Required by dependents. build_package::adjust_reconfigure}; }; @@ -3194,11 +7039,10 @@ namespace bpkg // list, the package is in the map (but not on the list) and it // is in neither. // - // If the existing entry is a drop, then we skip it. If it is - // pre-entered, is an adjustment, or is a build that is not supposed - // to be built (not in the list), then we merge it into the new - // adjustment entry. Otherwise (is a build in the list), we just add - // the reconfigure adjustment flag to it. + // If the existing entry is pre-entered, is an adjustment, or is a + // build that is not supposed to be built (not in the list), then we + // merge it into the new adjustment entry. Otherwise (is a build in + // the list), we just add the reconfigure adjustment flag to it. // if (i != map_.end ()) { @@ -3209,11 +7053,6 @@ namespace bpkg *dp.action != build_package::build || // Non-build. dpos == end ()) // Build not in the list. { - // Skip the droped package. - // - if (dp.action && *dp.action == build_package::drop) - continue; - build_package bp (adjustment ()); bp.merge (move (dp)); dp = move (bp); @@ -3246,7 +7085,7 @@ namespace bpkg { // Don't move dn since it is used by adjustment(). // - i = map_.emplace (config_package {ddb, dn}, + i = map_.emplace (package_key {ddb, dn}, data_type {end (), adjustment ()}).first; i->second.position = insert (pos, i->second.package); @@ -3270,38 +7109,281 @@ namespace bpkg map_.clear (); } - void - clear_order () - { - build_package_list::clear (); + void + clear_order () + { + build_package_list::clear (); + + for (auto& p: map_) + p.second.position = end (); + } + + // Verify that builds ordering is consistent across all the data + // structures and the ordering expectations are fulfilled (real build + // actions are all ordered, etc). + // + void + verify_ordering () const + { + for (const auto& b: map_) + { + const build_package& bp (b.second.package); + + auto i (find_if (begin (), end (), + [&bp] (const build_package& p) {return &p == &bp;})); + + // List ordering must properly be reflected in the tree entries. + // + assert (i == b.second.position); + + // Pre-entered builds must never be ordered and the real build actions + // (builds, adjustments, etc) must all be ordered. + // + // Note that the later was not the case until we've implemented + // re-collection from scratch after the package version replacement + // (see replaced_versions for details). Before that the whole + // dependency trees from the being replaced dependent stayed in the + // map. + // + assert (bp.action.has_value () == (i != end ())); + } + } + + private: + // Return the list of existing dependents that has a configuration clause + // for the specified dependency. Skip dependents which are being built and + // require recursive recollection or dropped (present in the map) or + // expected to be built or dropped (present in rpt_depts or replaced_vers). + // + // Optionally, specify the function which can verify the dependent build + // and decide whether to override the default behavior and still add the + // dependent package to the resulting list, returning true in this case. + // + struct existing_dependent + { + reference_wrapper db; + shared_ptr selected; + pair dependency_position; + }; + + using verify_dependent_build_function = bool (const package_key&, + pair); + + vector + query_existing_dependents ( + tracer& trace, + database& db, + const package_name& name, + const replaced_versions& replaced_vers, + const repointed_dependents& rpt_depts, + const function& vdb = nullptr) + { + vector r; + + lazy_shared_ptr sp (db, name); + + for (database& ddb: db.dependent_configs ()) + { + for (auto& pd: query_dependents (ddb, name, db)) + { + shared_ptr dsp ( + ddb.load (pd.name)); + + auto i (dsp->prerequisites.find (sp)); + assert (i != dsp->prerequisites.end ()); + + const auto& pos (i->second.config_position); + + if (pos.first != 0) // Has config clause? + { + package_key pk (ddb, pd.name); + + if (rpt_depts.find (pk) != rpt_depts.end ()) + { + l5 ([&]{trace << "skip repointed existing dependent " << pk + << " of dependency " << name << db;}); + continue; + } + + // Ignore dependent which is already being built or dropped. + // + const build_package* p (entered_build (pk)); + + if (p != nullptr && p->action) + { + bool build; + if (((build = *p->action == build_package::build) && + (p->system || p->recollect_recursively (rpt_depts))) || + *p->action == build_package::drop) + { + if (!build || !vdb || !vdb (pk, pos)) + { + l5 ([&]{trace << "skip being " + << (build ? "built" : "dropped") + << " existing dependent " << pk + << " of dependency " << name << db;}); + continue; + } + } + } + + // Ignore dependent which is expected to be built or dropped. + // + auto vi (replaced_vers.find (pk)); + if (vi != replaced_vers.end () && !vi->second.replaced) + { + bool build (vi->second.available != nullptr); + + l5 ([&]{trace << "skip expected to be " + << (build ? "built" : "dropped") + << " existing dependent " << pk + << " of dependency " << name << db;}); + + continue; + } + + r.push_back (existing_dependent {ddb, move (dsp), pos}); + } + } + } + + return r; + } + + // Update the existing dependent object (previously obtained with the + // query_existing_dependents() call) with the new dependency position and + // collect the dependency referred by this position. Return the pointer to + // the collected build package object. + // + const build_package* + replace_existing_dependent_dependency ( + tracer& trace, + const pkg_build_options& o, + existing_dependent& ed, + pair pos, + const function& fdb, + const repointed_dependents& rpt_depts, + const function& apc, + bool initial_collection, + replaced_versions& replaced_vers, + postponed_configurations& postponed_cfgs) + { + // The repointed dependent cannot be returned by + // query_existing_dependents(). Note that the repointed dependent + // references both old and new prerequisites. + // + assert (rpt_depts.find (package_key (ed.db, ed.selected->name)) == + rpt_depts.end ()); + + shared_ptr dsp; + database* pdb (nullptr); + const version_constraint* vc (nullptr); + + // Find the dependency for this earlier dependency position. We know + // it must be there since it's with configuration. + // + for (const auto& p: ed.selected->prerequisites) + { + if (p.second.config_position == pos) + { + pdb = &p.first.database (); + + dsp = p.first.load (); + + l5 ([&]{trace << "replace dependency at index " + << ed.dependency_position.first + << " of existing dependent " << *ed.selected + << ed.db << " with dependency " << *dsp + << *pdb << " at index " << pos.first;}); + + if (p.second.constraint) + vc = &*p.second.constraint; + } + } + + assert (dsp != nullptr); + + package_key pk (*pdb, dsp->name); + + // Adjust the existing dependent entry. + // + ed.dependency_position = pos; + + // Collect the package build for this dependency. + // + pair, + lazy_shared_ptr> rp ( + find_available_fragment (o, pk.db, dsp)); + + bool system (dsp->system ()); + + package_key dpk (ed.db, ed.selected->name); + + build_package p { + build_package::build, + pk.db, + move (dsp), + move (rp.first), + move (rp.second), + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + system, // System. + false, // Keep output directory. + false, // Disfigure (from-scratch reconf). + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {dpk}, // Required by (dependent). + true, // Required by dependents. + build_package::adjust_reconfigure}; + + if (vc != nullptr) + p.constraints.emplace_back (dpk.db, dpk.name.string (), *vc); - for (auto& p: map_) - p.second.position = end (); + // Note: not recursive. + // + collect_build (o, + move (p), + fdb, + rpt_depts, + apc, + initial_collection, + replaced_vers, + postponed_cfgs); + + return entered_build (pk); } private: - struct config_package_name + struct package_ref { database& db; const package_name& name; bool - operator== (const config_package_name& v) + operator== (const package_ref& v) { return name == v.name && db == v.db; } }; - using config_package_names = small_vector; + using package_refs = small_vector; iterator order (database& db, const package_name& name, optional buildtime, - config_package_names& chain, + package_refs& chain, const function& fdb, bool reorder) { - config_package_map::iterator mi; + package_map::iterator mi; if (buildtime) { @@ -3326,7 +7408,7 @@ namespace bpkg // Make sure there is no dependency cycle. // - config_package_name cp {pdb, name}; + package_ref cp {pdb, name}; { auto i (find (chain.begin (), chain.end (), cp)); @@ -3335,7 +7417,7 @@ namespace bpkg diag_record dr (fail); dr << "dependency cycle detected involving package " << name << pdb; - auto nv = [this] (const config_package_name& cp) + auto nv = [this] (const package_ref& cp) { auto mi (map_.find (cp.db, cp.name)); assert (mi != map_.end ()); @@ -3554,15 +7636,17 @@ namespace bpkg build_package package; }; - class config_package_map: public map + class package_map: public map { public: - using base_type = map; + using base_type = map; + + using base_type::find; iterator find (database& db, const package_name& pn) { - return base_type::find (config_package {db, pn}); + return find (package_key {db, pn}); } // Try to find a package build in the dependency configurations (see @@ -3587,7 +7671,7 @@ namespace bpkg else fail << "building package " << pn << " in multiple " << "configurations" << - info << r->first.db.config_orig << + info << r->first.db.get().config_orig << info << ldb.config_orig << info << "use --config-* to select package configuration"; } @@ -3596,7 +7680,7 @@ namespace bpkg return r; } }; - config_package_map map_; + package_map map_; }; // Return a patch version constraint for the selected package if it has a @@ -3699,19 +7783,19 @@ namespace bpkg bool system; }; - struct config_package_dependent + struct dependent_constraint { database& db; shared_ptr package; optional constraint; - config_package_dependent (database& d, - shared_ptr p, - optional c) + dependent_constraint (database& d, + shared_ptr p, + optional c) : db (d), package (move (p)), constraint (move (c)) {} }; - using config_package_dependents = vector; + using dependent_constraints = vector; static optional evaluate_dependency (database&, @@ -3723,7 +7807,7 @@ namespace bpkg bool patch, bool explicitly, const config_repo_fragments&, - const config_package_dependents&, + const dependent_constraints&, bool ignore_unsatisfiable); // If there are no user expectations regarding this dependency, then we give @@ -3927,7 +8011,7 @@ namespace bpkg // dependency. // config_repo_fragments repo_frags; - config_package_dependents dependents; + dependent_constraints dpt_constrs; for (auto& pd: pds) { @@ -3941,7 +8025,7 @@ namespace bpkg available_package_id (p->name, p->version), repo_frags); - dependents.emplace_back (ddb, move (p), move (dep.constraint)); + dpt_constrs.emplace_back (ddb, move (p), move (dep.constraint)); } return evaluate_dependency (db, @@ -3953,7 +8037,7 @@ namespace bpkg i->patch, true /* explicitly */, repo_frags, - dependents, + dpt_constrs, ignore_unsatisfiable); } @@ -3990,7 +8074,7 @@ namespace bpkg bool patch, bool explicitly, const config_repo_fragments& rfs, - const config_package_dependents& dependents, + const dependent_constraints& dpt_constrs, bool ignore_unsatisfiable) { tracer trace ("evaluate_dependency"); @@ -4009,8 +8093,10 @@ namespace bpkg // Build the list of available packages for the potential up/down-grade // to, in the version-descending order. If patching, then we constrain the // choice with the latest patch version and place no constraints if - // upgrading. For a system package we also put no constraints just to make - // sure that the package is recognized. + // upgrading. For a system package we will try to find the available + // package that matches the user-specified system version (preferable for + // the configuration negotiation machinery) and, if fail, fallback to + // picking the latest one just to make sure the package is recognized. // optional c; @@ -4029,13 +8115,16 @@ namespace bpkg } } } - else if (!dsys) + else if (!dsys || !wildcard (*dvc)) c = dvc; vector, lazy_shared_ptr>> afs ( find_available (nm, c, rfs)); + if (afs.empty () && dsys && c) + afs = find_available (nm, nullopt, rfs); + // Go through up/down-grade candidates and pick the first one that // satisfies all the dependents. Collect (and sort) unsatisfied dependents // per the unsatisfiable version in case we need to print them. @@ -4090,7 +8179,7 @@ namespace bpkg bool satisfactory (true); sp_set unsatisfied_dependents; - for (const auto& dp: dependents) + for (const auto& dp: dpt_constrs) { if (!satisfies (av, dp.constraint)) { @@ -4327,7 +8416,7 @@ namespace bpkg // dependency. // config_repo_fragments repo_frags; - config_package_dependents dependents; + dependent_constraints dpt_constrs; // Only collect repository fragments (for best version selection) of // (immediate) dependents that have a hit (direct or indirect) in recs. @@ -4341,7 +8430,7 @@ namespace bpkg { shared_ptr p (ddb.load (pd.name)); - dependents.emplace_back (ddb, p, move (pd.constraint)); + dpt_constrs.emplace_back (ddb, p, move (pd.constraint)); if (optional u = upgrade_dependencies (ddb, pd.name, recs)) { @@ -4380,7 +8469,7 @@ namespace bpkg !*upgrade /* patch */, false /* explicitly */, repo_frags, - dependents, + dpt_constrs, ignore_unsatisfiable)); // Translate the "no change" result into nullopt. @@ -5564,7 +9653,7 @@ namespace bpkg // List of package configurations specified on the command line. // - vector pkg_confs; + vector pkg_confs; // Separate the packages specified on the command line into to hold and to // up/down-grade as dependencies, and save dependents whose dependencies @@ -5578,16 +9667,16 @@ namespace bpkg // Check if the package is a duplicate. Return true if it is but // harmless. // - struct config_package_key // Like config_package but with NULL'able db. + struct sys_package_key // Like package_key but with NULL'able db. { package_name name; database* db; // Can be NULL for system dependency. - config_package_key (package_name n, database* d) + sys_package_key (package_name n, database* d) : name (move (n)), db (d) {} bool - operator< (const config_package_key& v) const + operator< (const sys_package_key& v) const { if (int r = name.compare (v.name)) return r < 0; @@ -5598,14 +9687,14 @@ namespace bpkg } }; - map package_map; + map package_map; auto check_dup = [&package_map, &arg_string, &arg_parsed] (const pkg_arg& pa) -> bool { assert (arg_parsed (pa)); - auto r (package_map.emplace (config_package_key {pa.name, pa.db}, pa)); + auto r (package_map.emplace (sys_package_key {pa.name, pa.db}, pa)); const pkg_arg& a (r.first->second); assert (arg_parsed (a)); @@ -5852,14 +9941,19 @@ namespace bpkg lazy_shared_ptr root (*pdb, empty_string); // Either get the user-specified version or the latest allowed - // for a source code package. For a system package we pick the - // latest one just to make sure the package is recognized. + // for a source code package. For a system package we will try + // to find the available package that matches the user-specified + // system version (preferable for the configuration negotiation + // machinery) and, if fail, fallback to picking the latest one + // just to make sure the package is recognized. // optional c; + bool sys (arg_sys (pa)); + if (!pa.constraint) { - assert (!arg_sys (pa)); + assert (!sys); if (pa.options.patch () && (sp = pdb->find (pa.name)) != nullptr) @@ -5878,11 +9972,14 @@ namespace bpkg patch = true; } } - else if (!arg_sys (pa)) + else if (!sys || !wildcard (*pa.constraint)) c = pa.constraint; auto rp (find_available_one (pa.name, c, root)); + if (rp.first == nullptr && sys && c) + rp = find_available_one (pa.name, nullopt, root); + ap = move (rp.first); af = move (rp.second); } @@ -5918,8 +10015,7 @@ namespace bpkg if (r) { - l4 ([&]{trace << "stashing recursive package " - << arg_string (pa);}); + l4 ([&]{trace << "stash recursive package " << arg_string (pa);}); // The above options are meaningless for system packages, so we // just ignore them for a system dependency with unspecified @@ -5934,8 +10030,7 @@ namespace bpkg // if (pa.options.dependency ()) { - l4 ([&]{trace << "stashing dependency package " - << arg_string (pa);}); + l4 ([&]{trace << "stash dependency package " << arg_string (pa);}); bool sys (arg_sys (pa)); @@ -6146,8 +10241,10 @@ namespace bpkg move (ap), move (af), nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. + false, // Recursive collection. true, // Hold package. pa.constraint.has_value (), // Hold version. {}, // Constraints. @@ -6160,11 +10257,11 @@ namespace bpkg : optional ()), pa.options.checkout_purge (), move (pa.config_vars), - {config_package {mdb, ""}}, // Required by (command line). + {package_key {mdb, ""}}, // Required by (command line). false, // Required by dependents. 0}; // State flags. - l4 ([&]{trace << "stashing held package " + l4 ([&]{trace << "stash held package " << p.available_name_version_db ();}); // "Fix" the version the user asked for by adding the constraint. @@ -6256,24 +10353,26 @@ namespace bpkg move (sp), move (ap), move (apr.second), - nullopt, // Dependencies. - nullopt, // Package skeleton. - nullopt, // Postponed dependency alternatives. - true, // Hold package. - false, // Hold version. - {}, // Constraints. - false, // System package. + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + true, // Hold package. + false, // Hold version. + {}, // Constraints. + false, // System package. keep_out, o.disfigure (), - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - {config_package {mdb, ""}}, // Required by (command line). - false, // Required by dependents. - 0}; // State flags. - - l4 ([&]{trace << "stashing held package " + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {package_key {mdb, ""}}, // Required by (command line). + false, // Required by dependents. + 0}; // State flags. + + l4 ([&]{trace << "stash held package " << p.available_name_version_db ();}); hold_pkgs.push_back (move (p)); @@ -6323,18 +10422,18 @@ namespace bpkg linked_databases ddbs (db.dependency_configs (nm, buildtime)); - for (const config_package& cp: pkg_confs) + for (const package_key& p: pkg_confs) { - if (cp.name == nm && - find (ddbs.begin (), ddbs.end (), cp.db) != ddbs.end ()) + if (p.name == nm && + find (ddbs.begin (), ddbs.end (), p.db) != ddbs.end ()) { if (r == nullptr) - r = &cp.db; + r = &p.db.get (); else - fail << "multiple " << cp.db.type << " configurations " + fail << "multiple " << p.db.get ().type << " configurations " << "specified for package " << nm << info << r->config_orig << - info << cp.db.config_orig; + info << p.db.get ().config_orig; } } @@ -6401,6 +10500,10 @@ namespace bpkg }; vector deps; + replaced_versions replaced_vers; + postponed_dependencies postponed_deps; + postponed_positions postponed_poss; + // Map the repointed dependents to the replacement flags (see // repointed_dependents for details), unless --no-move is specified. // @@ -6427,7 +10530,7 @@ namespace bpkg for (shared_ptr sp: pointer_result (cdb.query (q))) { - map ps; // Old/new prerequisites. + map ps; // Old/new prerequisites. for (const auto& p: sp->prerequisites) { @@ -6447,13 +10550,13 @@ namespace bpkg if (pdb != nullptr && *pdb != db && pdb->type == db.type) { - ps.emplace (config_package {*pdb, name}, true); - ps.emplace (config_package { db, name}, false); + ps.emplace (package_key {*pdb, name}, true); + ps.emplace (package_key { db, name}, false); } } if (!ps.empty ()) - rpt_depts.emplace (config_package {cdb, sp->name}, move (ps)); + rpt_depts.emplace (package_key {cdb, sp->name}, move (ps)); } } @@ -6462,9 +10565,23 @@ namespace bpkg // Iteratively refine the plan with dependency up/down-grades/drops. // - for (bool refine (true), scratch (true); refine; ) + // Note that we should not clean the deps list on scratch_col (scratch + // during the package collection) because we want to enter them before + // collect_build_postponed() and they could be the dependents that have + // the config clauses. In a sense, change to replaced_vers, + // postponed_deps, or postponed_poss maps should not affect the deps + // list. But not the other way around: a dependency erased from the deps + // list could have caused an entry in the replaced_vers, postponed_deps, + // and/or postponed_poss maps. And so we clean replaced_vers, + // postponed_deps, and postponed_poss on scratch_exe (scratch during the + // plan execution). + // + for (bool refine (true), scratch_exe (true), scratch_col (false); + refine; ) { - l4 ([&]{trace << "refining execution plan" + bool scratch (scratch_exe || scratch_col); + + l4 ([&]{trace << "refine package collection/plan execution" << (scratch ? " from scratch" : "");}); transaction t (mdb); @@ -6486,38 +10603,81 @@ namespace bpkg // Temporarily add the replacement prerequisites to the repointed // dependent prerequisites sets and persist the changes. // - // Note that we don't copy the prerequisite constraints into the - // replacements, since they are unused in the collecting/ordering - // logic. - // for (auto& rd: rpt_depts) { database& db (rd.first.db); const package_name& nm (rd.first.name); shared_ptr sp (db.load (nm)); + package_prerequisites& prereqs (sp->prerequisites); for (const auto& prq: rd.second) { if (prq.second) // Prerequisite replacement? { - const config_package& cp (prq.first); + const package_key& p (prq.first); + + // Find the being replaced prerequisite to copy it's information + // into the replacement. + // + auto i (find_if (prereqs.begin (), prereqs.end (), + [&p] (const auto& pr) + { + return pr.first.object_id () == p.name; + })); + + assert (i != prereqs.end ()); - auto i (sp->prerequisites.emplace ( - lazy_shared_ptr (cp.db, cp.name), - nullopt)); + auto j (prereqs.emplace ( + lazy_shared_ptr (p.db.get (), + p.name), + i->second)); // The selected package should only contain the old // prerequisites at this time, so adding a replacement should // always succeed. // - assert (i.second); + assert (j.second); } } db.update (sp); } + // Erase the replacements from the repointed dependents prerequisite + // sets and persist the changes. + // + auto restore_repointed_dependents = [&rpt_depts] () + { + for (auto& rd: rpt_depts) + { + database& db (rd.first.db); + const package_name& nm (rd.first.name); + + shared_ptr sp (db.load (nm)); + + for (const auto& prq: rd.second) + { + if (prq.second) // Prerequisite replacement? + { + const package_key& p (prq.first); + + size_t n ( + sp->prerequisites.erase ( + lazy_shared_ptr (p.db.get (), p.name))); + + // The selected package should always contain the prerequisite + // replacement at this time, so its removal should always + // succeed. + // + assert (n == 1); + } + } + + db.update (sp); + } + }; + // Pre-enter dependency to keep track of the desired versions and // options specified on the command line. In particular, if the // version is specified and the dependency is used as part of the @@ -6528,8 +10688,7 @@ namespace bpkg // Also, if a dependency package already has selected package that // is held, then we need to unhold it. // - auto enter = [&mdb, &pkgs] (database& db, - const dependency_package& p) + auto enter = [&mdb, &pkgs] (database& db, const dependency_package& p) { build_package bp { nullopt, // Action. @@ -6538,8 +10697,10 @@ namespace bpkg nullptr, // Available package/repo fragment. nullptr, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. + false, // Recursive collection. false, // Hold package. p.constraint.has_value (), // Hold version. {}, // Constraints. @@ -6550,7 +10711,7 @@ namespace bpkg p.checkout_root, p.checkout_purge, p.config_vars, - {config_package {mdb, ""}}, // Required by (command line). + {package_key {mdb, ""}}, // Required by (command line). false, // Required by dependents. 0}; // State flags. @@ -6620,179 +10781,329 @@ namespace bpkg } }); - build_packages::postponed_packages postponed_repo; - build_packages::postponed_packages postponed_alts; + postponed_packages postponed_repo; + postponed_packages postponed_alts; + postponed_configurations postponed_cfgs; + strings postponed_cfgs_history; - if (scratch) + try { - pkgs.clear (); - - // Pre-enter dependencies with specified configurations. - // - for (const dependency_package& p: dep_pkgs) + if (scratch) { - if (p.db != nullptr) - enter (*p.db, p); - } + pkgs.clear (); - // Pre-enter system dependencies with unspecified configuration for - // all dependency configurations, excluding those which already have - // this dependency pre-entered. - // - for (const dependency_package& p: dep_pkgs) - { - if (p.db == nullptr) + if (scratch_exe) + { + replaced_vers.clear (); + postponed_deps.clear (); + postponed_poss.clear (); + + scratch_exe = false; + } + else if (scratch_col) + { + // Reset to detect bogus entries. + // + for (auto& rv: replaced_vers) + rv.second.replaced = false; + + for (auto& pd: postponed_deps) + { + pd.second.wout_config = false; + pd.second.with_config = false; + } + + for (auto& pd: postponed_poss) + { + pd.second.skipped = false; + pd.second.reevaluated = false; + } + + scratch_col = false; + } + + // Pre-enter dependencies with specified configurations. + // + for (const dependency_package& p: dep_pkgs) + { + if (p.db != nullptr) + enter (*p.db, p); + } + + // Pre-enter system dependencies with unspecified configuration + // for all dependency configurations, excluding those which + // already have this dependency pre-entered. + // + for (const dependency_package& p: dep_pkgs) + { + if (p.db == nullptr) + { + for (database& db: dep_dbs) + { + if (!pkgs.entered_build (db, p.name)) + enter_system_dependency (db, p); + } + } + } + + // Pre-collect user selection to make sure dependency-forced + // up/down-grades are handled properly (i.e., the order in which we + // specify packages on the command line does not matter). + // + for (const build_package& p: hold_pkgs) + pkgs.collect_build (o, + p, + find_prereq_database, + rpt_depts, + add_priv_cfg, + true /* initial_collection */, + replaced_vers, + postponed_cfgs); + + // Collect all the prerequisites of the user selection. + // + // Note that some of the user-selected packages can well be + // dependencies whose recursive processing should be postponed. + // + for (const build_package& p: hold_pkgs) + { + package_key pk (p.db, p.name ()); + + auto i (postponed_deps.find (pk)); + + if (i == postponed_deps.end ()) + { + pkgs.collect_build_prerequisites ( + o, + p.db, + p.name (), + find_prereq_database, + rpt_depts, + add_priv_cfg, + true /* initial_collection */, + replaced_vers, + postponed_repo, + postponed_alts, + 0 /* max_alt_index */, + postponed_deps, + postponed_cfgs, + postponed_poss); + } + else + { + // Even though the user selection may have a configuration, we + // treat it as a dependent without any configuration because + // it is non-negotiable, known at the outset, and thus cannot + // be a reason to postpone anything. + // + i->second.wout_config = true; + + l5 ([&]{trace << "dep-postpone user-specified " << pk;}); + } + } + + // Note that we need to collect unheld after prerequisites, not to + // overwrite the pre-entered entries before they are used to + // provide additional constraints for the collected prerequisites. + // + for (const dependency_package& p: dep_pkgs) { - for (database& db: dep_dbs) + auto unhold = [&p, &pkgs] (database& db) + { + shared_ptr sp ( + p.db != nullptr + ? p.selected + : db.find (p.name)); + + if (sp != nullptr && sp->hold_package) + pkgs.collect_unhold (db, sp); + }; + + if (p.db != nullptr) + { + unhold (*p.db); + } + else { - if (!pkgs.entered (db, p.name)) - enter_system_dependency (db, p); + for (database& db: dep_dbs) + unhold (db); } } + + // Collect dependents whose dependencies need to be repointed to + // packages from different configurations. + // + pkgs.collect_repointed_dependents (o, + rpt_depts, + replaced_vers, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs, + postponed_poss, + find_prereq_database, + add_priv_cfg); } + else + pkgs.clear_order (); // Only clear the ordered list. - // Pre-collect user selection to make sure dependency-forced - // up/down-grades are handled properly (i.e., the order in which we - // specify packages on the command line does not matter). - // - for (const build_package& p: hold_pkgs) - pkgs.collect_build (o, - p, - find_prereq_database, - rpt_depts, - add_priv_cfg); - - // Collect all the prerequisites of the user selection. + // Add to the plan dependencies to up/down-grade/drop that were + // discovered on the previous iterations. // - for (const build_package& p: hold_pkgs) - pkgs.collect_build_prerequisites (o, - p.db, - p.name (), - postponed_repo, - postponed_alts, - 0 /* max_alt_index */, - find_prereq_database, - rpt_depts, - add_priv_cfg); - - // Note that we need to collect unheld after prerequisites, not to - // overwrite the pre-entered entries before they are used to provide - // additional constraints for the collected prerequisites. + // Note: this loop takes care of both the from-scratch and + // refinement cases. // - for (const dependency_package& p: dep_pkgs) + for (const dep& d: deps) { - auto unhold = [&p, &pkgs] (database& db) - { - shared_ptr sp ( - p.db != nullptr - ? p.selected - : db.find (p.name)); - - if (sp != nullptr && sp->hold_package) - pkgs.collect_unhold (db, sp); - }; + database& ddb (d.db); - if (p.db != nullptr) + if (d.available == nullptr) { - unhold (*p.db); + pkgs.collect_drop (o, + ddb, + ddb.load (d.name), + replaced_vers); } else { - for (database& db: dep_dbs) - unhold (db); + shared_ptr sp ( + ddb.find (d.name)); + + // We will keep the output directory only if the external package + // is replaced with an external one (see above for details). + // + bool keep_out (o.keep_out () && sp->external ()); + + // Marking upgraded dependencies as "required by command line" + // may seem redundant as they should already be pre-entered as + // such (see above). But remember dependencies upgraded with + // -i|-r? Note that the required_by data member should never be + // empty, as it is used in prompts/diagnostics. + // + build_package p { + build_package::build, + ddb, + move (sp), + d.available, + d.repository_fragment, + nullopt, // Dependencies. + nullopt, // Dependencies alternatives. + nullopt, // Package skeleton. + nullopt, // Postponed dependency alternatives. + false, // Recursive collection. + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + d.system, + keep_out, + o.disfigure (), + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {package_key {mdb, ""}}, // Required by (command line). + false, // Required by dependents. + 0}; // State flags. + + build_package_refs dep_chain; + + // Note: recursive. + // + pkgs.collect_build (o, + move (p), + find_prereq_database, + rpt_depts, + add_priv_cfg, + true /* initial_collection */, + replaced_vers, + postponed_cfgs, + &dep_chain, + &postponed_repo, + &postponed_alts, + &postponed_deps, + &postponed_poss); } } - // Collect dependents whose dependencies need to be repointed to - // packages from different configurations. + // Erase the bogus postponements and re-collect from scratch, if any + // (see postponed_dependencies for details). // - pkgs.collect_repointed_dependents (o, - rpt_depts, - postponed_repo, - postponed_alts, - find_prereq_database, - add_priv_cfg); + // Note that we used to re-collect such postponements in-place but + // re-doing from scratch feels more correct (i.e., we may end up + // doing it earlier which will affect dependency alternatives). + // + postponed_deps.cancel_bogus (trace, true /* initial_collection */); - scratch = false; - } - else - pkgs.clear_order (); // Only clear the ordered list. + // Now remove all the dependencies postponed during the initial + // collection since all this information is already in + // postponed_cfgs. + // + for (auto i (postponed_deps.begin ()); i != postponed_deps.end (); ) + { + if (i->second.initial_collection) + i = postponed_deps.erase (i); + else + ++i; + } - // Add to the plan dependencies to up/down-grade/drop that were - // discovered on the previous iterations. - // - for (const dep& d: deps) + // Handle the (combined) postponed collection. + // + if (!postponed_repo.empty () || + !postponed_alts.empty () || + postponed_deps.has_bogus () || + !postponed_cfgs.empty ()) + pkgs.collect_build_postponed (o, + replaced_vers, + postponed_repo, + postponed_alts, + postponed_deps, + postponed_cfgs, + postponed_cfgs_history, + postponed_poss, + find_prereq_database, + rpt_depts, + add_priv_cfg); + + // Erase the bogus replacements and re-collect from scratch, if any + // (see replaced_versions for details). + // + replaced_vers.cancel_bogus (trace, true /* scratch */); + + // Erase the bogus existing dependent re-evaluation postponements + // and re-collect from scratch, if any (see postponed_positions for + // details). + // + postponed_poss.cancel_bogus (trace); + } + catch (const scratch_collection& e) { - database& ddb (d.db); + // Re-collect from scratch (but keep deps). + // + scratch_col = true; - if (d.available == nullptr) - { - pkgs.collect_drop (ddb, ddb.load (d.name)); - } - else - { - shared_ptr sp ( - ddb.find (d.name)); + l5 ([&]{trace << "collection failed due to " << e.description + << (e.package != nullptr + ? " (" + e.package->string () + ")" + : empty_string) + << ", retry from scratch";}); - // We will keep the output directory only if the external package - // is replaced with an external one (see above for details). - // - bool keep_out (o.keep_out () && sp->external ()); + // Erase the package version replacements that we didn't apply + // during the current (re-)collection iteration since the dependents + // demanding this version are not collected anymore. + // + replaced_vers.cancel_bogus (trace, false /* scratch */); - // Marking upgraded dependencies as "required by command line" may - // seem redundant as they should already be pre-entered as such - // (see above). But remember dependencies upgraded with -i|-r? - // Note that the required_by data member should never be empty, as - // it is used in prompts/diagnostics. - // - build_package p { - build_package::build, - ddb, - move (sp), - d.available, - d.repository_fragment, - nullopt, // Dependencies. - nullopt, // Package skeleton. - nullopt, // Postponed dependency alternatives. - nullopt, // Hold package. - nullopt, // Hold version. - {}, // Constraints. - d.system, - keep_out, - o.disfigure (), - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - {config_package {mdb, ""}}, // Required by (command line). - false, // Required by dependents. - 0}; // State flags. + restore_repointed_dependents (); - build_package_refs dep_chain; + // Commit linking of private configurations that were potentially + // created during the collection of the package builds with their + // parent configurations. + // + t.commit (); - // Note: recursive. - // - pkgs.collect_build (o, - move (p), - find_prereq_database, - rpt_depts, - add_priv_cfg, - &postponed_repo, - &postponed_alts, - &dep_chain); - } + continue; } - // Handle the (combined) postponed collection. - // - if (!postponed_repo.empty () || !postponed_alts.empty ()) - pkgs.collect_build_postponed (o, - postponed_repo, - postponed_alts, - find_prereq_database, - rpt_depts, - add_priv_cfg); - // Now that we have collected all the package versions that we need to // build, arrange them in the "dependency order", that is, with every // package on the list only possibly depending on the ones after @@ -6826,6 +11137,22 @@ namespace bpkg find_prereq_database, false /* reorder */); + for (const postponed_configuration& cfg: postponed_cfgs) + { + for (const auto& d: cfg.dependents) + { + if (d.second.existing) + { + const package_key& p (d.first); + + pkgs.order (p.db, + p.name, + nullopt /* buildtime */, + find_prereq_database); + } + } + } + // Collect and order all the dependents that we will need to // reconfigure because of the up/down-grades of packages that are now // on the list. @@ -6863,36 +11190,14 @@ namespace bpkg } } +#ifndef NDEBUG + pkgs.verify_ordering (); +#endif // Now, as we are done with package builds collecting/ordering, erase // the replacements from the repointed dependents prerequisite sets // and persist the changes. // - for (auto& rd: rpt_depts) - { - database& db (rd.first.db); - const package_name& nm (rd.first.name); - - shared_ptr sp (db.load (nm)); - - for (const auto& prq: rd.second) - { - if (prq.second) // Prerequisite replacement? - { - const config_package& cp (prq.first); - - size_t n (sp->prerequisites.erase ( - lazy_shared_ptr (cp.db, cp.name))); - - // The selected package should always contain the prerequisite - // replacement at this time, so its removal should always - // succeed. - // - assert (n == 1); - } - } - - db.update (sp); - } + restore_repointed_dependents (); // We are about to execute the plan on the database (but not on the // filesystem / actual packages). Save the session state for the @@ -7034,7 +11339,7 @@ namespace bpkg if (s) { - scratch = true; // Rebuild the plan from scratch. + scratch_exe = true; // Rebuild the plan from scratch. i = deps.erase (i); } else @@ -7052,13 +11357,13 @@ namespace bpkg // if (!changed && dep_pkgs.empty () && rec_pkgs.empty ()) { - assert (!scratch); // No reason to change any previous decision. + assert (!scratch_exe); // No reason to change any previous decision. if (o.keep_unused () || o.no_refinement ()) refine = false; } - if (!scratch && refine) + if (!scratch_exe && refine) { // First, we check if the refinement is required, ignoring the // unsatisfiable dependency version constraints. If we end up @@ -7152,7 +11457,7 @@ namespace bpkg using prerequisites = set, compare_lazy_ptr_id>; - map package_prereqs; + map package_prereqs; small_vector chain; auto verify_dependencies = [&package_prereqs, &chain] @@ -7163,9 +11468,9 @@ namespace bpkg { // Return the cached value, if present. // - config_package cp {db, sp->name}; + package_key pk {db, sp->name}; { - auto i (package_prereqs.find (cp)); + auto i (package_prereqs.find (pk)); if (i != package_prereqs.end ()) return i->second; @@ -7208,15 +11513,15 @@ namespace bpkg // // Note, however, that we cannot easily determine if the // prerequisite corresponds to the runtime or build-time - // dependency, since we only store its version constraint. The - // current implementation relies on the fact that the build-time - // dependency configuration type (host or build2) differs from the - // dependent configuration type (target is a common case) and - // doesn't work well, for example, for the self-hosted - // configurations. For them it can fail erroneously. We can - // potentially fix that by additionally storing the build-time - // flag besides the version constraint. However, let's first see - // if it ever becomes a problem. + // dependency, since we don't store this information for + // prerequisites. The current implementation relies on the fact + // that the build-time dependency configuration type (host or + // build2) differs from the dependent configuration type (target + // is a common case) and doesn't work well, for example, for the + // self-hosted configurations. For them it can fail + // erroneously. We can potentially fix that by additionally + // storing the build-time flag for a prerequisite. However, let's + // first see if it ever becomes a problem. // prerequisites r; const package_prerequisites& prereqs (sp->prerequisites); @@ -7275,7 +11580,7 @@ namespace bpkg // Cache the resulting package prerequisites set and return a // reference to it. // - auto j (package_prereqs.emplace (move (cp), move (r))); + auto j (package_prereqs.emplace (move (pk), move (r))); assert (j.second); // A package cannot depend on itself. return j.first->second; @@ -7298,22 +11603,22 @@ namespace bpkg // List of module packages together with the linked configuration // clusters they belong to. // - vector> build2_mods; + vector> build2_mods; for (const auto& pp: package_prereqs) { - const config_package& cp (pp.first); + const package_key& pk (pp.first); // Skip packages other than the build2 modules. // - if (!build2_module (cp.name)) + if (!build2_module (pk.name)) continue; // Skip build2 modules configured as system. // { shared_ptr sp ( - cp.db.find (cp.name)); + pk.db.get ().find (pk.name)); assert (sp != nullptr); @@ -7326,28 +11631,28 @@ namespace bpkg // for (const auto& m: build2_mods) { - if (m.first.name != cp.name) + if (m.first.name != pk.name) continue; // The `package_prereqs` map can only contain the same package // twice if databases differ. // - assert (m.first.db != cp.db); + assert (m.first.db != pk.db); const linked_databases& lcc (m.second); - if (find (lcc.begin (), lcc.end (), cp.db) != lcc.end ()) + if (find (lcc.begin (), lcc.end (), pk.db) != lcc.end ()) { - fail << "building build system module " << cp.name + fail << "building build system module " << pk.name << " in multiple configurations" << - info << m.first.db.config_orig << - info << cp.db.config_orig; + info << m.first.db.get ().config_orig << + info << pk.db.get ().config_orig; } } // Add the module and its cluster to the list. // - build2_mods.emplace_back (cp, cp.db.cluster_configs ()); + build2_mods.emplace_back (pk, pk.db.get ().cluster_configs ()); } } } @@ -7507,6 +11812,13 @@ namespace bpkg // While at it, detect if we have any dependents that the user may want to // update. // + // For the packages being printed also print the configuration specified + // by the user, dependents, and via the reflect clauses. For that we will + // use the package skeletons, initializing them if required. Note that for + // a system package the skeleton may already be initialized during the + // dependency negotiation process. Also note that the freshly-initialized + // skeletons will be reused during the plan execution. + // bool update_dependents (false); // We need the plan and to ask for the user's confirmation only if some @@ -7524,17 +11836,22 @@ namespace bpkg o.plan_specified () || o.rebuild_checksum_specified ()) { + // Start the transaction since we may query available packages for + // skeleton initializations. + // + transaction t (mdb); + bool first (true); // First entry in the plan. - for (const build_package& p: reverse_iterate (pkgs)) + for (build_package& p: reverse_iterate (pkgs)) { + assert (p.action); + database& pdb (p.db); const shared_ptr& sp (p.selected); string act; - assert (p.action); - if (*p.action == build_package::drop) { act = "drop " + sp->string (pdb) + " (unused)"; @@ -7542,6 +11859,13 @@ namespace bpkg } else { + // Print configuration variables. + // + // The idea here is to only print configuration for those packages + // for which we call pkg_configure*() in execute_plan(). + // + package_skeleton* cfg (nullptr); + string cause; if (*p.action == build_package::adjust) { @@ -7577,14 +11901,43 @@ namespace bpkg const string& s (pdb.string); if (!s.empty ()) act += ' ' + s; + + // This is an adjustment and so there is no available package + // specified for the build package object and thus the skeleton + // cannot be present. + // + assert (p.available == nullptr && !p.skeleton); + + // We shouldn't be printing configurations for plain unholds. + // + if (p.reconfigure ()) + { + // Since there is no available package specified we need to find + // it (or create a transient one). + // + cfg = &p.init_skeleton (o, find_available (o, pdb, sp)); + } } else { + assert (p.available != nullptr); // This is a package build. + // Even if we already have this package selected, we have to // make sure it is configured and updated. // if (sp == nullptr) + { act = p.system ? "configure" : "new"; + + // For a new non-system package the skeleton must already be + // initialized. + // + assert (p.system || p.skeleton.has_value ()); + + // Initialize the skeleton if it is not initialized yet. + // + cfg = &(p.skeleton ? *p.skeleton : p.init_skeleton (o)); + } else if (sp->version == p.available_version ()) { // If this package is already configured and is not part of the @@ -7606,6 +11959,13 @@ namespace bpkg ? "reconfigure" : "reconfigure/update") : "update"); + + if (p.reconfigure ()) + { + // Initialize the skeleton if it is not initialized yet. + // + cfg = &(p.skeleton ? *p.skeleton : p.init_skeleton (o)); + } } else { @@ -7615,6 +11975,15 @@ namespace bpkg ? "upgrade" : "downgrade"; + // For a non-system package up/downgrade the skeleton must + // already be initialized. + // + assert (p.system || p.skeleton.has_value ()); + + // Initialize the skeleton if it is not initialized yet. + // + cfg = &(p.skeleton ? *p.skeleton : p.init_skeleton (o)); + need_prompt = true; } @@ -7637,8 +12006,8 @@ namespace bpkg // dependent-dependency structure change without any of the // package versions changing? Doesn't feel like it should. // - for (const config_package& cp: p.required_by) - rb += (rb.empty () ? " " : ", ") + cp.string (); + for (const package_key& pk: p.required_by) + rb += (rb.empty () ? " " : ", ") + pk.string (); // If not user-selected, then there should be another (implicit) // reason for the action. @@ -7650,6 +12019,14 @@ namespace bpkg if (!rb.empty ()) act += " (" + cause + rb + ')'; + + if (cfg != nullptr && !cfg->empty_print ()) + { + ostringstream os; + cfg->print_config (os, o.print_only () ? " " : " "); + act += '\n'; + act += os.str (); + } } if (first) @@ -7677,6 +12054,8 @@ namespace bpkg if (o.rebuild_checksum_specified ()) csum.append (act); } + + t.commit (); } if (o.rebuild_checksum_specified ()) @@ -7882,7 +12261,7 @@ namespace bpkg } if (*p.action != build_package::drop && - !p.skeleton && + !p.dependencies && !sp->prerequisites.empty ()) { vector& ps (previous_prerequisites[&p]); @@ -7913,7 +12292,6 @@ namespace bpkg assert (sp->state == package_state::unpacked || sp->state == package_state::transient); - if (result || progress) { const char* what (sp->state == package_state::transient @@ -8280,6 +12658,9 @@ namespace bpkg // source code one, or just available, in which case it is a system // one. Note that a system package gets selected as being configured. // + // NOTE: remember to update the preparation of the plan to be presented + // to the user if changing anything here. + // assert (sp != nullptr || p.system); database& pdb (p.db); @@ -8314,9 +12695,11 @@ namespace bpkg } else if (ap != nullptr) { + assert (*p.action == build_package::build); + // If the package prerequisites builds are collected, then use the - // resulting package skeleton and dependency list for optimization - // (not to re-evaluate enable conditions, etc). + // resulting package skeleton and the pre-selected dependency + // alternatives. // // Note that we may not collect the package prerequisites builds if // the package is already configured but we still need to reconfigure @@ -8333,17 +12716,19 @@ namespace bpkg // the package manually). Maybe that a good reason not to allow // this? Or we could store this information in the database. // - if (p.skeleton) + if (p.dependencies) { - assert (p.dependencies); + assert (p.skeleton); pkg_configure (o, pdb, t, sp, *p.dependencies, + &*p.alternatives, move (*p.skeleton), nullptr /* previous_prerequisites */, + p.disfigure, simulate, fdb); } @@ -8351,85 +12736,77 @@ namespace bpkg { assert (sp != nullptr); // See above. - optional src_root (p.external_dir ()); - - optional out_root ( - src_root && !p.disfigure - ? dir_path (pdb.config) /= p.name ().string () - : optional ()); + // Note that the skeleton can be present if, for example, this is a + // dependency which configuration has been negotiated but it is not + // collected recursively since it has no buildfile clauses. + // + if (!p.skeleton) + p.init_skeleton (o); pkg_configure (o, pdb, t, sp, ap->dependencies, - package_skeleton (o, - pdb, - *ap, - move (p.config_vars), - move (src_root), - move (out_root)), + nullptr /* alternatives */, + move (*p.skeleton), prereqs (), + p.disfigure, simulate, fdb); } } else // Dependent. { + // This is an adjustment of a dependent which cannot be system + // (otherwise it wouldn't be a dependent) and cannot become system + // (otherwise it would be a build). + // + assert (*p.action == build_package::adjust && + !p.system && + !sp->system ()); + // Must be in the unpacked state since it was disfigured on the first // pass (see above). // assert (sp->state == package_state::unpacked); - // First try to avoid the package manifest parsing, searching for an - // existing available package for the selected package and, if not - // found, create a transient one. + // Initialize the skeleton if it is not initialized yet. // - // Note that we don't use find_available*() here since we don't care - // about the repository fragment the package comes from and only need - // its manifest information. + // Note that the skeleton can only be present here if it was + // initialized during the preparation of the plan and so this plan + // execution is not simulated (see above for details). // - shared_ptr dap; - - available_package_id pid (sp->name, sp->version); - for (database& db: dependent_repo_configs (pdb)) - { - shared_ptr ap (db.find (pid)); - - if (ap != nullptr && !ap->stub ()) - { - dap = move (ap); - break; - } - } + // Also note that there is no available package specified for the + // build package object here and so we need to find it (or create a + // transient one). + // + assert (p.available == nullptr && (!p.skeleton || !simulate)); - if (dap == nullptr) - dap = make_available (o, pdb, sp); + if (!p.skeleton) + p.init_skeleton (o, find_available (o, pdb, sp)); - optional src_root (p.external_dir ()); + assert (p.skeleton->available != nullptr); // Can't be system. - optional out_root ( - src_root && !p.disfigure - ? dir_path (pdb.config) /= p.name ().string () - : optional ()); + const dependencies& deps (p.skeleton->available->dependencies); // @@ Note that on reconfiguration the dependent looses the potential // configuration variables specified by the user on some previous // build, which can be quite surprising. Should we store this // information in the database? // + // I believe this now works for external packages via package + // skeleton (which extracts user configuration). + // pkg_configure (o, pdb, t, sp, - dap->dependencies, - package_skeleton (o, - pdb, - *dap, - move (p.config_vars), - move (src_root), - move (out_root)), + deps, + nullptr /* alternatives */, + move (*p.skeleton), prereqs (), + false /* disfigured */, simulate, fdb); } diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx index 1392545..a86752e 100644 --- a/bpkg/pkg-configure.cxx +++ b/bpkg/pkg-configure.cxx @@ -36,7 +36,7 @@ namespace bpkg // package skeleton, excluding those user-specified variables which are // not the project variables for the specified package (module // configuration variables, etc). Thus, it is not parallel to the - // variables member. + // config_variables member. // vector config_sources; // Note: name and source. }; @@ -48,14 +48,19 @@ namespace bpkg database& db, transaction&, const dependencies& deps, + const vector* alts, package_skeleton&& ps, const vector* prev_prereqs, bool simulate, const function& fdb) { - package_prerequisites prereqs; - strings vars; - vector srcs; + package_prerequisites prereqs; + strings vars; + + // Alternatives argument must be parallel to the dependencies argument if + // specified. + // + assert (alts == nullptr || alts->size () == deps.size ()); for (size_t di (0); di != deps.size (); ++di) { @@ -64,19 +69,55 @@ namespace bpkg // const dependency_alternatives_ex& das (deps[di]); - if (das.empty () || toolchain_buildtime_dependency (o, das, ps.name ())) + if (das.empty ()) continue; - small_vector, 2> edas; + small_vector, + size_t>, + 2> edas; - for (const dependency_alternative& da: das) + // If the dependency alternatives are not pre-selected, then evaluate + // the enable clauses. + // + // Note that evaluating the require and prefer clauses in this case is + // meaningless since we don't reconfigure the dependencies nor negotiate + // configurations with other dependents. What we should probably do is + // load configurations of the dependencies and use them while evaluating + // the dependent's enable and reflect clauses as we go along. Probably + // we should still evaluate the accept clauses to make sure that the + // dependency is configured acceptably for the dependent. For now we + // fail and will support this maybe later. + // + if (alts == nullptr) { - if (!da.enable || ps.evaluate_enable (*da.enable, di)) - edas.push_back (da); + if (toolchain_buildtime_dependency (o, das, &ps.package.name)) + continue; + + for (size_t i (0); i != das.size (); ++i) + { + const dependency_alternative& da (das[i]); + + if (!da.enable || ps.evaluate_enable (*da.enable, make_pair (di, i))) + { + if (da.prefer || da.require) + fail << "manual configuration of dependents with prefer or " + << "require clauses is not yet supported"; + + edas.push_back (make_pair (ref (da), i)); + } + } + + if (edas.empty ()) + continue; } + else + { + // Must only contain the selected alternative. + // + assert (das.size () == 1); - if (edas.empty ()) - continue; + edas.push_back (make_pair (ref (das.front ()), (*alts)[di])); + } // Pick the first alternative with dependencies that can all be resolved // to the configured packages, satisfying the respective constraints. @@ -91,16 +132,19 @@ namespace bpkg for (const vector* pps (prev_prereqs);;) { bool satisfied (false); - for (const dependency_alternative& da: edas) + for (const auto& eda: edas) { + const dependency_alternative& da (eda.first); + size_t dai (eda.second); + // Cache the selected packages which correspond to the alternative // dependencies, pairing them with the respective constraints. If // the alternative turns out to be fully resolvable, we will add the // cached packages into the dependent's prerequisites map. // small_vector< - pair, - const optional&>, 1> prerequisites; + pair, prerequisite_info>, + 1> prerequisites; dependency_alternative::const_iterator b (da.begin ()); dependency_alternative::const_iterator i (b); @@ -132,9 +176,13 @@ namespace bpkg // See the package_prerequisites definition for details on // creating the map keys with the database passed. // + bool conf (da.prefer || da.require); + prerequisites.emplace_back ( lazy_shared_ptr (*spd.second, dp), - d.constraint); + prerequisite_info {d.constraint, + make_pair (conf ? di + 1 : 0, + conf ? dai + 1 : 0)}); } // Try the next alternative if there are unresolved dependencies for @@ -150,9 +198,9 @@ namespace bpkg for (auto& pr: prerequisites) { const package_name& pn (pr.first.object_id ()); - const optional& pc (pr.second); + const prerequisite_info& pi (pr.second); - auto p (prereqs.emplace (pr.first, pc)); + auto p (prereqs.emplace (pr.first, pi)); // Currently we can only capture a single constraint, so if we // already have a dependency on this package and one constraint is @@ -160,18 +208,28 @@ namespace bpkg // if (!p.second) { - auto& c (p.first->second); + auto& c1 (p.first->second.constraint); + auto& c2 (pi.constraint); - bool s1 (satisfies (c, pc)); - bool s2 (satisfies (pc, c)); + bool s1 (satisfies (c1, c2)); + bool s2 (satisfies (c2, c1)); if (!s1 && !s2) fail << "multiple dependencies on package " << pn << - info << pn << " " << *c << - info << pn << " " << *pc; + info << pn << " " << *c1 << + info << pn << " " << *c2; if (s2 && !s1) - c = pc; + c1 = c2; + + // Keep position of the first dependency alternative with a + // configuration clause. + // + pair& p1 (p.first->second.config_position); + pair p2 (pi.config_position); + + if (p1.first == 0 && p2.first != 0) + p1 = p2; } // If the prerequisite is configured in the linked configuration, @@ -215,7 +273,7 @@ namespace bpkg // Evaluate the dependency alternative reflect clause, if present. // if (da.reflect) - ps.evaluate_reflect (*da.reflect, di); + ps.evaluate_reflect (*da.reflect, make_pair (di, dai)); satisfied = true; break; @@ -244,34 +302,28 @@ namespace bpkg } } - // Add the configuration variables collected from the reflect clauses, if - // any. + // Add the rest of the configuration variables (user overrides, reflects, + // etc) as well as their sources. // + vector srcs; + if (!simulate) { - auto rvs (move (ps).collect_config ()); + pair> rvs (move (ps).collect_config ()); - strings& vs (rvs.first); - vector>& ss (rvs.second); + strings& vs (rvs.first); + srcs = move (rvs.second); if (!vs.empty ()) { - vars.reserve (vars.size () + vs.size ()); - - for (size_t i (0); i != vs.size (); ++i) + if (vars.empty ()) + vars = move (vs); + else { - string& v (vs[i]); - const optional& s (ss[i]); - - if (s) - { - size_t p (v.find_first_of ("=+ \t")); - assert (p != string::npos); - - srcs.push_back (config_variable {string (v, 0, p), *s}); - } + vars.reserve (vars.size () + vs.size ()); - vars.push_back (move (v)); + for (string& v: vs) + vars.push_back (move (v)); } } } @@ -287,8 +339,10 @@ namespace bpkg transaction& t, const shared_ptr& p, const dependencies& deps, + const vector* alts, package_skeleton&& ps, const vector* pps, + bool disfigured, bool simulate, const function& fdb) { @@ -322,6 +376,7 @@ namespace bpkg db, t, deps, + alts, move (ps), pps, simulate, @@ -346,31 +401,26 @@ namespace bpkg l4 ([&]{trace << "buildspec: " << bspec;}); - // Deduce the configuration variables which are not reflected anymore - // and disfigure them. + // Unless this package has been completely disfigured, disfigure all the + // package configuration variables to reset all the old values to + // defaults (all the new user/dependent/reflec values, including old + // user, are returned by collect_config() and specified as overrides). + // Note that this semantics must be consistent with how we load things + // in the package skeleton during configuration negotiation. + // + // Note also that this means we don't really use the dependent and + // reflect sources that we save in the database. But let's keep them + // for the completeness of information (maybe could be useful during + // configuration reset or some such). // string dvar; - for (const config_variable& cv: p->config_variables) + if (!disfigured) { - if (cv.source == config_source::reflect) - { - const vector& ss (cpr.config_sources); - auto i (find_if (ss.begin (), ss.end (), - [&cv] (const config_variable& v) - { - return v.name == cv.name; - })); - - if (i == ss.end ()) - { - if (dvar.empty ()) - dvar = "config.config.disfigure="; - else - dvar += ' '; - - dvar += cv.name; - } - } + // Note: must be quoted to preserve the pattern. + // + dvar = "config.config.disfigure='config."; + dvar += p->name.variable (); + dvar += "**'"; } // Configure. @@ -554,18 +604,30 @@ namespace bpkg ? dir_path (db.config) /= p->name.string () : optional ()); + // Note on the disfigure logic: while we don't know whether the package + // has been disfigured with --keep-config or not, it has already been + // done physically and if without --keep-config, then config.build has + // been removed and config_variables cleaned. As a result, we can just + // proceed as disfigure=false and disfigure=true will be taken care + // automatically (because then things have been removed/cleaned). + // pkg_configure (o, db, t, p, ap->dependencies, + nullptr /* alternatives */, package_skeleton (o, - db, - *ap, + package_key (db, ap->id.name), + false /* system */, + ap, move (vars), + false /* disfigure */, + &p->config_variables, move (src_root), move (out_root)), nullptr /* prerequisites */, + false /* disfigured */, false /* simulate */); } diff --git a/bpkg/pkg-configure.hxx b/bpkg/pkg-configure.hxx index 23dab83..16ed96f 100644 --- a/bpkg/pkg-configure.hxx +++ b/bpkg/pkg-configure.hxx @@ -33,9 +33,16 @@ namespace bpkg // Configure the package, update its state, and commit the transaction. // - // The package dependency constraints are expected to be complete. Empty - // dependency alternatives lists are allowed and are ignored (see pkg-build - // for the use-case). + // The package dependency constraints are expected to be complete. + // + // The dependencies argument may contain pre-selected dependency + // alternatives (with the potential empty entries for the toolchain + // build-time dependencies or for dependencies with all the alternatives + // disabled; see pkg-build for the use-case). In this case the number of + // dependency alternatives for each dependency must be 1 (or 0) and the + // alternatives argument must be specified. The alternatives argument must + // be parallel to the dependencies argument and specify indexes of the + // selected alternatives. // // If prerequisites corresponding to the previous configured state of the // package are specified, then for each depends value try to select an @@ -49,8 +56,10 @@ namespace bpkg transaction&, const shared_ptr&, const dependencies&, + const vector* alternatives, package_skeleton&&, const vector* prerequisites, + bool disfigured, bool simulate, const function& = {}); diff --git a/bpkg/pkg-drop.cxx b/bpkg/pkg-drop.cxx index 08168d4..0aadaa7 100644 --- a/bpkg/pkg-drop.cxx +++ b/bpkg/pkg-drop.cxx @@ -77,7 +77,7 @@ namespace bpkg drop_reason r = drop_reason::user) { package_name n (p->name); // Because of move(p) below. - return map_.emplace (config_package {db, move (n)}, + return map_.emplace (package_key {db, move (n)}, data_type {end (), {db, move (p), r}}).second; } @@ -278,7 +278,7 @@ namespace bpkg if (!keep) { i = erase (i); - map_.erase (config_package {db, p->name}); + map_.erase (package_key {db, p->name}); continue; } @@ -298,18 +298,18 @@ namespace bpkg drop_package package; }; - class config_package_map: public map + class package_map: public map { public: - using base_type = map; + using base_type = map; iterator find (database& db, const package_name& pn) { - return base_type::find (config_package {db, pn}); + return base_type::find (package_key {db, pn}); } }; - config_package_map map_; + package_map map_; }; // Drop ordered list of packages. diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx index 531bf84..d065ff1 100644 --- a/bpkg/pkg-fetch.cxx +++ b/bpkg/pkg-fetch.cxx @@ -256,12 +256,12 @@ namespace bpkg // assert (ap->sha256sum); - const string& sha256sum (sha256 (co, a)); - if (sha256sum != *ap->sha256sum) + const string& cs (sha256sum (co, a)); + if (cs != *ap->sha256sum) { fail << "checksum mismatch for " << n << " " << v << info << pl->repository_fragment->name << " has " << *ap->sha256sum << - info << "fetched archive has " << sha256sum << + info << "fetched archive has " << cs << info << "consider re-fetching package list and trying again" << info << "if problem persists, consider reporting this to " << "the repository maintainer"; diff --git a/bpkg/pkg-status.cxx b/bpkg/pkg-status.cxx index 45ea519..09a9424 100644 --- a/bpkg/pkg-status.cxx +++ b/bpkg/pkg-status.cxx @@ -144,7 +144,7 @@ namespace bpkg { shared_ptr d (pair.first.load ()); database& db (pair.first.database ()); - const optional& c (pair.second); + const optional& c (pair.second.constraint); r.push_back (package {db, rdb, d->name, version (), move (d), c}); } return r; diff --git a/bpkg/rep-create.cxx b/bpkg/rep-create.cxx index b4944b7..c9ee924 100644 --- a/bpkg/rep-create.cxx +++ b/bpkg/rep-create.cxx @@ -24,7 +24,7 @@ using namespace butl; namespace bpkg { - struct package_key + struct package_version_key { package_name name; bpkg::version version; @@ -34,20 +34,20 @@ namespace bpkg // revision. // bool - operator< (const package_key& y) const + operator< (const package_version_key& y) const { int r (name.compare (y.name)); return r < 0 || (r == 0 && version.compare (y.version, true) < 0); } }; - struct package_data + struct package_version_data { path archive; package_manifest manifest; }; - using package_map = map; + using package_map = map; static void collect (const rep_create_options& o, @@ -106,7 +106,7 @@ namespace bpkg // Calculate its checksum. // - m.sha256sum = sha256 (o, a); + m.sha256sum = sha256sum (o, a); l4 ([&]{trace << m.name << " " << m.version << " in " << a << " sha256sum " << *m.sha256sum;}); @@ -115,8 +115,8 @@ namespace bpkg // m.location = a.leaf (root); - package_key k {m.name, m.version}; // Argument evaluation order. - auto r (map.emplace (move (k), package_data {a, move (m)})); + package_version_key k {m.name, m.version}; // Argument evaluation order. + auto r (map.emplace (move (k), package_version_data {a, move (m)})); // Diagnose duplicates. // @@ -183,7 +183,7 @@ namespace bpkg collect (o, pm, d, d); pkg_package_manifests manifests; - manifests.sha256sum = sha256 (o, path (d / repositories_file)); + manifests.sha256sum = sha256sum (o, path (d / repositories_file)); for (auto& p: pm) { @@ -235,7 +235,7 @@ namespace bpkg info << "run 'bpkg help rep-create' for more information"; signature_manifest m; - m.sha256sum = sha256 (o, p); + m.sha256sum = sha256sum (o, p); m.signature = sign_repository (o, m.sha256sum, key, *cert, d); p = path (d / signature_file); diff --git a/bpkg/rep-info.cxx b/bpkg/rep-info.cxx index 5184048..7d78173 100644 --- a/bpkg/rep-info.cxx +++ b/bpkg/rep-info.cxx @@ -5,7 +5,6 @@ #include // cout -#include // sha256_to_fingerprint() #include #include diff --git a/bpkg/types.hxx b/bpkg/types.hxx index a8cd0f4..2b6a1f8 100644 --- a/bpkg/types.hxx +++ b/bpkg/types.hxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include // icase_compare_string, // compare_reference_target @@ -103,6 +104,12 @@ namespace bpkg // using butl::url; + // + // + using butl::sha256; + using butl::sha256_to_fingerprint; + using butl::fingerprint_to_sha256; + // // using butl::process; diff --git a/bpkg/utility.hxx b/bpkg/utility.hxx index 2dcf46d..597800c 100644 --- a/bpkg/utility.hxx +++ b/bpkg/utility.hxx @@ -48,6 +48,10 @@ namespace bpkg using butl::digit; using butl::xdigit; + using butl::trim; + using butl::trim_left; + using butl::trim_right; + using butl::make_guard; using butl::make_exception_guard; diff --git a/tests/common/dependency-alternatives/t11a/bac-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bac-1.0.0.tar.gz new file mode 100644 index 0000000..7a7670d Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bac-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bar-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/bar-0.1.0.tar.gz new file mode 100644 index 0000000..34774a8 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bar-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bar-1.0.0.tar.gz new file mode 100644 index 0000000..15d819e Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bas-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bas-1.0.0.tar.gz new file mode 100644 index 0000000..c6365e3 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bas-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bat-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bat-1.0.0.tar.gz new file mode 100644 index 0000000..bb92104 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bat-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bax-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bax-1.0.0.tar.gz new file mode 100644 index 0000000..d488f1a Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bax-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/baz-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/baz-0.1.0.tar.gz new file mode 100644 index 0000000..a1f37b0 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/baz-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/baz-1.0.0.tar.gz new file mode 100644 index 0000000..1fd57fe Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bex-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bex-1.0.0.tar.gz new file mode 100644 index 0000000..4afa0f8 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bex-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bix-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bix-1.0.0.tar.gz new file mode 100644 index 0000000..2109914 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bix-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/biz-1.0.0.tar.gz new file mode 100644 index 0000000..e81a027 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/boo-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/boo-1.0.0.tar.gz new file mode 100644 index 0000000..778b253 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/boo-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/box-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/box-0.1.0.tar.gz new file mode 100644 index 0000000..8e91f91 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/box-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/box-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/box-1.0.0.tar.gz new file mode 100644 index 0000000..d205dc1 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/box-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/buc-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/buc-1.0.0.tar.gz new file mode 100644 index 0000000..88183f7 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/buc-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bus-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bus-1.0.0.tar.gz new file mode 100644 index 0000000..1530f14 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bus-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/bux-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/bux-1.0.0.tar.gz new file mode 100644 index 0000000..210941a Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/bux-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/buz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/buz-1.0.0.tar.gz new file mode 100644 index 0000000..7419d8a Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/buz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/dex-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/dex-1.0.0.tar.gz new file mode 100644 index 0000000..58bb16d Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/dex-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/dix-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/dix-1.0.0.tar.gz new file mode 100644 index 0000000..2236190 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/dix-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/diz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/diz-1.0.0.tar.gz new file mode 100644 index 0000000..a7fc8fa Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/diz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/dox-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/dox-1.0.0.tar.gz new file mode 100644 index 0000000..00c730a Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/dox-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/fex-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/fex-1.0.0.tar.gz new file mode 100644 index 0000000..00e925a Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/fex-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/fix-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/fix-0.1.0.tar.gz new file mode 100644 index 0000000..1d4ab42 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/fix-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/fix-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/fix-1.0.0.tar.gz new file mode 100644 index 0000000..3613136 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/fix-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/foo-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/foo-0.1.0.tar.gz new file mode 100644 index 0000000..ebf3f01 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/foo-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/foo-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/foo-1.0.0.tar.gz new file mode 100644 index 0000000..c9a4d6d Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/foo-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/fox-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/fox-1.0.0.tar.gz new file mode 100644 index 0000000..17b7278 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/fox-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/fux-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/fux-1.0.0.tar.gz new file mode 100644 index 0000000..b045fcd Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/fux-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libbar-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/libbar-0.1.0.tar.gz new file mode 100644 index 0000000..57b9ccc Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libbar-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libbar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/libbar-1.0.0.tar.gz new file mode 100644 index 0000000..7c5a840 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libbar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libbaz-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/libbaz-0.1.0.tar.gz new file mode 100644 index 0000000..b8bfaec Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libbaz-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libbaz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/libbaz-1.0.0.tar.gz new file mode 100644 index 0000000..9fde627 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libbaz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libbox-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/libbox-0.1.0.tar.gz new file mode 100644 index 0000000..3388a94 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libbox-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libbox-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/libbox-1.0.0.tar.gz new file mode 100644 index 0000000..8a417e4 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libbox-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libfoo-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/libfoo-0.1.0.tar.gz new file mode 100644 index 0000000..a94b8fe Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libfoo-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/libfoo-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/libfoo-1.0.0.tar.gz new file mode 100644 index 0000000..52ba91b Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/libfoo-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/repositories.manifest b/tests/common/dependency-alternatives/t11a/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t11a/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t11a/tax-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/tax-1.0.0.tar.gz new file mode 100644 index 0000000..616cb05 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tax-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tex-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/tex-0.1.0.tar.gz new file mode 100644 index 0000000..eb7d09f Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tex-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tex-0.2.0.tar.gz b/tests/common/dependency-alternatives/t11a/tex-0.2.0.tar.gz new file mode 100644 index 0000000..0b21183 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tex-0.2.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tex-0.3.0.tar.gz b/tests/common/dependency-alternatives/t11a/tex-0.3.0.tar.gz new file mode 100644 index 0000000..836a032 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tex-0.3.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tex-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/tex-1.0.0.tar.gz new file mode 100644 index 0000000..3c9093d Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tex-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tez-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/tez-1.0.0.tar.gz new file mode 100644 index 0000000..edf378b Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tez-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tix-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/tix-0.1.0.tar.gz new file mode 100644 index 0000000..2badf78 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tix-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tix-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/tix-1.0.0.tar.gz new file mode 100644 index 0000000..a1f2930 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tix-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tiz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/tiz-1.0.0.tar.gz new file mode 100644 index 0000000..63c5876 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tiz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/toz-0.1.0.tar.gz b/tests/common/dependency-alternatives/t11a/toz-0.1.0.tar.gz new file mode 100644 index 0000000..b99803c Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/toz-0.1.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/toz-0.2.0.tar.gz b/tests/common/dependency-alternatives/t11a/toz-0.2.0.tar.gz new file mode 100644 index 0000000..b2bd931 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/toz-0.2.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/toz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/toz-1.0.0.tar.gz new file mode 100644 index 0000000..ccfc094 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/toz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t11a/tux-1.0.0.tar.gz b/tests/common/dependency-alternatives/t11a/tux-1.0.0.tar.gz new file mode 100644 index 0000000..62c4e73 Binary files /dev/null and b/tests/common/dependency-alternatives/t11a/tux-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13a/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13a/bar-1.0.0.tar.gz new file mode 100644 index 0000000..db3c22d Binary files /dev/null and b/tests/common/dependency-alternatives/t13a/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13a/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13a/baz-1.0.0.tar.gz new file mode 100644 index 0000000..f86b0aa Binary files /dev/null and b/tests/common/dependency-alternatives/t13a/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13a/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13a/biz-1.0.0.tar.gz new file mode 100644 index 0000000..cfa8fa2 Binary files /dev/null and b/tests/common/dependency-alternatives/t13a/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13a/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13a/liba-1.0.0.tar.gz new file mode 100644 index 0000000..c177a1b Binary files /dev/null and b/tests/common/dependency-alternatives/t13a/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13a/repositories.manifest b/tests/common/dependency-alternatives/t13a/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13a/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13b/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13b/bar-1.0.0.tar.gz new file mode 100644 index 0000000..5b49774 Binary files /dev/null and b/tests/common/dependency-alternatives/t13b/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13b/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13b/baz-1.0.0.tar.gz new file mode 100644 index 0000000..cb136b4 Binary files /dev/null and b/tests/common/dependency-alternatives/t13b/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13b/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13b/biz-1.0.0.tar.gz new file mode 100644 index 0000000..27fe077 Binary files /dev/null and b/tests/common/dependency-alternatives/t13b/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13b/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13b/liba-1.0.0.tar.gz new file mode 100644 index 0000000..ee1dd3a Binary files /dev/null and b/tests/common/dependency-alternatives/t13b/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13b/repositories.manifest b/tests/common/dependency-alternatives/t13b/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13b/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13c/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13c/bar-1.0.0.tar.gz new file mode 100644 index 0000000..83ad484 Binary files /dev/null and b/tests/common/dependency-alternatives/t13c/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13c/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13c/baz-1.0.0.tar.gz new file mode 100644 index 0000000..b64552e Binary files /dev/null and b/tests/common/dependency-alternatives/t13c/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13c/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13c/liba-1.0.0.tar.gz new file mode 100644 index 0000000..5b79dc9 Binary files /dev/null and b/tests/common/dependency-alternatives/t13c/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13c/repositories.manifest b/tests/common/dependency-alternatives/t13c/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13c/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13d/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13d/bar-1.0.0.tar.gz new file mode 100644 index 0000000..5683176 Binary files /dev/null and b/tests/common/dependency-alternatives/t13d/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13d/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13d/baz-1.0.0.tar.gz new file mode 100644 index 0000000..8101c5d Binary files /dev/null and b/tests/common/dependency-alternatives/t13d/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13d/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13d/liba-1.0.0.tar.gz new file mode 100644 index 0000000..f2965f5 Binary files /dev/null and b/tests/common/dependency-alternatives/t13d/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13d/libb-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13d/libb-1.0.0.tar.gz new file mode 100644 index 0000000..0f13429 Binary files /dev/null and b/tests/common/dependency-alternatives/t13d/libb-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13d/repositories.manifest b/tests/common/dependency-alternatives/t13d/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13d/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13e/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13e/bar-1.0.0.tar.gz new file mode 100644 index 0000000..7211afb Binary files /dev/null and b/tests/common/dependency-alternatives/t13e/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13e/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13e/baz-1.0.0.tar.gz new file mode 100644 index 0000000..e5f2880 Binary files /dev/null and b/tests/common/dependency-alternatives/t13e/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13e/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13e/biz-1.0.0.tar.gz new file mode 100644 index 0000000..afd7db3 Binary files /dev/null and b/tests/common/dependency-alternatives/t13e/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13e/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13e/liba-1.0.0.tar.gz new file mode 100644 index 0000000..f3cb165 Binary files /dev/null and b/tests/common/dependency-alternatives/t13e/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13e/repositories.manifest b/tests/common/dependency-alternatives/t13e/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13e/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13f/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13f/bar-1.0.0.tar.gz new file mode 100644 index 0000000..7a255a1 Binary files /dev/null and b/tests/common/dependency-alternatives/t13f/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13f/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13f/baz-1.0.0.tar.gz new file mode 100644 index 0000000..f697c81 Binary files /dev/null and b/tests/common/dependency-alternatives/t13f/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13f/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13f/liba-1.0.0.tar.gz new file mode 100644 index 0000000..877305f Binary files /dev/null and b/tests/common/dependency-alternatives/t13f/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13f/repositories.manifest b/tests/common/dependency-alternatives/t13f/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13f/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13g/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13g/bar-1.0.0.tar.gz new file mode 100644 index 0000000..5c8a596 Binary files /dev/null and b/tests/common/dependency-alternatives/t13g/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13g/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13g/baz-1.0.0.tar.gz new file mode 100644 index 0000000..011ffea Binary files /dev/null and b/tests/common/dependency-alternatives/t13g/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13g/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13g/biz-1.0.0.tar.gz new file mode 100644 index 0000000..77cb421 Binary files /dev/null and b/tests/common/dependency-alternatives/t13g/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13g/box-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13g/box-1.0.0.tar.gz new file mode 100644 index 0000000..5217e88 Binary files /dev/null and b/tests/common/dependency-alternatives/t13g/box-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13g/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13g/liba-1.0.0.tar.gz new file mode 100644 index 0000000..2ee35ae Binary files /dev/null and b/tests/common/dependency-alternatives/t13g/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13g/libb-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13g/libb-1.0.0.tar.gz new file mode 100644 index 0000000..0f13429 Binary files /dev/null and b/tests/common/dependency-alternatives/t13g/libb-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13g/repositories.manifest b/tests/common/dependency-alternatives/t13g/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13g/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13h/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13h/bar-1.0.0.tar.gz new file mode 100644 index 0000000..7e85fca Binary files /dev/null and b/tests/common/dependency-alternatives/t13h/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13h/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13h/baz-1.0.0.tar.gz new file mode 100644 index 0000000..6fbdf82 Binary files /dev/null and b/tests/common/dependency-alternatives/t13h/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13h/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13h/liba-1.0.0.tar.gz new file mode 100644 index 0000000..ffd380c Binary files /dev/null and b/tests/common/dependency-alternatives/t13h/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13h/repositories.manifest b/tests/common/dependency-alternatives/t13h/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13h/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13i/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13i/bar-1.0.0.tar.gz new file mode 100644 index 0000000..6c1076d Binary files /dev/null and b/tests/common/dependency-alternatives/t13i/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13i/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13i/baz-1.0.0.tar.gz new file mode 100644 index 0000000..b086ef4 Binary files /dev/null and b/tests/common/dependency-alternatives/t13i/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13i/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13i/liba-1.0.0.tar.gz new file mode 100644 index 0000000..4a7bd22 Binary files /dev/null and b/tests/common/dependency-alternatives/t13i/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13i/repositories.manifest b/tests/common/dependency-alternatives/t13i/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13i/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13j/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13j/bar-1.0.0.tar.gz new file mode 100644 index 0000000..8660e0f Binary files /dev/null and b/tests/common/dependency-alternatives/t13j/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13j/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13j/baz-1.0.0.tar.gz new file mode 100644 index 0000000..7483377 Binary files /dev/null and b/tests/common/dependency-alternatives/t13j/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13j/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13j/biz-1.0.0.tar.gz new file mode 100644 index 0000000..948c1c4 Binary files /dev/null and b/tests/common/dependency-alternatives/t13j/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13j/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13j/liba-1.0.0.tar.gz new file mode 100644 index 0000000..0c94586 Binary files /dev/null and b/tests/common/dependency-alternatives/t13j/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13j/repositories.manifest b/tests/common/dependency-alternatives/t13j/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13j/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13k/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13k/bar-1.0.0.tar.gz new file mode 100644 index 0000000..51de0c5 Binary files /dev/null and b/tests/common/dependency-alternatives/t13k/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13k/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13k/baz-1.0.0.tar.gz new file mode 100644 index 0000000..a8c62ba Binary files /dev/null and b/tests/common/dependency-alternatives/t13k/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13k/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13k/liba-1.0.0.tar.gz new file mode 100644 index 0000000..c4a4b32 Binary files /dev/null and b/tests/common/dependency-alternatives/t13k/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13k/repositories.manifest b/tests/common/dependency-alternatives/t13k/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13k/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13l/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13l/bar-1.0.0.tar.gz new file mode 100644 index 0000000..d114c5c Binary files /dev/null and b/tests/common/dependency-alternatives/t13l/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13l/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13l/baz-1.0.0.tar.gz new file mode 100644 index 0000000..e884965 Binary files /dev/null and b/tests/common/dependency-alternatives/t13l/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13l/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13l/liba-1.0.0.tar.gz new file mode 100644 index 0000000..3590340 Binary files /dev/null and b/tests/common/dependency-alternatives/t13l/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13l/libb-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13l/libb-1.0.0.tar.gz new file mode 100644 index 0000000..409f438 Binary files /dev/null and b/tests/common/dependency-alternatives/t13l/libb-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13l/repositories.manifest b/tests/common/dependency-alternatives/t13l/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13l/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13m/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13m/bar-1.0.0.tar.gz new file mode 100644 index 0000000..7a2bc53 Binary files /dev/null and b/tests/common/dependency-alternatives/t13m/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13m/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13m/baz-1.0.0.tar.gz new file mode 100644 index 0000000..c5028f7 Binary files /dev/null and b/tests/common/dependency-alternatives/t13m/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13m/bix-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13m/bix-1.0.0.tar.gz new file mode 100644 index 0000000..9d45acc Binary files /dev/null and b/tests/common/dependency-alternatives/t13m/bix-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13m/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13m/biz-1.0.0.tar.gz new file mode 100644 index 0000000..d12852b Binary files /dev/null and b/tests/common/dependency-alternatives/t13m/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13m/box-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13m/box-1.0.0.tar.gz new file mode 100644 index 0000000..316458d Binary files /dev/null and b/tests/common/dependency-alternatives/t13m/box-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13m/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13m/liba-1.0.0.tar.gz new file mode 100644 index 0000000..3857354 Binary files /dev/null and b/tests/common/dependency-alternatives/t13m/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13m/repositories.manifest b/tests/common/dependency-alternatives/t13m/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13m/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13n/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13n/bar-1.0.0.tar.gz new file mode 100644 index 0000000..d3d3a24 Binary files /dev/null and b/tests/common/dependency-alternatives/t13n/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13n/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13n/liba-1.0.0.tar.gz new file mode 100644 index 0000000..fd96b61 Binary files /dev/null and b/tests/common/dependency-alternatives/t13n/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13n/libb-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13n/libb-1.0.0.tar.gz new file mode 100644 index 0000000..409f438 Binary files /dev/null and b/tests/common/dependency-alternatives/t13n/libb-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13n/repositories.manifest b/tests/common/dependency-alternatives/t13n/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13n/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/dependency-alternatives/t13o/bar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13o/bar-1.0.0.tar.gz new file mode 100644 index 0000000..d6ed94c Binary files /dev/null and b/tests/common/dependency-alternatives/t13o/bar-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13o/baz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13o/baz-1.0.0.tar.gz new file mode 100644 index 0000000..7c1f3b7 Binary files /dev/null and b/tests/common/dependency-alternatives/t13o/baz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13o/bix-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13o/bix-1.0.0.tar.gz new file mode 100644 index 0000000..cdbba8d Binary files /dev/null and b/tests/common/dependency-alternatives/t13o/bix-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13o/biz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13o/biz-1.0.0.tar.gz new file mode 100644 index 0000000..a7908c1 Binary files /dev/null and b/tests/common/dependency-alternatives/t13o/biz-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13o/liba-1.0.0.tar.gz b/tests/common/dependency-alternatives/t13o/liba-1.0.0.tar.gz new file mode 100644 index 0000000..d9dcd50 Binary files /dev/null and b/tests/common/dependency-alternatives/t13o/liba-1.0.0.tar.gz differ diff --git a/tests/common/dependency-alternatives/t13o/repositories.manifest b/tests/common/dependency-alternatives/t13o/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/dependency-alternatives/t13o/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/satisfy/t12a/libbar-0.1.0.tar.gz b/tests/common/satisfy/t12a/libbar-0.1.0.tar.gz new file mode 100644 index 0000000..10c7f29 Binary files /dev/null and b/tests/common/satisfy/t12a/libbar-0.1.0.tar.gz differ diff --git a/tests/common/satisfy/t12a/libbaz-1.0.0.tar.gz b/tests/common/satisfy/t12a/libbaz-1.0.0.tar.gz new file mode 100644 index 0000000..1d498b8 Binary files /dev/null and b/tests/common/satisfy/t12a/libbaz-1.0.0.tar.gz differ diff --git a/tests/common/satisfy/t12a/repositories.manifest b/tests/common/satisfy/t12a/repositories.manifest new file mode 100644 index 0000000..5b70556 --- /dev/null +++ b/tests/common/satisfy/t12a/repositories.manifest @@ -0,0 +1 @@ +: 1 diff --git a/tests/common/satisfy/t12b/bar-1.0.0.tar.gz b/tests/common/satisfy/t12b/bar-1.0.0.tar.gz new file mode 100644 index 0000000..8999e1a Binary files /dev/null and b/tests/common/satisfy/t12b/bar-1.0.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/baz-0.1.0.tar.gz b/tests/common/satisfy/t12b/baz-0.1.0.tar.gz new file mode 100644 index 0000000..2676c52 Binary files /dev/null and b/tests/common/satisfy/t12b/baz-0.1.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/baz-1.0.0.tar.gz b/tests/common/satisfy/t12b/baz-1.0.0.tar.gz new file mode 100644 index 0000000..1aec461 Binary files /dev/null and b/tests/common/satisfy/t12b/baz-1.0.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/foo-0.1.0.tar.gz b/tests/common/satisfy/t12b/foo-0.1.0.tar.gz new file mode 100644 index 0000000..a282f20 Binary files /dev/null and b/tests/common/satisfy/t12b/foo-0.1.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/foo-1.0.0.tar.gz b/tests/common/satisfy/t12b/foo-1.0.0.tar.gz new file mode 100644 index 0000000..4c66d3d Binary files /dev/null and b/tests/common/satisfy/t12b/foo-1.0.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/libbar-1.0.0.tar.gz b/tests/common/satisfy/t12b/libbar-1.0.0.tar.gz new file mode 100644 index 0000000..c0fe278 Binary files /dev/null and b/tests/common/satisfy/t12b/libbar-1.0.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/libbaz-0.1.0.tar.gz b/tests/common/satisfy/t12b/libbaz-0.1.0.tar.gz new file mode 100644 index 0000000..73c0edb Binary files /dev/null and b/tests/common/satisfy/t12b/libbaz-0.1.0.tar.gz differ diff --git a/tests/common/satisfy/t12b/repositories.manifest b/tests/common/satisfy/t12b/repositories.manifest new file mode 100644 index 0000000..6387e01 --- /dev/null +++ b/tests/common/satisfy/t12b/repositories.manifest @@ -0,0 +1,4 @@ +: 1 +: +location: ../t12a +role: prerequisite diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index b5954f2..86a63f1 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -171,6 +171,211 @@ # | |-- libfoo-tests-1.0.0.tar.gz # | `-- repositories.manifest # | +# |-- t11a +# | |-- libfoo-0.1.0.tar.gz +# | |-- libfoo-1.0.0.tar.gz +# | |-- libbar-0.1.0.tar.gz +# | |-- libbar-1.0.0.tar.gz +# | |-- libbaz-0.1.0.tar.gz +# | |-- libbaz-1.0.0.tar.gz +# | |-- libbox-0.1.0.tar.gz +# | |-- libbox-1.0.0.tar.gz +# | |-- foo-1.0.0.tar.gz -> libfoo {require {config.libfoo.extras=true}} +# | |-- fox-1.0.0.tar.gz -> libfoo {require {config.libfoo.extras=true}} +# | |-- fux-1.0.0.tar.gz -> libfoo +# | |-- fix-1.0.0.tar.gz -> foo {require {config.foo.extras=true}} +# | |-- fex-1.0.0.tar.gz -> foo, libfoo {require {config.libfoo.extras=true}} +# | |-- bar-0.1.0.tar.gz -> libbar == 0.1.0 {require {config.libbar.extras=true}} +# | |-- bar-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}} +# | |-- baz-0.1.0.tar.gz -> {libbar libfoo} == 0.1.0 {require {config.libbar.extras=true config.libfoo.extras=true}} +# | |-- baz-1.0.0.tar.gz -> {libbar libfoo} {require {config.libbar.extras=true config.libfoo.extras=true}} +# | |-- bac-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | libbaz {require {config.libbaz.extras=true}}, +# | | libfoo {require {config.libfoo.extras=true}} +# | |-- bat-1.0.0.tar.gz -> libbaz {require {config.libbaz.extras=true}} +# | |-- bas-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | bus {require {config.bus.extras=true}} +# | |-- bus-1.0.0.tar.gz -> libaz {require {config.libbox.extras=true}}, +# | | foo {require {config.foo.extras=true}} +# | |-- box-0.1.0.tar.gz -> libbox {require {config.libbox.extras=true}} +# | |-- box-1.0.0.tar.gz -> {libbar libfoo} {require {config.libbar.extras=true config.libfoo.extras=true}} | +# | | libbox +# | |-- bax-1.0.0.tar.gz -> libfoo {require {config.libfoo.extras=true}}, +# | | {libbox libbar} {require {config.libbox.extras=true config.libbar.extras=true}} +# | |-- bux-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}} +# | |-- bix-1.0.0.tar.gz -> {libbar bar} {require {config.libbar.extras=true config.bar.extras=true}}, +# | | bux +# | |-- bex-1.0.0.tar.gz -> libbar +# | |-- boo-1.0.0.tar.gz -> libbar | libfoo {require {config.libfoo.extras=true}} | libbox +# | |-- biz-1.0.0.tar.gz -> boo {require {config.boo.extras=true}} +# | |-- buz-1.0.0.tar.gz -> bux {require {config.bux.extras=true}} +# | |-- buc-1.0.0.tar.gz -> libfoo {require {config.libfoo.extras=true}}, +# | | bux {require {config.bux.extras=true}} +# | |-- tax-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | libfoo +# | |-- tex-0.1.0.tar.gz -> libfoo {require {config.libfoo.extras=true}} +# | |-- tex-0.2.0.tar.gz -> libbar, +# | | libfoo {require {config.libfoo.extras=true}} +# | |-- tex-0.3.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | libfoo {require {config.libfoo.extras=true}} +# | |-- tex-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | libfoo {require {config.libfoo.extras=true}} +# | |-- tix-0.1.0.tar.gz +# | |-- tix-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | tex {require {config.tex.extras=true}} +# | |-- tiz-1.0.0.tar.gz -> tex {require {config.tex.extras=true}}, +# | | libbar {require {config.libbar.extras=true}} +# | |-- toz-0.1.0.tar.gz +# | |-- toz-0.2.0.tar.gz -> libfoo {require {config.libfoo.extras=true}}, +# | | libbar {require {config.libbar.extras=true}} +# | |-- toz-1.0.0.tar.gz -> libbaz {require {config.libbaz.extras=true}}, +# | | libfoo {require {config.libfoo.extras=true}}, +# | | libbar {require {config.libbar.extras=true}} +# | |-- tez-1.0.0.tar.gz -> libbox {require {config.libbox.extras=true}}, +# | | toz == 0.1.0 {require {config.toz.extras=true}}, +# | | libbar {require {config.libbar.extras=true}} +# | |-- tux-1.0.0.tar.gz -> libbox {require {config.libbox.extras=true}}, +# | | tix == 0.1.0 +# | |-- dex-1.0.0.tar.gz -> bar {require {config.bar.extras=true}}, +# | | libfoo {require {config.libfoo.extras=true}} +# | |-- dix-1.0.0.tar.gz -> libbar {require {config.libbar.extras=true}}, +# | | libbox {require {config.libbox.extras=true}}, +# | | dox {require {config.dox.extras=true}} +# | |-- diz-1.0.0.tar.gz -> dox {require {config.dox.extras=true}}, +# | | libbox {require {config.libbox.extras=true}}, +# | | libbar {require {config.libbar.extras=true}} +# | |-- dox-1.0.0.tar.gz -> dex {require {config.dex.extras=true}} +# | `-- repositories.manifest +# | +# |-- t12a +# | |-- libbaz-1.0.0.tar.gz +# | |-- libbar-0.1.0.tar.gz -> libbaz +# | `-- repositories.manifest +# | +# |-- t12b -> t12b (prerequisite repository) +# | |-- libbaz-0.1.0.tar.gz +# | |-- libbar-1.0.0.tar.gz -> libbaz == 0.1.0 +# | |-- foo-0.1.0.tar.gz +# | |-- foo-1.0.0.tar.gz -> libbar +# | |-- bar-1.0.0.tar.gz -> libbar == 0.1.0 +# | |-- baz-0.1.0.tar.gz -> libbaz +# | |-- baz-1.0.0.tar.gz -> libbaz == 1.0.0 +# | `-- repositories.manifest +# | +# |-- t13a +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- biz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | `-- repositories.manifest +# | +# |-- t13b +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- biz-1.0.0.tar.gz -> liba {require {...}} +# | `-- repositories.manifest +# | +# |-- t13c +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...) reflect (...)} +# | |-- baz-1.0.0.tar.gz -> liba {require {...}} +# | `-- repositories.manifest +# | +# |-- t13d +# | |-- liba-0.1.0.tar.gz +# | |-- libb-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...) reflect (...)}, +# | | libb ? (...) +# | |-- baz-1.0.0.tar.gz -> bar, liba {require {...}} +# | `-- repositories.manifest +# | +# |-- t13e +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)}, +# | | bar ? (...), +# | | biz +# | |-- biz-1.0.0.tar.gz -> liba {require {...}} +# | `-- repositories.manifest +# | +# |-- t13f +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | `-- repositories.manifest +# | +# |-- t13g +# | |-- liba-0.1.0.tar.gz +# | |-- libb-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...) reflect (...)}, +# | | libb {prefer {...} accept (...) reflect (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...) reflect (...)}, +# | | libb ? (...) +# | |-- biz-1.0.0.tar.gz -> liba {prefer {...} accept (...) reflect (...)}, +# | | libb {prefer {...} accept (...) reflect (...)} +# | |-- box-1.0.0.tar.gz -> liba {prefer {...} accept (...) reflect (...)}, +# | | libb {prefer {...} accept (...) reflect (...)} +# | `-- repositories.manifest +# | +# |-- t13h +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | `-- repositories.manifest +# | +# |-- t13i +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {require {...} reflect (...)} +# | `-- repositories.manifest +# | +# |-- t13j +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {require {...}} +# | |-- biz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | `-- repositories.manifest +# | +# |-- t13k +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)} +# | `-- repositories.manifest +# | +# |-- t13l +# | |-- liba-0.1.0.tar.gz +# | |-- libb-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {require {...}}, +# | | libb ? (...) +# | |-- baz-1.0.0.tar.gz -> liba {prefer {...} accept (...)}, +# | | libb ? (...) +# | `-- repositories.manifest +# | +# |-- t13m +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {require {...}} +# | |-- baz-1.0.0.tar.gz -> liba {require {...}}, +# | | bar { enable (...) reflect {...}} +# | |-- biz-1.0.0.tar.gz -> liba {require {...}} +# | |-- bix-1.0.0.tar.gz -> liba {require {...}} +# | |-- box-1.0.0.tar.gz -> liba {require {...}} +# | `-- repositories.manifest +# | +# |-- t13n +# | |-- liba-0.1.0.tar.gz +# | |-- libb-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {reflect {...}}, +# | | libb {reflect {...}} +# | `-- repositories.manifest +# | +# |-- t13o +# | |-- liba-0.1.0.tar.gz +# | |-- bar-1.0.0.tar.gz -> liba {reflect {...}} +# | |-- baz-1.0.0.tar.gz -> bar {require {...}} +# | |-- biz-1.0.0.tar.gz -> bar {prefer {...} accept (...)} +# | |-- bix-1.0.0.tar.gz -> bar {prefer {...} accept (...)} +# | `-- repositories.manifest +# | # `-- git # |-- libbar.git -> style-basic.git (prerequisite repository) # |-- libbaz.git @@ -183,25 +388,43 @@ posix = ($cxx.target.class != 'windows') +if! $remote rep_create += 2>! - cp -r $src/t0a $out/t0a && $rep_create $out/t0a &$out/t0a/packages.manifest - cp -r $src/t0b $out/t0b && $rep_create $out/t0b &$out/t0b/packages.manifest - cp -r $src/t0c $out/t0c && $rep_create $out/t0c &$out/t0c/packages.manifest - cp -r $src/t0d $out/t0d && $rep_create $out/t0d &$out/t0d/packages.manifest - cp -r $src/t1 $out/t1 && $rep_create $out/t1 &$out/t1/packages.manifest - cp -r $src/t2 $out/t2 && $rep_create $out/t2 &$out/t2/packages.manifest - cp -r $src/t3 $out/t3 && $rep_create $out/t3 &$out/t3/packages.manifest - cp -r $src/t4a $out/t4a && $rep_create $out/t4a &$out/t4a/packages.manifest - cp -r $src/t4b $out/t4b && $rep_create $out/t4b &$out/t4b/packages.manifest - cp -r $src/t4c $out/t4c && $rep_create $out/t4c &$out/t4c/packages.manifest - cp -r $src/t4d $out/t4d && $rep_create $out/t4d &$out/t4d/packages.manifest - cp -r $src/t4e $out/t4e && $rep_create $out/t4e &$out/t4e/packages.manifest - cp -r $src/t5 $out/t5 && $rep_create $out/t5 &$out/t5/packages.manifest - cp -r $src/t6 $out/t6 && $rep_create $out/t6 &$out/t6/packages.manifest - cp -r $src/t7a $out/t7a && $rep_create $out/t7a &$out/t7a/packages.manifest - cp -r $src/t7b $out/t7b && $rep_create $out/t7b &$out/t7b/packages.manifest - cp -r $src/t8a $out/t8a && $rep_create $out/t8a &$out/t8a/packages.manifest - cp -r $src/t9 $out/t9 && $rep_create $out/t9 &$out/t9/packages.manifest - cp -r $src/t10 $out/t10 && $rep_create $out/t10 &$out/t10/packages.manifest + cp -r $src/t0a $out/t0a && $rep_create $out/t0a &$out/t0a/packages.manifest + cp -r $src/t0b $out/t0b && $rep_create $out/t0b &$out/t0b/packages.manifest + cp -r $src/t0c $out/t0c && $rep_create $out/t0c &$out/t0c/packages.manifest + cp -r $src/t0d $out/t0d && $rep_create $out/t0d &$out/t0d/packages.manifest + cp -r $src/t1 $out/t1 && $rep_create $out/t1 &$out/t1/packages.manifest + cp -r $src/t2 $out/t2 && $rep_create $out/t2 &$out/t2/packages.manifest + cp -r $src/t3 $out/t3 && $rep_create $out/t3 &$out/t3/packages.manifest + cp -r $src/t4a $out/t4a && $rep_create $out/t4a &$out/t4a/packages.manifest + cp -r $src/t4b $out/t4b && $rep_create $out/t4b &$out/t4b/packages.manifest + cp -r $src/t4c $out/t4c && $rep_create $out/t4c &$out/t4c/packages.manifest + cp -r $src/t4d $out/t4d && $rep_create $out/t4d &$out/t4d/packages.manifest + cp -r $src/t4e $out/t4e && $rep_create $out/t4e &$out/t4e/packages.manifest + cp -r $src/t5 $out/t5 && $rep_create $out/t5 &$out/t5/packages.manifest + cp -r $src/t6 $out/t6 && $rep_create $out/t6 &$out/t6/packages.manifest + cp -r $src/t7a $out/t7a && $rep_create $out/t7a &$out/t7a/packages.manifest + cp -r $src/t7b $out/t7b && $rep_create $out/t7b &$out/t7b/packages.manifest + cp -r $src/t8a $out/t8a && $rep_create $out/t8a &$out/t8a/packages.manifest + cp -r $src/t9 $out/t9 && $rep_create $out/t9 &$out/t9/packages.manifest + cp -r $src/t10 $out/t10 && $rep_create $out/t10 &$out/t10/packages.manifest + cp -r $src/t11a $out/t11a && $rep_create $out/t11a &$out/t11a/packages.manifest + cp -r $src/t12a $out/t12a && $rep_create $out/t12a &$out/t12a/packages.manifest + cp -r $src/t12b $out/t12b && $rep_create $out/t12b &$out/t12b/packages.manifest + cp -r $src/t13a $out/t13a && $rep_create $out/t13a &$out/t13a/packages.manifest + cp -r $src/t13b $out/t13b && $rep_create $out/t13b &$out/t13b/packages.manifest + cp -r $src/t13c $out/t13c && $rep_create $out/t13c &$out/t13c/packages.manifest + cp -r $src/t13d $out/t13d && $rep_create $out/t13d &$out/t13d/packages.manifest + cp -r $src/t13e $out/t13e && $rep_create $out/t13e &$out/t13e/packages.manifest + cp -r $src/t13f $out/t13f && $rep_create $out/t13f &$out/t13f/packages.manifest + cp -r $src/t13g $out/t13g && $rep_create $out/t13g &$out/t13g/packages.manifest + cp -r $src/t13h $out/t13h && $rep_create $out/t13h &$out/t13h/packages.manifest + cp -r $src/t13i $out/t13i && $rep_create $out/t13i &$out/t13i/packages.manifest + cp -r $src/t13j $out/t13j && $rep_create $out/t13j &$out/t13j/packages.manifest + cp -r $src/t13k $out/t13k && $rep_create $out/t13k &$out/t13k/packages.manifest + cp -r $src/t13l $out/t13l && $rep_create $out/t13l &$out/t13l/packages.manifest + cp -r $src/t13m $out/t13m && $rep_create $out/t13m &$out/t13m/packages.manifest + cp -r $src/t13n $out/t13n && $rep_create $out/t13n &$out/t13n/packages.manifest + cp -r $src/t13o $out/t13o && $rep_create $out/t13o &$out/t13o/packages.manifest # Create git repositories. # @@ -2712,6 +2935,30 @@ test.options += --no-progress $pkg_drop libbar libbaz } + : sys-to-src-unhold-same-ver + : + : Same as above but the version does not change. + : + { + $clone_cfg; + + $* 'sys:libbaz/0.1.0' 2>>EOE; + configured sys:libbaz/0.1.0 + EOE + + $* ?libbaz libbar/0.0.3 2>>EOE; + purged libbaz/0.1.0 + fetched libbaz/0.1.0 + unpacked libbaz/0.1.0 + fetched libbar/0.0.3 + unpacked libbar/0.0.3 + configured libbaz/0.1.0 + configured libbar/0.0.3 + EOE + + $pkg_drop libbar libbaz + } + : sys-to-src : : As above but keep held. @@ -3382,7 +3629,7 @@ test.options += --no-progress { +$clone_cfg - test.arguments += --yes + test.arguments += --yes --plan 'build plan:' : ambiguity : @@ -3409,6 +3656,10 @@ test.options += --no-progress $clone_cfg; $* fox ?libbaz 2>>~%EOE%; + build plan: + new libbaz/1.1.0 + new fox/1.0.0 + config.fox.backend=libbaz (set by fox) fetched libbaz/1.1.0 unpacked libbaz/1.1.0 fetched fox/1.0.0 @@ -3430,6 +3681,10 @@ test.options += --no-progress $clone_cfg; $* fox libbaz 2>>~%EOE%; + build plan: + new libbaz/1.1.0 + new fox/1.0.0 + config.fox.backend=libbaz (set by fox) fetched libbaz/1.1.0 unpacked libbaz/1.1.0 fetched fox/1.0.0 @@ -3454,6 +3709,9 @@ test.options += --no-progress $* libbaz 2>!; $* fox 2>>~%EOE%; + build plan: + new fox/1.0.0 + config.fox.backend=libbaz (set by fox) fetched fox/1.0.0 unpacked fox/1.0.0 configured fox/1.0.0 @@ -3473,6 +3731,10 @@ test.options += --no-progress $pkg_fetch libbaz/1.0.0; $* fox 2>>~%EOE%; + build plan: + update libbaz/1.0.0 (required by fox) + new fox/1.0.0 + config.fox.backend=libbaz (set by fox) unpacked libbaz/1.0.0 fetched fox/1.0.0 unpacked fox/1.0.0 @@ -3497,6 +3759,12 @@ test.options += --no-progress $clone_cfg; $* fox foo 2>>~%EOE%; + build plan: + new libbaz/1.1.0 (required by foo) + new libbar/1.0.0 (required by foo, fox) + new fox/1.0.0 + config.fox.backend=libbar (set by fox) + new foo/1.0.0 fetched libbaz/1.1.0 unpacked libbaz/1.1.0 fetched libbar/1.0.0 @@ -3525,6 +3793,13 @@ test.options += --no-progress $clone_cfg; $* baz fox bar 2>>~%EOE%; + build plan: + new libbaz/1.1.0 (required by baz) + new baz/1.0.0 + new libbar/1.0.0 (required by bar, fox) + new fox/1.0.0 + config.fox.backend=libbar (set by fox) + new bar/1.0.0 fetched libbaz/1.1.0 unpacked libbaz/1.1.0 fetched baz/1.0.0 @@ -3566,6 +3841,10 @@ test.options += --no-progress $clone_cfg; $* fox ?libbaz/1.0.0 2>>~%EOE%; + build plan: + new libbaz/1.0.0 + new fox/1.0.0 + config.fox.backend=libbaz (set by fox) fetched libbaz/1.0.0 unpacked libbaz/1.0.0 fetched fox/1.0.0 @@ -3579,6 +3858,9 @@ test.options += --no-progress EOE $* ?libbaz 2>>~%EOE%; + build plan: + upgrade libbaz/1.1.0 + reconfigure fox (dependent of libbaz) disfigured fox/1.0.0 disfigured libbaz/1.0.0 fetched libbaz/1.1.0 @@ -3621,6 +3903,10 @@ test.options += --no-progress $* box libbox 2>!; $* box +{ config.box.extras=true } ?libbiz 2>>~%EOE%; + build plan: + reconfigure/update box/1.0.0 + config.box.extras=true (user configuration) + config.box.backend=libbox (set by box) disfigured box/1.0.0 configured box/1.0.0 %info: .+box-1.0.0.+ is up to date% @@ -3628,6 +3914,11 @@ test.options += --no-progress EOE $* box +{ config.box.extras=false } libbiz 2>>~%EOE%; + build plan: + reconfigure/update box/1.0.0 + config.box.extras=false (user configuration) + config.box.backend=libbox (set by box) + new libbiz/1.0.0 disfigured box/1.0.0 fetched libbiz/1.0.0 unpacked libbiz/1.0.0 @@ -3667,6 +3958,12 @@ test.options += --no-progress $* box libbox 2>!; $* box +{ config.box.extras=true } ?libbox/0.1.0 2>>~%EOE%; + build plan: + new libbiz/1.0.0 (required by box) + drop libbox/1.0.0 (unused) + reconfigure/update box/1.0.0 + config.box.extras=true (user configuration) + config.box.backend=libbiz (set by box) disfigured box/1.0.0 disfigured libbox/1.0.0 fetched libbiz/1.0.0 @@ -3702,6 +3999,12 @@ test.options += --no-progress $* box libbox 2>!; $* box +{ config.box.extras=true } libbox/0.1.0 2>>~%EOE%; + build plan: + new libbiz/1.0.0 (required by box) + downgrade libbox/0.1.0 + reconfigure/update box/1.0.0 + config.box.extras=true (user configuration) + config.box.backend=libbiz (set by box) disfigured box/1.0.0 disfigured libbox/1.0.0 fetched libbiz/1.0.0 @@ -3739,6 +4042,11 @@ test.options += --no-progress $clone_cfg; $* box ?libbiz/0.1.0 2>>~%EOE%; + build plan: + new libbox/1.0.0 (required by box) + new libbaz/1.1.0 (required by box) + new box/1.0.0 + config.box.backend=libbox (set by box) fetched libbox/1.0.0 unpacked libbox/1.0.0 fetched libbaz/1.1.0 @@ -3767,6 +4075,10 @@ test.options += --no-progress # libbox as a prerequisite of box. # $* libbiz ?libbaz/1.0.0 2>>~%EOE%; + build plan: + new libbiz/1.0.0 + downgrade libbaz/1.0.0 + reconfigure box (dependent of libbaz) disfigured box/1.0.0 disfigured libbaz/1.1.0 fetched libbiz/1.0.0 @@ -3799,6 +4111,9 @@ test.options += --no-progress # Make sure the decision is hold for downgraded dependency either. # $* ?libbox/0.1.1 2>>~%EOE%; + build plan: + downgrade libbox/0.1.1 + reconfigure box (dependent of libbox) disfigured box/1.0.0 disfigured libbox/1.0.0 fetched libbox/0.1.1 @@ -4134,7 +4449,7 @@ test.options += --no-progress $* fax/ 2>>~%EOE% != 0; :1: error: invalid bool value: multiple names info: enable condition: (config.fax.libbiz = true) - % .+fax.manifest:10:10: info: depends value defined here% + % fax.manifest:10:10: info: in depends manifest value defined here% info: while satisfying fax/1.0.0#3 EOE @@ -4403,152 +4718,8497 @@ test.options += --no-progress } } } -} - -: test-dependency -: -{ - +$clone_cfg - +$rep_add $rep/t10 && $rep_fetch - : simultaneously + : version-replacement : { - $clone_cfg; + +$clone_root_cfg && $rep_add $rep/t12b && $rep_fetch - $* libfoo-bar libfoo-tests 2>>~%EOE%; - fetched libfoo-bar/1.0.0 - unpacked libfoo-bar/1.0.0 - fetched libfoo-tests/1.0.0 - unpacked libfoo-tests/1.0.0 - configured libfoo-bar/1.0.0 - configured libfoo-tests/1.0.0 - %info: .+libfoo-bar-1.0.0.+ is up to date% - %info: .+libfoo-tests-1.0.0.+ is up to date% - updated libfoo-bar/1.0.0 - updated libfoo-tests/1.0.0 - EOE + test.arguments += --yes - cat cfg/libfoo-tests-1.0.0/build/config.build >>~%EOO%; - %.* - config.libfoo_tests.test = libfoo-bar - %.* - EOO + : not-replaced + : + { + $clone_cfg; - $pkg_drop libfoo-bar libfoo-tests - } + $* bar foo 2>!; - : sequentially - : - { - $clone_cfg; + $pkg_status -r >>EOO; + !bar configured 1.0.0 + libbar configured 0.1.0 available 1.0.0 + libbaz configured 1.0.0 + !foo configured 1.0.0 + libbar configured 0.1.0 available 1.0.0 + libbaz configured 1.0.0 + EOO - $* libfoo-baz 2>>~%EOE%; - fetched libfoo-baz/1.0.0 - unpacked libfoo-baz/1.0.0 - configured libfoo-baz/1.0.0 - %info: .+libfoo-baz-1.0.0.+ is up to date% - updated libfoo-baz/1.0.0 - EOE + $pkg_drop bar foo + } - $* libfoo-tests 2>>~%EOE%; - fetched libfoo-tests/1.0.0 - unpacked libfoo-tests/1.0.0 - configured libfoo-tests/1.0.0 - %info: .+libfoo-tests-1.0.0.+ is up to date% - updated libfoo-tests/1.0.0 - EOE + : replaced-scratch + : + : Test that changing package order on the command line does not result + : in a sub-optimal choice of the libbaz version (0.1.0). + : + : Note that this was not the case until we implemented the builds + : re-collection on the package version change. + : + { + $clone_cfg; - cat cfg/libfoo-tests-1.0.0/build/config.build >>~%EOO%; - %.* - config.libfoo_tests.test = libfoo-baz - %.* - EOO + $* foo bar --verbose 5 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent foo/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + %.* + trace: collect_build: add libbaz/0.1.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbaz/0.1.0 of dependent libbar/1.0.0 + trace: collect_build_prerequisites: begin libbaz/0.1.0 + trace: collect_build_prerequisites: end libbaz/0.1.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build: pick libbar/0.1.0 over libbar/1.0.0 + trace: collect_build: libbar/1.0.0 package version needs to be replaced with libbar/0.1.0 + trace: pkg_build: collection failed due to package version replacement, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: apply version replacement for libbar/1.0.0 + trace: collect_build: replacement: libbar/0.1.0 + trace: collect_build: add libbar/0.1.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/0.1.0 of dependent foo/1.0.0 + trace: collect_build_prerequisites: begin libbar/0.1.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbaz/1.0.0 of dependent libbar/0.1.0 + trace: collect_build_prerequisites: begin libbaz/1.0.0 + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_prerequisites: end libbar/0.1.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/0.1.0 of dependent bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: execute_plan: simulate: yes + %.* + EOE - $pkg_drop libfoo-baz libfoo-tests + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libbar configured 0.1.0 available 1.0.0 + libbaz configured 1.0.0 + !bar configured 1.0.0 + libbar configured 0.1.0 available 1.0.0 + libbaz configured 1.0.0 + EOO + + $pkg_drop foo bar + } + + : replaced-inplace + : + : Test the version replacement optimization. Here libbaz/1.0.0 get + : replaced with 0.1.0 but without re-collection from scratch since it + : does not have any dependencies. + : + { + $clone_cfg && $rep_add $rep/t12a && $rep_fetch; + + $* libbaz libbar --verbose 5 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: begin libbaz/1.0.0 + %.* + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + %.* + trace: collect_build: pick libbaz/0.1.0 over libbaz/1.0.0 + trace: collect_build: libbaz/1.0.0 package version needs to be replaced in-place with libbaz/0.1.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbaz/0.1.0 of dependent libbar/1.0.0 + trace: collect_build_prerequisites: begin libbaz/0.1.0 + trace: collect_build_prerequisites: end libbaz/0.1.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !libbaz configured 0.1.0 available 1.0.0 + !libbar configured 1.0.0 + !libbaz configured 0.1.0 available 1.0.0 + EOO + + $pkg_drop libbaz libbar + } } - : tests-only + : drop-dependent : { - $clone_cfg; + +$clone_root_cfg && $rep_add $rep/t12b && $rep_fetch - $* libfoo-tests 2>>~%EOE% != 0 - error: unable to select dependency alternative for package libfoo-tests/1.0.0 - info: explicitly specify dependency packages to manually select the alternative - info: alternative: libfoo-bar - info: alternative: libfoo-baz - info: while satisfying libfoo-tests/1.0.0 - EOE - } -} + test.arguments += --yes -: dependent -: -{ - +$clone_cfg - +$rep_add $rep/t2 && $rep_fetch + : unhold + : + : Test that the being dropped dependent does not constrain a dependency + : anymore. + : + { + $clone_cfg; - : update - : - : Test --update-dependent option. - : - { - $clone_cfg; + $* libbar 2>!; - $* --yes libbar 2>>~%EOE%; - fetched libfoo/1.0.0 - unpacked libfoo/1.0.0 - fetched libbar/1.0.0 - unpacked libbar/1.0.0 - configured libfoo/1.0.0 - configured libbar/1.0.0 - %info: .+ is up to date% - updated libbar/1.0.0 - EOE + $pkg_status -r >>EOO; + !libbar configured 1.0.0 + libbaz configured 0.1.0 available [1.0.0] + EOO - $rep_add $rep/t4a && $rep_fetch; + $* baz/0.1.0 ?libbar ?libbaz 2>!; - $* --update-dependent libfoo/1.1.0 <'y' 2>>~%EOE%; - upgrade libfoo/1.1.0 - reconfigure libbar (dependent of libfoo) - continue? [Y/n] disfigured libbar/1.0.0 - disfigured libfoo/1.0.0 - fetched libfoo/1.1.0 - unpacked libfoo/1.1.0 - configured libfoo/1.1.0 - configured libbar/1.0.0 - %info: .+ is up to date%{2} - updated libfoo/1.1.0 - updated libbar/1.0.0 - EOE + $pkg_status -r >>EOO; + !baz configured !0.1.0 available 1.0.0 + libbaz configured 1.0.0 + EOO - $pkg_disfigure libbar 2>'disfigured libbar/1.0.0'; - $pkg_purge libbar 2>'purged libbar/1.0.0'; + $pkg_drop baz + } - $pkg_disfigure libfoo 2>'disfigured libfoo/1.1.0'; - $pkg_purge libfoo 2>'purged libfoo/1.1.0' + : unuse + : + : Unlike the previous test, at the time we check the constraint applied + : by libbar on libbaz (== 0.1.0) there is no evidence that libbar will be + : dropped, which will happen some later execution plan refinement + : iteration. + : + : @@ This scenario is not supported yet and fails with: + : + : error: unable to upgrade package libbaz/0.1.0 to 1.0.0 + : info: because package libbar depends on (libbaz == 0.1.0) + : info: package libbaz/1.0.0 required by baz + : info: explicitly request up/downgrade of package libbar + : info: or explicitly specify package libbaz version to manually satisfy these constraints + : + : We could probably fix this postponing the constraints check in + : collect_order_dependents() until the final execution plan is produced + : (after all that refinement iterations). We could have an additional + : iteration after all the refinements which would enable the constraint + : check in collect_order_dependents(). + : + if false + { + $clone_cfg; + + $* foo 2>!; + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libbar configured 1.0.0 + libbaz configured 0.1.0 available [1.0.0] + EOO + + $* baz foo/0.1.0 2>|; + + $pkg_status -r >>EOO; + EOO + + $pkg_drop foo + } } - : leave - : - : Test --leave-dependent option. + : configuration-negotiation-order : { - $clone_cfg; + +$clone_root_cfg && $rep_add $rep/t11a && $rep_fetch - $* --yes libbar 2>>~%EOE%; - fetched libfoo/1.0.0 - unpacked libfoo/1.0.0 - fetched libbar/1.0.0 - unpacked libbar/1.0.0 - configured libfoo/1.0.0 - configured libbar/1.0.0 - %info: .+ is up to date% - updated libbar/1.0.0 - EOE + test.arguments += --yes --plan 'build plan:' --verbose 5 --build-option --quiet - $rep_add $rep/t4a && $rep_fetch; + : initial-collection + : + { + +$clone_cfg + + : postpone + : + { + $clone_cfg; + + $* foo fox fux 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fox/1.0.0 + trace: collect_build: add fux/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin fox/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent fox/1.0.0 + trace: postponed_configurations::add: add {fox 1,1: libfoo} to {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone fox/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo fox | libfoo->{foo/1,1 fox/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo fox | libfoo->{foo/1,1 fox/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fox/1.0.0 + trace: collect_build_prerequisites: resume fox/1.0.0 + trace: collect_build_prerequisites: end fox/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo fox | libfoo->{foo/1,1 fox/1,1}}! + trace: collect_build_postponed (1): end {foo fox | libfoo->{foo/1,1 fox/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + build plan: + new libfoo/1.0.0 (required by foo, fox, fux) + config.libfoo.extras=true (set by foo) + new foo/1.0.0 + new fox/1.0.0 + new fux/1.0.0 + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured 1.0.0 + !fox configured 1.0.0 + libfoo configured 1.0.0 + !fux configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop foo fox fux + } + + : postpone-system + : + { + $clone_cfg; + + $* foo fox '?sys:libfoo/*' 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fox/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency sys:libfoo/* of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin fox/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency sys:libfoo/* of dependent fox/1.0.0 + trace: postponed_configurations::add: add {fox 1,1: libfoo} to {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone fox/1.0.0 + %.* + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo fox | libfoo->{foo/1,1 fox/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo fox | libfoo->{foo/1,1 fox/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip system sys:libfoo/* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fox/1.0.0 + trace: collect_build_prerequisites: resume fox/1.0.0 + trace: collect_build_prerequisites: end fox/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo fox | libfoo->{foo/1,1 fox/1,1}}! + trace: collect_build_postponed (1): end {foo fox | libfoo->{foo/1,1 fox/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + build plan: + configure sys:libfoo/* + config.libfoo.extras=true (expected by foo) + new foo/1.0.0 + new fox/1.0.0 + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured,system !* available 1.0.0 0.1.0 + !fox configured 1.0.0 + libfoo configured,system !* available 1.0.0 0.1.0 + EOO + + $pkg_drop foo fox + } + + : postpone-merge + : + { + $clone_cfg; + + $* foo bar baz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bar/1.0.0 + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_prerequisites: begin baz/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent baz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent baz/1.0.0 + trace: postponed_configurations::add: add {baz 1,1: libbar libfoo} to {foo | libfoo->{foo/1,1}} + trace: postponed_configurations::add: merge {bar | libbar->{bar/1,1}} into {baz foo | libfoo->{baz/1,1 foo/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: postpone baz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bar baz foo | libfoo->{baz/1,1 foo/1,1} libbar->{bar/1,1 baz/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {bar baz foo | libfoo->{baz/1,1 foo/1,1} libbar->{bar/1,1 baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bar/1.0.0 + trace: collect_build_prerequisites: resume bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bar baz foo | libfoo->{baz/1,1 foo/1,1} libbar->{bar/1,1 baz/1,1}}! + trace: collect_build_postponed (1): end {bar baz foo | libfoo->{baz/1,1 foo/1,1} libbar->{bar/1,1 baz/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + build plan: + new libfoo/1.0.0 (required by baz, foo) + config.libfoo.extras=true (set by baz) + new foo/1.0.0 + new libbar/1.0.0 (required by bar, baz) + config.libbar.extras=true (set by bar) + new bar/1.0.0 + new baz/1.0.0 + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured 1.0.0 + !bar configured 1.0.0 + libbar configured 1.0.0 + !baz configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop foo bar baz + } + + : postpone-dependency-dependent + : + { + $clone_cfg; + + $* fex 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fex/1.0.0 + trace: collect_build_prerequisites: begin fex/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency foo/1.0.0 of dependent fex/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent fex/1.0.0 + trace: postponed_configurations::add: add {fex 2,1: libfoo} to {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone fex/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fex foo | libfoo->{fex/2,1 foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fex foo | libfoo->{fex/2,1 foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fex/1.0.0 + trace: collect_build_prerequisites: resume fex/1.0.0 + trace: collect_build_prerequisites: end fex/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {fex foo | libfoo->{fex/2,1 foo/1,1}}! + trace: collect_build_postponed (1): end {fex foo | libfoo->{fex/2,1 foo/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fex configured 1.0.0 + foo configured 1.0.0 + libfoo configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop fex + } + + : premature + : + { + $clone_cfg; + + $* fux foo fox 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fox/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fox/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin fox/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent fox/1.0.0 + trace: postponed_configurations::add: add {fox 1,1: libfoo} to {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone fox/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo fox | libfoo->{foo/1,1 fox/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo fox | libfoo->{foo/1,1 fox/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fox/1.0.0 + trace: collect_build_prerequisites: resume fox/1.0.0 + trace: collect_build_prerequisites: end fox/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo fox | libfoo->{foo/1,1 fox/1,1}}! + trace: collect_build_postponed (1): end {foo fox | libfoo->{foo/1,1 fox/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fux configured 1.0.0 + libfoo configured 1.0.0 + !foo configured 1.0.0 + libfoo configured 1.0.0 + !fox configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop fux foo fox + } + + : bogus-postponment + : + { + $clone_cfg; + + $* fux foo fix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (foo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: pkg_build: dep-postpone user-specified foo + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: pkg_build: erase bogus postponement libfoo + trace: pkg_build: bogus postponements erased, throwing + trace: pkg_build: collection failed due to bogus dependency collection postponement cancellation, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: pkg_build: dep-postpone user-specified foo + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (foo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: pkg_build: dep-postpone user-specified foo + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fix/1.0.0 + trace: collect_build_prerequisites: resume fix/1.0.0 + trace: collect_build_prerequisites: end fix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {fix | foo->{fix/1,1}}! + trace: collect_build_postponed (2): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (2): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (1): end {fix | foo->{fix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fux configured 1.0.0 + libfoo configured 1.0.0 + !foo configured 1.0.0 + libfoo configured 1.0.0 + !fix configured 1.0.0 + !foo configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop fux foo fix + } + } + + : existing + : + { + +$clone_cfg + + : dependency + : + { + $clone_cfg; + + $* libfoo 2>!; + + $* foo 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + build plan: + reconfigure/update libfoo/1.0.0 (required by foo) + config.libfoo.extras=true (set by foo) + new foo/1.0.0 + %.* + EOE + + $pkg_status -r >>EOO; + !libfoo configured 1.0.0 + !foo configured 1.0.0 + !libfoo configured 1.0.0 + EOO + + $pkg_drop foo libfoo + } + + : dependent-single-position + : + { + +$clone_cfg + + : basic + : + { + $clone_cfg; + + $* foo 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + # Downgrade the dependency. + # + $* ?libfoo/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: execute_plan: simulate: yes + %.* + trace: evaluate_dependency: libfoo/1.0.0: update to libfoo/0.1.0 + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of existing dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo^ | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {foo^ | libfoo->{foo/1,1}} + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: reeval foo/1.0.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: postponed_configurations::add: add {foo^ 1,1: libfoo} to {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent foo/1.0.0 results in {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: re-evaluated foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo^ | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): end {foo^ | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + build plan: + downgrade libfoo/0.1.0 + config.libfoo.extras=true (set by foo) + reconfigure/update foo/1.0.0 (dependent of libfoo) + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured !0.1.0 available 1.0.0 + EOO + + # Reconfigure the dependency and hold. + # + $* libfoo/0.1.0 +{ config.libfoo.extras=true } 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of existing dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo^ | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: reeval foo/1.0.0 + %.* + trace: postponed_configurations::add: add {foo^ 1,1: libfoo} to {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent foo/1.0.0 results in {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: re-evaluated foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo^ | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): end {foo^ | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + build plan: + reconfigure/update libfoo/0.1.0 + config.libfoo.extras=true (user configuration) + reconfigure/update foo/1.0.0 (dependent of libfoo) + %.* + EOE + + $pkg_status -r >>EOO; + !libfoo configured !0.1.0 available 1.0.0 + !foo configured 1.0.0 + !libfoo configured !0.1.0 available 1.0.0 + EOO + + # Upgrade the dependency and unhold existing dependent. + # + $* libfoo ?foo 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of existing dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo^ | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: reeval foo/1.0.0 + %.* + trace: collect_build: pick libfoo/1.0.0 over libfoo/0.1.0 + trace: postponed_configurations::add: add {foo^ 1,1: libfoo} to {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent foo/1.0.0 results in {foo^ | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: re-evaluated foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo^ | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): end {foo^ | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + trace: evaluate_dependency: foo/1.0.0: unused + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: collect_drop: foo/1.0.0 package version needs to be replaced with drop + trace: pkg_build: collection failed due to package version replacement, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/1.0.0 + %.* + trace: collect_build_prerequisites: skip expected to be dropped existing dependent foo of dependency libfoo + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_drop: overwrite foo + %.* + trace: execute_plan: simulate: yes + %.* + build plan: + upgrade libfoo/1.0.0 + drop foo/1.0.0 (unused) + %.* + EOE + + $pkg_status -r >>EOO; + !libfoo configured 1.0.0 + EOO + + $pkg_drop libfoo + } + + : postpone-existing + : + { + $clone_cfg; + + # Dependencies: + # + # fix/1.0.0: depends: foo(c) + # foo/1.0.0: depends: libfoo(c) + # + # fix/0.1.0: depends: foo == 0.1.0 + # foo/0.1.0: depends: libfoo(c) + # + $* fix 2>!; + + $* libfoo/0.1.0 fix/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + trace: collect_build: add fix/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of existing dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo^ | libfoo->{foo/1,1}} + %.* + trace: collect_build_prerequisites: begin fix/0.1.0 + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + trace: collect_build: add fix/0.1.0 + %.* + trace: collect_build_prerequisites: skip expected to be built existing dependent foo of dependency libfoo + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + %.* + trace: collect_build_prerequisites: begin fix/0.1.0 + %.* + trace: collect_build: apply version replacement for foo/0.1.0 + trace: collect_build: replacement: foo/0.1.0 + trace: collect_build: add foo/0.1.0 + info: package fix dependency on (foo == 0.1.0) is forcing downgrade of foo/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: no cfg-clause for dependency foo/0.1.0 of dependent fix/0.1.0 + %.* + trace: collect_build_prerequisites: skip being built existing dependent fix of dependency foo + trace: collect_build_prerequisites: begin foo/0.1.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/0.1.0 of dependent foo/0.1.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + trace: collect_build: add fix/0.1.0 + trace: pkg_build: dep-postpone user-specified libfoo + %.* + trace: collect_build_prerequisites: begin fix/0.1.0 + %.* + trace: collect_build: apply version replacement for foo/0.1.0 + trace: collect_build: replacement: foo/0.1.0 + trace: collect_build: add foo/0.1.0 + info: package fix dependency on (foo == 0.1.0) is forcing downgrade of foo/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: no cfg-clause for dependency foo/0.1.0 of dependent fix/0.1.0 + %.* + trace: collect_build_prerequisites: skip being built existing dependent fix of dependency foo + trace: collect_build_prerequisites: begin foo/0.1.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of dependent foo/0.1.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/0.1.0 + trace: collect_build_prerequisites: end fix/0.1.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): skip being built existing dependent foo of dependency libfoo + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/0.1.0 + trace: collect_build_prerequisites: resume foo/0.1.0 + trace: collect_build_prerequisites: end foo/0.1.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !libfoo configured !0.1.0 available 1.0.0 + !fix configured !0.1.0 available 1.0.0 + foo configured 0.1.0 available 1.0.0 + !libfoo configured !0.1.0 available 1.0.0 + EOO + + $pkg_drop fix libfoo --drop-dependent + } + } + + : dependent-multiple-positions + : + { + +$clone_cfg + + : non-negotiated + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + $* tex 2>!; + + $* libfoo/0.1.0 libbar/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + trace: collect_build: add libbar/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of existing dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/0.1.0 of existing dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex^ | libbar->{tex/1,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): cannot re-evaluate dependent tex to dependency index 2 due to earlier dependency index 1 in {tex^ | libbar->{tex/1,1}}, skipping {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (0): postpone cfg-negotiation of {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): begin {tex^ | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tex^ | libbar->{tex/1,1}} + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: reeval tex/1.0.0 + %.* + trace: collect_build: pick libbar/0.1.0 over libbar/1.0.0 + trace: postponed_configurations::add: add {tex^ 1,1: libbar} to {tex^ | libbar->{tex/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent tex/1.0.0 results in {tex^ | libbar->{tex/1,1}} + trace: collect_build_prerequisites: re-evaluated tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tex^ | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/0.1.0 + trace: collect_build_prerequisites: end libbar/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 2,1: libfoo} to {tex^ | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tex^ | libbar->{tex/1,1}}! + trace: collect_build_postponed (2): begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (2): cfg-negotiate begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex^ | libfoo->{tex/2,1}}! + trace: collect_build_postponed (2): end {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): end {tex^ | libbar->{tex/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + build plan: + downgrade libfoo/0.1.0 + config.libfoo.extras=true (set by tex) + downgrade libbar/0.1.0 + config.libbar.extras=true (set by tex) + reconfigure/update tex/1.0.0 (dependent of libbar) + %.* + EOE + + $pkg_status -r >>EOO; + !libbar configured !0.1.0 available 1.0.0 + !libfoo configured !0.1.0 available 1.0.0 + !tex configured 1.0.0 + !libbar configured !0.1.0 available 1.0.0 + !libfoo configured !0.1.0 available 1.0.0 + EOO + + $pkg_drop tex libfoo libbar + } + + : negotiated + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # bar: depends: libbar == 0.1.0 (c) + # + $* tex 2>!; + + $* libfoo/0.1.0 bar/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + trace: collect_build: add bar/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of existing dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex^ | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: begin bar/0.1.0 + %.* + trace: collect_build: add libbar/0.1.0 + info: package bar dependency on (libbar == 0.1.0) is forcing downgrade of libbar/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/0.1.0 of dependent bar/0.1.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/0.1.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: reeval tex/1.0.0 + %.* + trace: collect_build: pick libbar/0.1.0 over libbar/1.0.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: postponed_configurations::add: add {tex^ 2,1: libfoo} to {tex^ | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent tex/1.0.0 results in {tex^ | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: re-evaluated tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tex^ | libfoo->{tex/2,1}}! + trace: collect_build_postponed (2): begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (2): re-evaluate existing dependents for {bar | libbar->{bar/1,1}} + trace: collect_build_postponed (2): cannot re-evaluate dependent tex to dependency index 1 due to greater dependency index 2 in {tex^ | libfoo->{tex/2,1}}!, throwing postpone_position + trace: pkg_build: collection failed due to earlier dependency position, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libfoo/0.1.0 + trace: collect_build: add bar/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of existing dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex^ | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: begin bar/0.1.0 + %.* + trace: collect_build: add libbar/0.1.0 + info: package bar dependency on (libbar == 0.1.0) is forcing downgrade of libbar/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/0.1.0 of dependent bar/0.1.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/0.1.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): pos-postpone existing dependent tex re-evaluation to dependency index 2 due to recorded index 1, skipping {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (0): postpone cfg-negotiation of {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bar | libbar->{bar/1,1}} + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: reeval tex/1.0.0 + %.* + trace: collect_build: pick libbar/0.1.0 over libbar/1.0.0 + trace: postponed_configurations::add: add {tex^ 1,1: libbar} to {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent tex/1.0.0 results in {bar tex^ | libbar->{bar/1,1 tex/1,1}} + trace: collect_build_prerequisites: re-evaluated tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {bar tex^ | libbar->{bar/1,1 tex/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/0.1.0 + trace: collect_build_prerequisites: end libbar/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bar/0.1.0 + trace: collect_build_prerequisites: resume bar/0.1.0 + trace: collect_build_prerequisites: end bar/0.1.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 2,1: libfoo} to {tex^ | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bar tex^ | libbar->{bar/1,1 tex/1,1}}! + trace: collect_build_postponed (2): begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (2): cfg-negotiate begin {tex^ | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex^ | libfoo->{tex/2,1}}! + trace: collect_build_postponed (2): end {tex^ | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): end {bar | libbar->{bar/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !libfoo configured !0.1.0 available 1.0.0 + !tex configured 1.0.0 + libbar configured 0.1.0 available 1.0.0 + !libfoo configured !0.1.0 available 1.0.0 + !bar configured !0.1.0 available 1.0.0 + libbar configured 0.1.0 available 1.0.0 + EOO + + $pkg_drop tex libfoo bar + } + + : up-negotiate + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # baz: depends: {libbar libfoo} == 0.1.0 (c) + # + $* tex 2>!; + + $* baz/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add baz/0.1.0 + trace: collect_build_prerequisites: begin baz/0.1.0 + %.* + trace: collect_build: add libbar/0.1.0 + info: package baz dependency on (libbar == 0.1.0) is forcing downgrade of libbar/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/0.1.0 of dependent baz/0.1.0 + trace: collect_build: add libfoo/0.1.0 + info: package baz dependency on (libfoo == 0.1.0) is forcing downgrade of libfoo/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of dependent baz/0.1.0 + trace: postponed_configurations::add: create {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone baz/0.1.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: reeval tex/1.0.0 + %.* + trace: collect_build: pick libbar/0.1.0 over libbar/1.0.0 + trace: postponed_configurations::add: add {tex^ 1,1: libbar} to {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent tex/1.0.0 results in {baz tex^ | libbar->{baz/1,1 tex/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {baz tex^ | libbar->{baz/1,1 tex/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/0.1.0 + trace: collect_build_prerequisites: end libbar/0.1.0 + trace: collect_build_prerequisites: begin libfoo/0.1.0 + trace: collect_build_prerequisites: end libfoo/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/0.1.0 + trace: collect_build_prerequisites: resume baz/0.1.0 + trace: collect_build_prerequisites: end baz/0.1.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: pick libfoo/0.1.0 over libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/0.1.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 2,1: libfoo} to {baz tex^ | libbar->{baz/1,1 tex/1,1} libfoo->{baz/1,1}}? + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tex/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libfoo/0.1.0 of dependent tex/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {baz tex^ | libbar->{baz/1,1 tex/1,1} libfoo->{baz/1,1 tex/2,1}}! + trace: collect_build_postponed (1): end {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tex configured 1.0.0 + libbar configured 0.1.0 available 1.0.0 + libfoo configured 0.1.0 available 1.0.0 + !baz configured !0.1.0 available 1.0.0 + libbar configured 0.1.0 available 1.0.0 + libfoo configured 0.1.0 available 1.0.0 + EOO + + $pkg_drop tex baz + } + + : replace-re-evaluation + : + { + +$clone_cfg + + : initial-collection + : + { + $clone_cfg; + + # Dependencies: + # + # bax: depends: libfoo(c) + # depends: {libbox libbar} (c) + # + # baz: depends: {libbar libfoo} (c) + # + $* bax baz 2>!; + + $* libbox/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbox/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of existing dependent bax/1.0.0 + trace: postponed_configurations::add: create {bax^ | libbox->{bax/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bax^ | libbox->{bax/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libbox->{bax/2,1}} + %.* + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {bax^ 2,1: libbox libbar} to {bax^ | libbox->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libbox->{bax/2,1} libbar->{bax/2,1}} + %.* + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libbox->{bax/2,1} libbar->{bax/2,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libfoo + trace: collect_build_postponed (1): cannot re-evaluate dependent bax to dependency index 1 since it is already re-evaluated to greater index 2 in {bax^ baz^ | libbox->{bax/2,1} libbar->{bax/2,1 baz/1,1} libfoo->{baz/1,1}}, throwing postpone_position + trace: pkg_build: collection failed due to earlier dependency position, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbox/0.1.0 + %.* + trace: collect_build_prerequisites: replace dependency at index 2 of existing dependent bax/1.0.0 with dependency libfoo/1.0.0 at index 1 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of existing dependent bax/1.0.0 + trace: postponed_configurations::add: create {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libfoo->{bax/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: add {bax 2,1: libbox libbar} to {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}}? + %.* + trace: collect_build_prerequisites: cfg-postponing dependent bax/1.0.0 involves (being) negotiated configurations and results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1}}?, throwing retry_configuration + trace: collect_build_postponed (0): cfg-negotiation of {bax^ | libfoo->{bax/1,1}} failed due to dependent bax, refining configuration + trace: collect_build_postponed (1): begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libfoo->{bax/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: add {bax 2,1: libbox libbar} to {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}}? + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bax/1.0.0 is negotiated + trace: collect_build_prerequisites: collecting cfg-postponed dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: begin libbox/0.1.0 + trace: collect_build_prerequisites: end libbox/0.1.0 + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bax/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bax/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1}}! + trace: collect_build_postponed (1): end {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !libbox configured !0.1.0 available 1.0.0 + !bax configured 1.0.0 + libbar configured 1.0.0 + !libbox configured !0.1.0 available 1.0.0 + libfoo configured 1.0.0 + !baz configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop bax baz libbox + } + + : collect-postponed + : + { + $clone_cfg; + + # Dependencies: + # + # bax: depends: libfoo(c) + # depends: {libbox libbar} (c) + # + # baz: depends: {libbar libfoo} (c) + # + # box: depends: libbox == 0.1.0 (c) + # + $* bax baz 2>!; + + $* box/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add box/0.1.0 + trace: collect_build_prerequisites: begin box/0.1.0 + %.* + trace: collect_build: add libbox/0.1.0 + info: package box dependency on (libbox == 0.1.0) is forcing downgrade of libbox/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent box/0.1.0 + trace: postponed_configurations::add: create {box | libbox->{box/1,1}} + trace: collect_build_prerequisites: postpone box/0.1.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {box | libbox->{box/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {box | libbox->{box/1,1}} + %.* + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {bax^ 2,1: libbox libbar} to {box | libbox->{box/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ box | libbox->{bax/2,1 box/1,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ box | libbox->{bax/2,1 box/1,1} libbar->{bax/2,1}} + %.* + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ box | libbox->{bax/2,1 box/1,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ box | libbox->{bax/2,1 box/1,1} libbar->{bax/2,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libfoo + trace: collect_build_postponed (1): cannot re-evaluate dependent bax to dependency index 1 since it is already re-evaluated to greater index 2 in {bax^ baz^ box | libbox->{bax/2,1 box/1,1} libbar->{bax/2,1 baz/1,1} libfoo->{baz/1,1}}, throwing postpone_position + trace: pkg_build: collection failed due to earlier dependency position, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add box/0.1.0 + trace: collect_build_prerequisites: begin box/0.1.0 + %.* + trace: collect_build: add libbox/0.1.0 + info: package box dependency on (libbox == 0.1.0) is forcing downgrade of libbox/1.0.0 to 0.1.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent box/0.1.0 + trace: postponed_configurations::add: create {box | libbox->{box/1,1}} + trace: collect_build_prerequisites: postpone box/0.1.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {box | libbox->{box/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {box | libbox->{box/1,1}} + trace: collect_build_postponed (1): pos-postpone existing dependent bax re-evaluation to dependency index 2 due to recorded index 1, skipping {box | libbox->{box/1,1}} + trace: collect_build_postponed (0): replace dependency at index 2 of existing dependent bax/1.0.0 with dependency libfoo/1.0.0 at index 1 + trace: collect_build: add libfoo/1.0.0 + trace: postponed_configurations::add: create {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (0): postpone cfg-negotiation of {box | libbox->{box/1,1}} + trace: collect_build_postponed (1): begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libfoo->{bax/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: add {bax 2,1: libbox libbar} to {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}}? + trace: postponed_configurations::add: merge {box | libbox->{box/1,1}} into {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1}}? + trace: collect_build_prerequisites: cfg-postponing dependent bax/1.0.0 merges non-negotiated and/or being negotiated configurations in and results in {bax^ baz^ box | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1 box/1,1}}?, throwing merge_configuration + trace: collect_build_postponed (0): cfg-negotiation of {bax^ | libfoo->{bax/1,1}} failed due to non-negotiated clusters, force-merging based on shadow cluster {bax^ baz^ box | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1 box/1,1}}? + trace: collect_build_postponed (0): force-merge {box | libbox->{box/1,1}} into {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (1): begin {bax^ box | libfoo->{bax/1,1} libbox->{box/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ box | libfoo->{bax/1,1} libbox->{box/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ box | libfoo->{bax/1,1} libbox->{box/1,1}} (shadow cluster-based) + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ box | libfoo->{bax/1,1} libbox->{box/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build: add baz/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ box | libfoo->{bax/1,1} libbox->{box/1,1}} (shadow cluster-based) + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ box | libfoo->{bax/1,1 baz/1,1} libbox->{box/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ baz^ box | libfoo->{bax/1,1 baz/1,1} libbox->{box/1,1} libbar->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_prerequisites: begin libbox/0.1.0 + trace: collect_build_prerequisites: end libbox/0.1.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: add {bax 2,1: libbox libbar} to {bax^ baz^ box | libfoo->{bax/1,1 baz/1,1} libbox->{box/1,1} libbar->{baz/1,1}}? (shadow cluster-based) + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bax/1.0.0 is shadow-negotiated + trace: collect_build_prerequisites: dependency libbox/0.1.0 of dependent bax/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bax/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bax/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent box/0.1.0 + trace: collect_build_prerequisites: resume box/0.1.0 + trace: collect_build_prerequisites: end box/0.1.0 + trace: collect_build_postponed (1): cfg-negotiate end {bax^ baz^ box | libfoo->{bax/1,1 baz/1,1} libbox->{bax/2,1 box/1,1} libbar->{bax/2,1 baz/1,1}}! + trace: collect_build_postponed (1): end {bax^ box | libfoo->{bax/1,1} libbox->{box/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !bax configured 1.0.0 + libbar configured 1.0.0 + libbox configured 0.1.0 available 1.0.0 + libfoo configured 1.0.0 + !baz configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !box configured !0.1.0 available 1.0.0 + libbox configured 0.1.0 available 1.0.0 + EOO + + $pkg_drop bax baz box + } + + : bogus-harmless + : + { + $clone_cfg; + + # Dependencies: + # + # bax: depends: libfoo(c) + # depends: {libbox libbar} (c) + # + # baz: depends: {libbar libfoo} (c) + # + $* bax baz 2>!; + + $* libbox/0.1.0 ?baz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbox/0.1.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of existing dependent bax/1.0.0 + trace: postponed_configurations::add: create {bax^ | libbox->{bax/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bax^ | libbox->{bax/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libbox->{bax/2,1}} + %.* + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {bax^ 2,1: libbox libbar} to {bax^ | libbox->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libbox->{bax/2,1} libbar->{bax/2,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libfoo + trace: collect_build_postponed (1): cannot re-evaluate dependent bax to dependency index 1 since it is already re-evaluated to greater index 2 in {bax^ baz^ | libbox->{bax/2,1} libbar->{bax/2,1 baz/1,1} libfoo->{baz/1,1}}, throwing postpone_position + trace: pkg_build: collection failed due to earlier dependency position, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbox/0.1.0 + %.* + trace: collect_build_prerequisites: replace dependency at index 2 of existing dependent bax/1.0.0 with dependency libfoo/1.0.0 at index 1 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of existing dependent bax/1.0.0 + trace: postponed_configurations::add: create {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libfoo->{bax/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: add {bax 2,1: libbox libbar} to {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}}? + %.* + trace: collect_build_prerequisites: cfg-postponing dependent bax/1.0.0 involves (being) negotiated configurations and results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1}}?, throwing retry_configuration + trace: collect_build_postponed (0): cfg-negotiation of {bax^ | libfoo->{bax/1,1}} failed due to dependent bax, refining configuration + trace: collect_build_postponed (1): begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libfoo->{bax/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build_prerequisites: reeval baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: add {baz^ 1,1: libbar libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent baz/1.0.0 results in {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + trace: collect_build_prerequisites: re-evaluated baz/1.0.0 + %.* + trace: collect_build_postponed (1): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (1): skip being built existing dependent baz of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: add {bax 2,1: libbox libbar} to {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{baz/1,1}}? + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bax/1.0.0 is negotiated + trace: collect_build_prerequisites: collecting cfg-postponed dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build_prerequisites: begin libbox/0.1.0 + trace: collect_build_prerequisites: end libbox/0.1.0 + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bax/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bax/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bax^ baz^ | libfoo->{bax/1,1 baz/1,1} libbar->{bax/2,1 baz/1,1} libbox->{bax/2,1}}! + trace: collect_build_postponed (1): end {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + trace: evaluate_dependency: baz/1.0.0: unused + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: collect_drop: baz/1.0.0 package version needs to be replaced with drop + trace: pkg_build: collection failed due to package version replacement, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbox/0.1.0 + trace: pkg_build: dep-postpone user-specified libbox + trace: collect_drop: overwrite baz + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (0): erase bogus postponement libbox + trace: collect_build_postponed (0): bogus postponements erased, throwing + trace: pkg_build: collection failed due to bogus dependency collection postponement cancellation, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add libbox/0.1.0 + %.* + trace: collect_build_prerequisites: replace dependency at index 2 of existing dependent bax/1.0.0 with dependency libfoo/1.0.0 at index 1 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of existing dependent bax/1.0.0 + trace: postponed_configurations::add: create {bax^ | libfoo->{bax/1,1}} + trace: collect_drop: overwrite baz + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): skip being dropped existing dependent baz of dependency libfoo + trace: collect_build_postponed (1): re-evaluate existing dependents for {bax^ | libfoo->{bax/1,1}} + trace: collect_build: add bax/1.0.0 + trace: collect_build_prerequisites: reeval bax/1.0.0 + %.* + trace: postponed_configurations::add: add {bax^ 1,1: libfoo} to {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bax/1.0.0 results in {bax^ | libfoo->{bax/1,1}} + trace: collect_build_prerequisites: re-evaluated bax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {bax^ | libfoo->{bax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + %.* + trace: collect_build: pick libbox/0.1.0 over libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/0.1.0 of dependent bax/1.0.0 + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bax/1.0.0 + trace: postponed_configurations::add: create {bax | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_prerequisites: postpone bax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bax^ | libfoo->{bax/1,1}}! + trace: collect_build_postponed (2): begin {bax | libbox->{bax/2,1} libbar->{bax/2,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent bax of dependency libbox + %.* + trace: collect_build_postponed (2): skip being built existing dependent bax of dependency libbar + trace: collect_build_postponed (2): skip being dropped existing dependent baz of dependency libbar + trace: collect_build_postponed (2): cfg-negotiate begin {bax | libbox->{bax/2,1} libbar->{bax/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/0.1.0 + trace: collect_build_prerequisites: end libbox/0.1.0 + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bax/1.0.0 + trace: collect_build_prerequisites: resume bax/1.0.0 + trace: collect_build_prerequisites: end bax/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {bax | libbox->{bax/2,1} libbar->{bax/2,1}}! + trace: collect_build_postponed (2): end {bax | libbox->{bax/2,1} libbar->{bax/2,1}} + trace: collect_build_postponed (1): end {bax^ | libfoo->{bax/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !libbox configured !0.1.0 available 1.0.0 + !bax configured 1.0.0 + libbar configured 1.0.0 + !libbox configured !0.1.0 available 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop bax libbox + } + } + } + } + + : postponed-collection + : + { + +$clone_cfg + + : backtrace + : + { + $clone_cfg; + + $* foo bar box 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bar/1.0.0 + trace: collect_build: add box/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_prerequisites: begin box/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent box/1.0.0 since max index is reached: 0 + info: dependency alternative: {libbar libfoo} + { + require + { + config.libbar.extras = true + config.libfoo.extras = true + } + } + trace: collect_build_prerequisites: postpone box/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (2): begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bar/1.0.0 + trace: collect_build_prerequisites: resume bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {bar | libbar->{bar/1,1}}! + trace: collect_build_postponed (2): index 1 collect alt-postponed box/1.0.0 + trace: collect_build_prerequisites: resume box/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent box/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent box/1.0.0 + trace: postponed_configurations::add: add {box 1,1: libbar libfoo} to {foo | libfoo->{foo/1,1}}! + trace: postponed_configurations::add: merge {bar | libbar->{bar/1,1}}! into {box foo | libfoo->{box/1,1 foo/1,1} libbar->{box/1,1}}! + %.* + trace: collect_build_prerequisites: cfg-postponing dependent box/1.0.0 involves (being) negotiated configurations and results in {bar box foo | libfoo->{box/1,1 foo/1,1} libbar->{bar/1,1 box/1,1}}!, throwing retry_configuration + trace: collect_build_postponed (0): cfg-negotiation of {foo | libfoo->{foo/1,1}} failed due to dependent box, refining configuration + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (2): begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bar/1.0.0 + trace: collect_build_prerequisites: resume bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {bar | libbar->{bar/1,1}}! + trace: collect_build_postponed (2): index 1 collect alt-postponed box/1.0.0 + trace: collect_build_prerequisites: resume box/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent box/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent box/1.0.0 + trace: postponed_configurations::add: add {box 1,1: libbar libfoo} to {foo | libfoo->{foo/1,1}}! + trace: postponed_configurations::add: merge {bar | libbar->{bar/1,1}}! into {box foo | libfoo->{box/1,1 foo/1,1} libbar->{box/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent box/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent box/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: dependency libfoo/1.0.0 of dependent box/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end box/1.0.0 + trace: collect_build_postponed (2): end {bar | libbar->{bar/1,1}} + trace: collect_build_postponed (1): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured 1.0.0 + !bar configured 1.0.0 + libbar configured 1.0.0 + !box configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop foo bar box + } + + : premature + : + { + $clone_cfg; + + $* fux fix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fix/1.0.0 + trace: collect_build_prerequisites: resume fix/1.0.0 + trace: collect_build_prerequisites: end fix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {fix | foo->{fix/1,1}}! + trace: collect_build_postponed (2): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (2): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (1): end {fix | foo->{fix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fux configured 1.0.0 + libfoo configured 1.0.0 + !fix configured 1.0.0 + foo configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop fux fix + } + + : double-premature + : + { + $clone_cfg; + + $* fux bex fix buz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add bex/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build: add buz/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin bex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent bex/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: end bex/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_prerequisites: begin buz/1.0.0 + %.* + trace: collect_build: add bux/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bux/1.0.0 of dependent buz/1.0.0 + trace: postponed_configurations::add: create {buz | bux->{buz/1,1}} + trace: collect_build_prerequisites: postpone buz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add bex/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build: add buz/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin bex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent bex/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: end bex/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_prerequisites: begin buz/1.0.0 + %.* + trace: collect_build: add bux/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bux/1.0.0 of dependent buz/1.0.0 + trace: postponed_configurations::add: create {buz | bux->{buz/1,1}} + trace: collect_build_prerequisites: postpone buz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fix/1.0.0 + trace: collect_build_prerequisites: resume fix/1.0.0 + trace: collect_build_prerequisites: end fix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {fix | foo->{fix/1,1}}! + trace: collect_build_postponed (2): begin {buz | bux->{buz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {buz | bux->{buz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bux/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libbar/1.0.0 of dependent bux/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libbar), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add bex/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build: add buz/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin bex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libbar/1.0.0 of dependent bex/1.0.0 + trace: collect_build_prerequisites: end bex/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_prerequisites: begin buz/1.0.0 + %.* + trace: collect_build: add bux/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bux/1.0.0 of dependent buz/1.0.0 + trace: postponed_configurations::add: create {buz | bux->{buz/1,1}} + trace: collect_build_prerequisites: postpone buz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fix/1.0.0 + trace: collect_build_prerequisites: resume fix/1.0.0 + trace: collect_build_prerequisites: end fix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {fix | foo->{fix/1,1}}! + trace: collect_build_postponed (2): begin {buz | bux->{buz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {buz | bux->{buz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bux/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bux/1.0.0 + trace: postponed_configurations::add: create {bux | libbar->{bux/1,1}} + trace: collect_build_prerequisites: postpone bux/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent buz/1.0.0 + trace: collect_build_prerequisites: resume buz/1.0.0 + trace: collect_build_prerequisites: end buz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {buz | bux->{buz/1,1}}! + trace: collect_build_postponed (3): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (4): begin {bux | libbar->{bux/1,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {bux | libbar->{bux/1,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent bux/1.0.0 + trace: collect_build_prerequisites: resume bux/1.0.0 + trace: collect_build_prerequisites: end bux/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {bux | libbar->{bux/1,1}}! + trace: collect_build_postponed (4): end {bux | libbar->{bux/1,1}} + trace: collect_build_postponed (3): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (2): end {buz | bux->{buz/1,1}} + trace: collect_build_postponed (1): end {fix | foo->{fix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fux configured 1.0.0 + libfoo configured 1.0.0 + !fix configured 1.0.0 + foo configured 1.0.0 + libfoo configured 1.0.0 + !bex configured 1.0.0 + libbar configured 1.0.0 + !buz configured 1.0.0 + bux configured 1.0.0 + libbar configured 1.0.0 + EOO + + $pkg_drop fux bex fix buz + } + + : up-negotiate-dependency + : + { + $clone_cfg; + + # Dependencies: + # + # bar: depends: libbar(c) + # + # bux: depends: libbar(c) + # + # bix: depends: {libbar bar} (c) + # depends: bux + # + $* bix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add bix/1.0.0 + trace: collect_build_prerequisites: begin bix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bix/1.0.0 + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent bix/1.0.0 + trace: postponed_configurations::add: create {bix | libbar->{bix/1,1} bar->{bix/1,1}} + trace: collect_build_prerequisites: postpone bix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bix | libbar->{bix/1,1} bar->{bix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {bix | libbar->{bix/1,1} bar->{bix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {bix | libbar->{bix/1,1} bar->{bix/1,1}}? + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bix/1.0.0 + trace: collect_build_prerequisites: resume bix/1.0.0 + %.* + trace: collect_build: add bux/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency bux/1.0.0 of dependent bix/1.0.0 + trace: collect_build_prerequisites: begin bux/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bux/1.0.0 + trace: postponed_configurations::add: add {bux 1,1: libbar} to {bar bix | libbar->{bar/1,1 bix/1,1} bar->{bix/1,1}}? + %.* + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bux/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bux/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bux/1.0.0 + trace: collect_build_prerequisites: end bix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bar bix bux | libbar->{bar/1,1 bix/1,1 bux/1,1} bar->{bix/1,1}}! + trace: collect_build_postponed (1): end {bix | libbar->{bix/1,1} bar->{bix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !bix configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + bux configured 1.0.0 + libbar configured 1.0.0 + libbar configured 1.0.0 + EOO + + $pkg_drop bix + } + + : postponed-alts + : + { + +$clone_cfg + + : with-premature + : + { + $clone_cfg; + + $* fux boo 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add boo/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 0 + info: dependency alternative: libbar + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (0): index 1 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 1 + info: dependency alternative: libfoo + { + require + { + config.libfoo.extras = true + } + } + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (0): index 2 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent boo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add boo/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 0 + info: dependency alternative: libbar + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (0): index 1 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 1 + info: dependency alternative: libfoo + { + require + { + config.libfoo.extras = true + } + } + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (0): index 2 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent boo/1.0.0 + trace: postponed_configurations::add: create {boo | libfoo->{boo/1,2}} + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (1): begin {boo | libfoo->{boo/1,2}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {boo | libfoo->{boo/1,2}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + trace: collect_build_prerequisites: end boo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {boo | libfoo->{boo/1,2}}! + trace: collect_build_postponed (1): end {boo | libfoo->{boo/1,2}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fux configured 1.0.0 + libfoo configured 1.0.0 + !boo configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop fux boo + } + + : with-premature-complex + : + { + $clone_cfg; + + $* fux bex fix biz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add bex/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build: add biz/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin bex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent bex/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: end bex/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_prerequisites: begin biz/1.0.0 + %.* + trace: collect_build: add boo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency boo/1.0.0 of dependent biz/1.0.0 + trace: postponed_configurations::add: create {biz | boo->{biz/1,1}} + trace: collect_build_prerequisites: postpone biz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add fux/1.0.0 + trace: collect_build: add bex/1.0.0 + trace: collect_build: add fix/1.0.0 + trace: collect_build: add biz/1.0.0 + trace: collect_build_prerequisites: begin fux/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent fux/1.0.0 + trace: collect_build_prerequisites: end fux/1.0.0 + trace: collect_build_prerequisites: begin bex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent bex/1.0.0 + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: end bex/1.0.0 + trace: collect_build_prerequisites: begin fix/1.0.0 + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent fix/1.0.0 + trace: postponed_configurations::add: create {fix | foo->{fix/1,1}} + trace: collect_build_prerequisites: postpone fix/1.0.0 + trace: collect_build_prerequisites: begin biz/1.0.0 + %.* + trace: collect_build: add boo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency boo/1.0.0 of dependent biz/1.0.0 + trace: postponed_configurations::add: create {biz | boo->{biz/1,1}} + trace: collect_build_prerequisites: postpone biz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {fix | foo->{fix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent fix/1.0.0 + trace: collect_build_prerequisites: resume fix/1.0.0 + trace: collect_build_prerequisites: end fix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {fix | foo->{fix/1,1}}! + trace: collect_build_postponed (2): begin {biz | boo->{biz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {biz | boo->{biz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 0 + info: dependency alternative: libbar + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent biz/1.0.0 + trace: collect_build_prerequisites: resume biz/1.0.0 + trace: collect_build_prerequisites: end biz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {biz | boo->{biz/1,1}}! + trace: collect_build_postponed (3): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (3): index 1 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent boo/1.0.0 + trace: collect_build_prerequisites: end boo/1.0.0 + trace: collect_build_postponed (3): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (2): end {biz | boo->{biz/1,1}} + trace: collect_build_postponed (1): end {fix | foo->{fix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !fux configured 1.0.0 + libfoo configured 1.0.0 + !fix configured 1.0.0 + foo configured 1.0.0 + libfoo configured 1.0.0 + !bex configured 1.0.0 + libbar configured 1.0.0 + !biz configured 1.0.0 + boo configured 1.0.0 + libbar configured 1.0.0 + EOO + + $pkg_drop fux bex fix biz + } + + : up-negotiate + : + { + $clone_cfg; + + $* foo boo 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add boo/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 0 + info: dependency alternative: libbar + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (1): index 1 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 1 + info: dependency alternative: libfoo + { + require + { + config.libfoo.extras = true + } + } + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (1): index 2 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent boo/1.0.0 + trace: postponed_configurations::add: add {boo 1,2: libfoo} to {foo | libfoo->{foo/1,1}}! + %.* + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent boo/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libfoo/1.0.0 of dependent boo/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end boo/1.0.0 + trace: collect_build_postponed (1): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured 1.0.0 + !boo configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop foo boo + } + + : after-negotiation + : + { + $clone_cfg; + + $* foo biz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add foo/1.0.0 + trace: collect_build: add biz/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: create {foo | libfoo->{foo/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin biz/1.0.0 + %.* + trace: collect_build: add boo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency boo/1.0.0 of dependent biz/1.0.0 + trace: postponed_configurations::add: create {biz | boo->{biz/1,1}} + trace: collect_build_prerequisites: postpone biz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {foo | libfoo->{foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {foo | libfoo->{foo/1,1}}! + trace: collect_build_postponed (2): begin {biz | boo->{biz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {biz | boo->{biz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 0 + info: dependency alternative: libbar + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent biz/1.0.0 + trace: collect_build_prerequisites: resume biz/1.0.0 + trace: collect_build_prerequisites: end biz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {biz | boo->{biz/1,1}}! + trace: collect_build_postponed (2): index 1 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: alt-postpone dependent boo/1.0.0 since max index is reached: 1 + info: dependency alternative: libfoo + { + require + { + config.libfoo.extras = true + } + } + trace: collect_build_prerequisites: postpone boo/1.0.0 + trace: collect_build_postponed (2): index 2 collect alt-postponed boo/1.0.0 + trace: collect_build_prerequisites: resume boo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent boo/1.0.0 + trace: postponed_configurations::add: add {boo 1,2: libfoo} to {foo | libfoo->{foo/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent boo/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libfoo/1.0.0 of dependent boo/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end boo/1.0.0 + trace: collect_build_postponed (2): end {biz | boo->{biz/1,1}} + trace: collect_build_postponed (1): end {foo | libfoo->{foo/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !foo configured 1.0.0 + libfoo configured 1.0.0 + !biz configured 1.0.0 + boo configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop foo biz + } + } + + : bogus-postponment + : + { + $clone_cfg; + + # Dependencies: + # + # tax: depends: libbar(c) + # depends: libfoo + # + # toz/0.1.0: + # + # toz/1.0.0: depends: libbaz(c) + # depends: libfoo(c) + # depends: libbar(c) + # + # tez: depends: libbox(c) + # depends: toz == 0.1.0 (c) + # depends: libbar(c) + # + $* tax toz tez 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add toz/1.0.0 + trace: collect_build: add tez/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin toz/1.0.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent toz/1.0.0 + trace: postponed_configurations::add: create {toz | libbaz->{toz/1,1}} + trace: collect_build_prerequisites: postpone toz/1.0.0 + trace: collect_build_prerequisites: begin tez/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | libbox->{tez/1,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {toz | libbaz->{toz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {toz | libbaz->{toz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbaz/1.0.0 + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent toz/1.0.0 + trace: collect_build_prerequisites: resume toz/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent toz/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add toz/1.0.0 + trace: collect_build: add tez/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin toz/1.0.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent toz/1.0.0 + trace: postponed_configurations::add: create {toz | libbaz->{toz/1,1}} + trace: collect_build_prerequisites: postpone toz/1.0.0 + trace: collect_build_prerequisites: begin tez/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | libbox->{tez/1,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {toz | libbaz->{toz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {toz | libbaz->{toz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbaz/1.0.0 + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent toz/1.0.0 + trace: collect_build_prerequisites: resume toz/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent toz/1.0.0 + trace: postponed_configurations::add: create {toz | libfoo->{toz/2,1}} + trace: collect_build_prerequisites: postpone toz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {toz | libbaz->{toz/1,1}}! + trace: collect_build_postponed (3): begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tez/1.0.0 + trace: collect_build_prerequisites: resume tez/1.0.0 + %.* + trace: collect_build: pick toz/0.1.0 over toz/1.0.0 + trace: collect_build: toz/1.0.0 package version needs to be replaced with toz/0.1.0 + trace: pkg_build: collection failed due to package version replacement, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: apply version replacement for toz/1.0.0 + trace: collect_build: replacement: toz/0.1.0 + trace: collect_build: add toz/0.1.0 + trace: collect_build: add tez/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin toz/0.1.0 + trace: collect_build_prerequisites: end toz/0.1.0 + trace: collect_build_prerequisites: begin tez/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | libbox->{tez/1,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tez/1.0.0 + trace: collect_build_prerequisites: resume tez/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency toz/0.1.0 of dependent tez/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (toz), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: apply version replacement for toz/1.0.0 + trace: collect_build: replacement: toz/0.1.0 + trace: collect_build: add toz/0.1.0 + trace: collect_build: add tez/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: pkg_build: dep-postpone user-specified toz + trace: collect_build_prerequisites: begin tez/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | libbox->{tez/1,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tez/1.0.0 + trace: collect_build_prerequisites: resume tez/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency toz/0.1.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | toz->{tez/2,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tez | libbox->{tez/1,1}}! + trace: collect_build_postponed (3): begin {tez | toz->{tez/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tez | toz->{tez/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin toz/0.1.0 + trace: collect_build_prerequisites: end toz/0.1.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tez/1.0.0 + trace: collect_build_prerequisites: resume tez/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: add {tez 3,1: libbar} to {tax | libbar->{tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tez/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent tez/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end tez/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tez | toz->{tez/2,1}}! + trace: collect_build_postponed (3): erase bogus postponement libfoo + trace: collect_build_postponed (3): bogus postponements erased, throwing + trace: pkg_build: collection failed due to bogus dependency collection postponement cancellation, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: apply version replacement for toz/1.0.0 + trace: collect_build: replacement: toz/0.1.0 + trace: collect_build: add toz/0.1.0 + trace: collect_build: add tez/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: pkg_build: dep-postpone user-specified toz + trace: collect_build_prerequisites: begin tez/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | libbox->{tez/1,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tez | libbox->{tez/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tez/1.0.0 + trace: collect_build_prerequisites: resume tez/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency toz/0.1.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: create {tez | toz->{tez/2,1}} + trace: collect_build_prerequisites: postpone tez/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tez | libbox->{tez/1,1}}! + trace: collect_build_postponed (3): begin {tez | toz->{tez/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tez | toz->{tez/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin toz/0.1.0 + trace: collect_build_prerequisites: end toz/0.1.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tez/1.0.0 + trace: collect_build_prerequisites: resume tez/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tez/1.0.0 + trace: postponed_configurations::add: add {tez 3,1: libbar} to {tax | libbar->{tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tez/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent tez/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end tez/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tez | toz->{tez/2,1}}! + trace: collect_build_postponed (3): end {tez | toz->{tez/2,1}} + trace: collect_build_postponed (2): end {tez | libbox->{tez/1,1}} + trace: collect_build_postponed (1): end {tax | libbar->{tax/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !toz configured 0.1.0 available 1.0.0 0.2.0 + !tax configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !tez configured 1.0.0 + libbar configured 1.0.0 + libbox configured 1.0.0 + !toz configured 0.1.0 available 1.0.0 0.2.0 + EOO + + $pkg_drop tax toz tez + } + } + + : cycle + : + { + +$clone_cfg + + : direct + : + { + +$clone_cfg + + : args-tex-tix + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tix: depends: libbar(c) + # depends: tex(c) + # + $* tex tix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: add {tix 1,1: libbar} to {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex tix | libbar->{tex/1,1 tix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tex tix | libbar->{tex/1,1 tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (tex), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add tix/1.0.0 + trace: pkg_build: dep-postpone user-specified tex + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | libbar->{tix/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | tex->{tix/2,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix | libbar->{tix/1,1}}! + trace: collect_build_postponed (2): begin {tix | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tix | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 1,1: libbar} to {tix | libbar->{tix/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tex/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent tex/1.0.0 is already (being) recursively collected, skipping + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tix | tex->{tix/2,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tix | tex->{tix/2,1}} + trace: collect_build_postponed (1): end {tix | libbar->{tix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !tix configured 1.0.0 + libbar configured 1.0.0 + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop tex tix + } + + : args-tix + : + : As above but with the different command-line arguments which results + : in the different cluster list at the moment of the cycle detection. + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tix: depends: libbar(c) + # depends: tex(c) + # + $* tix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | libbar->{tix/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | tex->{tix/2,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix | libbar->{tix/1,1}}! + trace: collect_build_postponed (2): begin {tix | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tix | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 1,1: libbar} to {tix | libbar->{tix/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tex/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent tex/1.0.0 is already (being) recursively collected, skipping + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tix | tex->{tix/2,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tix | tex->{tix/2,1}} + trace: collect_build_postponed (1): end {tix | libbar->{tix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tix configured 1.0.0 + libbar configured 1.0.0 + tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop tix + } + + : args-tex-tix-tux + : + : Here tux requires tix/0.1.0 which has no dependencies. + : + { + $clone_cfg; + + $* tex tix tux 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add tix/1.0.0 + trace: collect_build: add tux/1.0.0 + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: add {tix 1,1: libbar} to {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_prerequisites: begin tux/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tux/1.0.0 + trace: postponed_configurations::add: create {tux | libbox->{tux/1,1}} + trace: collect_build_prerequisites: postpone tux/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex tix | libbar->{tex/1,1 tix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tex tix | libbar->{tex/1,1 tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (tex), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add tix/1.0.0 + trace: collect_build: add tux/1.0.0 + trace: pkg_build: dep-postpone user-specified tex + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | libbar->{tix/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_prerequisites: begin tux/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tux/1.0.0 + trace: postponed_configurations::add: create {tux | libbox->{tux/1,1}} + trace: collect_build_prerequisites: postpone tux/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | tex->{tix/2,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix | libbar->{tix/1,1}}! + trace: collect_build_postponed (2): begin {tux | libbox->{tux/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tux | libbox->{tux/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tux/1.0.0 + trace: collect_build_prerequisites: resume tux/1.0.0 + %.* + trace: collect_build: pick tix/0.1.0 over tix/1.0.0 + trace: collect_build: tix/1.0.0 package version needs to be replaced with tix/0.1.0 + trace: pkg_build: collection failed due to package version replacement, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: apply version replacement for tix/1.0.0 + trace: collect_build: replacement: tix/0.1.0 + trace: collect_build: add tix/0.1.0 + trace: collect_build: add tux/1.0.0 + trace: pkg_build: dep-postpone user-specified tex + trace: collect_build_prerequisites: begin tix/0.1.0 + trace: collect_build_prerequisites: end tix/0.1.0 + trace: collect_build_prerequisites: begin tux/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tux/1.0.0 + trace: postponed_configurations::add: create {tux | libbox->{tux/1,1}} + trace: collect_build_prerequisites: postpone tux/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tux | libbox->{tux/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tux | libbox->{tux/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tux/1.0.0 + trace: collect_build_prerequisites: resume tux/1.0.0 + %.* + trace: collect_build_prerequisites: no cfg-clause for dependency tix/0.1.0 of dependent tux/1.0.0 + trace: collect_build_prerequisites: end tux/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tux | libbox->{tux/1,1}}! + trace: collect_build_postponed (1): erase bogus postponement tex + trace: collect_build_postponed (1): bogus postponements erased, throwing + trace: pkg_build: collection failed due to bogus dependency collection postponement cancellation, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: apply version replacement for tix/1.0.0 + trace: collect_build: replacement: tix/0.1.0 + trace: collect_build: add tix/0.1.0 + trace: collect_build: add tux/1.0.0 + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_prerequisites: begin tix/0.1.0 + trace: collect_build_prerequisites: end tix/0.1.0 + trace: collect_build_prerequisites: begin tux/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent tux/1.0.0 + trace: postponed_configurations::add: create {tux | libbox->{tux/1,1}} + trace: collect_build_prerequisites: postpone tux/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tex | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tex | libbar->{tex/1,1}}! + trace: collect_build_postponed (2): begin {tux | libbox->{tux/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tux | libbox->{tux/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tux/1.0.0 + trace: collect_build_prerequisites: resume tux/1.0.0 + %.* + trace: collect_build_prerequisites: no cfg-clause for dependency tix/0.1.0 of dependent tux/1.0.0 + trace: collect_build_prerequisites: end tux/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tux | libbox->{tux/1,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tux | libbox->{tux/1,1}} + trace: collect_build_postponed (1): end {tex | libbar->{tex/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !tix configured 0.1.0 available 1.0.0 + !tux configured 1.0.0 + libbox configured 1.0.0 + !tix configured 0.1.0 available 1.0.0 + EOO + + $pkg_drop tex tix tux + } + + : args-tex-tiz + : + : Note that tiz is a correct version of tix, which fixes the + : configuration cycle. + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tiz: depends: tex(c) + # depends: libbar(c) + # + $* tex tiz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add tiz/1.0.0 + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_prerequisites: begin tiz/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency tex/1.0.0 of dependent tiz/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (tex), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add tiz/1.0.0 + trace: pkg_build: dep-postpone user-specified tex + trace: collect_build_prerequisites: begin tiz/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/1.0.0 of dependent tiz/1.0.0 + trace: postponed_configurations::add: create {tiz | tex->{tiz/1,1}} + trace: collect_build_prerequisites: postpone tiz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tiz | tex->{tiz/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tiz | tex->{tiz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tiz/1.0.0 + trace: collect_build_prerequisites: resume tiz/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tiz/1.0.0 + trace: postponed_configurations::add: add {tiz 2,1: libbar} to {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tiz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tiz | tex->{tiz/1,1}}! + trace: collect_build_postponed (2): begin {tex tiz | libbar->{tex/1,1 tiz/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tex tiz | libbar->{tex/1,1 tiz/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tiz/1.0.0 + trace: collect_build_prerequisites: resume tiz/1.0.0 + trace: collect_build_prerequisites: end tiz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex tiz | libbar->{tex/1,1 tiz/2,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tex tiz | libbar->{tex/1,1 tiz/2,1}} + trace: collect_build_postponed (1): end {tiz | tex->{tiz/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !tiz configured 1.0.0 + libbar configured 1.0.0 + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop tex tiz + } + + : args-tiz + : + : Note that tiz is a correct version of tix, which fixes the + : configuration cycle. + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tiz: depends: tex(c) + # depends: libbar(c) + # + $* tiz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tiz/1.0.0 + trace: collect_build_prerequisites: begin tiz/1.0.0 + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency tex/1.0.0 of dependent tiz/1.0.0 + trace: postponed_configurations::add: create {tiz | tex->{tiz/1,1}} + trace: collect_build_prerequisites: postpone tiz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tiz | tex->{tiz/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tiz | tex->{tiz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tiz/1.0.0 + trace: collect_build_prerequisites: resume tiz/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tiz/1.0.0 + trace: postponed_configurations::add: add {tiz 2,1: libbar} to {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tiz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tiz | tex->{tiz/1,1}}! + trace: collect_build_postponed (2): begin {tex tiz | libbar->{tex/1,1 tiz/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tex tiz | libbar->{tex/1,1 tiz/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tiz/1.0.0 + trace: collect_build_prerequisites: resume tiz/1.0.0 + trace: collect_build_prerequisites: end tiz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex tiz | libbar->{tex/1,1 tiz/2,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tex tiz | libbar->{tex/1,1 tiz/2,1}} + trace: collect_build_postponed (1): end {tiz | tex->{tiz/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tiz configured 1.0.0 + libbar configured 1.0.0 + tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop tiz + } + + : depends-depends-conflict + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # toz: depends: libfoo(c) + # depends: libbar(c) + # + $* tex toz/0.2.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build: add toz/0.2.0 + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_prerequisites: begin toz/0.2.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent toz/0.2.0 + trace: postponed_configurations::add: create {toz | libfoo->{toz/1,1}} + trace: collect_build_prerequisites: postpone toz/0.2.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tex | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tex | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 2,1: libfoo} to {toz | libfoo->{toz/1,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tex | libbar->{tex/1,1}}! + trace: collect_build_postponed (2): begin {tex toz | libfoo->{tex/2,1 toz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tex toz | libfoo->{tex/2,1 toz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent toz/0.2.0 + trace: collect_build_prerequisites: resume toz/0.2.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent toz/0.2.0 + trace: postponed_configurations::add: add {toz 2,1: libbar} to {tex | libbar->{tex/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent toz/0.2.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent toz/0.2.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end toz/0.2.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex toz | libfoo->{tex/2,1 toz/1,1}}! + trace: collect_build_postponed (2): end {tex toz | libfoo->{tex/2,1 toz/1,1}} + trace: collect_build_postponed (1): end {tex | libbar->{tex/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !toz configured !0.2.0 available 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop tex toz + } + + : package-depends-conflict + : + { + $clone_cfg; + + # Dependencies: + # + # baz: depends: {libbar libfoo} (c) + # + # bac: depends: libbar(c) + # depends: libbaz(c) + # depends: libfoo(c) + # + # bat: depends: libbaz(c) + # + $* baz bac bat 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add baz/1.0.0 + trace: collect_build: add bac/1.0.0 + trace: collect_build: add bat/1.0.0 + trace: collect_build_prerequisites: begin baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent baz/1.0.0 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent baz/1.0.0 + trace: postponed_configurations::add: create {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone baz/1.0.0 + trace: collect_build_prerequisites: begin bac/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bac/1.0.0 + trace: postponed_configurations::add: add {bac 1,1: libbar} to {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone bac/1.0.0 + trace: collect_build_prerequisites: begin bat/1.0.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bat/1.0.0 + trace: postponed_configurations::add: create {bat | libbaz->{bat/1,1}} + trace: collect_build_prerequisites: postpone bat/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bac baz | libbar->{bac/1,1 baz/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {bac baz | libbar->{bac/1,1 baz/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bac/1.0.0 + trace: collect_build_prerequisites: resume bac/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bac/1.0.0 + trace: postponed_configurations::add: add {bac 2,1: libbaz} to {bat | libbaz->{bat/1,1}} + trace: collect_build_prerequisites: postpone bac/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bac baz | libbar->{bac/1,1 baz/1,1} libfoo->{baz/1,1}}! + trace: collect_build_postponed (2): begin {bac bat | libbaz->{bac/2,1 bat/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {bac bat | libbaz->{bac/2,1 bat/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbaz/1.0.0 + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bac/1.0.0 + trace: collect_build_prerequisites: resume bac/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent bac/1.0.0 + trace: postponed_configurations::add: add {bac 3,1: libfoo} to {bac baz | libbar->{bac/1,1 baz/1,1} libfoo->{baz/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bac/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libfoo/1.0.0 of dependent bac/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bac/1.0.0 + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bat/1.0.0 + trace: collect_build_prerequisites: resume bat/1.0.0 + trace: collect_build_prerequisites: end bat/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {bac bat | libbaz->{bac/2,1 bat/1,1}}! + trace: collect_build_postponed (2): end {bac bat | libbaz->{bac/2,1 bat/1,1}} + trace: collect_build_postponed (1): end {bac baz | libbar->{bac/1,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !bac configured 1.0.0 + libbar configured 1.0.0 + libbaz configured 1.0.0 + libfoo configured 1.0.0 + !bat configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $pkg_drop baz bac bat + } + + : existing + : + { + +$clone_cfg + + : dependency-new-downgrade + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tix: depends: libbar(c) + # depends: tex(c) + # + $* tex 2>!; + + # Build new dependency of an existing dependent. + # + $* tix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | libbar->{tix/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix | libbar->{tix/1,1}} + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: reeval tex/1.0.0 + %.* + trace: postponed_configurations::add: add {tex^ 1,1: libbar} to {tix | libbar->{tix/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent tex/1.0.0 results in {tex^ tix | libbar->{tex/1,1 tix/1,1}} + trace: collect_build_prerequisites: re-evaluated tex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tex^ tix | libbar->{tex/1,1 tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (tex), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: begin tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | libbar->{tix/1,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): skip dep-postponed existing dependent tex of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {tix | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build: add tex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency tex/1.0.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix | tex->{tix/2,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix | libbar->{tix/1,1}}! + trace: collect_build_postponed (2): begin {tix | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {tix | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: add {tex 1,1: libbar} to {tix | libbar->{tix/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tex/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent tex/1.0.0 is already (being) recursively collected, skipping + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/1.0.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tix | tex->{tix/2,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/1.0.0 + trace: collect_build_prerequisites: resume tex/1.0.0 + trace: collect_build_prerequisites: end tex/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tix | tex->{tix/2,1}} + trace: collect_build_postponed (1): end {tix | libbar->{tix/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !tix configured 1.0.0 + libbar configured 1.0.0 + !tex configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + # Downgrade the existing dependency of an existing dependent. + # + $* tex/0.3.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/0.3.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/0.3.0 of existing dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: reeval tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + %.* + trace: collect_build: pick tex/0.3.0 over tex/1.0.0 + trace: postponed_configurations::add: add {tix^ 2,1: tex} to {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent tix/1.0.0 results in {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluated tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/0.3.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/0.3.0 + trace: postponed_configurations::add: create {tex | libbar->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/0.3.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix^ | tex->{tix/2,1}}! + trace: collect_build_postponed (2): begin {tex | libbar->{tex/1,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tex of dependency libbar + trace: collect_build_postponed (2): re-evaluate existing dependents for {tex | libbar->{tex/1,1}} + trace: collect_build_postponed (2): cannot re-evaluate dependent tix to dependency index 1 due to greater dependency index 2 in {tix^ | tex->{tix/2,1}}!, throwing postpone_position + trace: pkg_build: collection failed due to earlier dependency position, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tex/0.3.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/0.3.0 of existing dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (1): pos-postpone existing dependent tix re-evaluation to dependency index 2 due to recorded index 1, skipping {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): postpone cfg-negotiation of {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): non-negotiated clusters left and non-replace postponed positions are present, overriding first encountered non-replace position to replace + trace: collect_build_postponed (1): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (1): pos-postpone existing dependent tix re-evaluation to dependency index 2 due to recorded index 1, skipping {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): replace dependency at index 2 of existing dependent tix/1.0.0 with dependency libbar/1.0.0 at index 1 + trace: collect_build: add libbar/1.0.0 + trace: postponed_configurations::add: create {tix^ | libbar->{tix/1,1}} + trace: collect_build_postponed (0): postpone cfg-negotiation of {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (1): begin {tix^ | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): skip being built existing dependent tex of dependency libbar + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | libbar->{tix/1,1}} + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: reeval tix/1.0.0 + %.* + trace: postponed_configurations::add: add {tix^ 1,1: libbar} to {tix^ | libbar->{tix/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent tix/1.0.0 results in {tix^ | libbar->{tix/1,1}} + trace: collect_build_prerequisites: re-evaluated tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tix^ | libbar->{tix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + %.* + trace: collect_build: pick tex/0.3.0 over tex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency tex/0.3.0 of dependent tix/1.0.0 + trace: postponed_configurations::add: add {tix 2,1: tex} to {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: postpone tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix^ | libbar->{tix/1,1}}! + trace: collect_build_postponed (2): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tix of dependency tex + trace: collect_build_postponed (2): cfg-negotiate begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/0.3.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tex/0.3.0 + trace: postponed_configurations::add: add {tex 1,1: libbar} to {tix^ | libbar->{tix/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent tex/0.3.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent tex/0.3.0 is already (being) recursively collected, skipping + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/0.3.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/0.3.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {tix^ | tex->{tix/2,1}}! + trace: collect_build_postponed (3): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (3): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent tex/0.3.0 + trace: collect_build_prerequisites: resume tex/0.3.0 + trace: collect_build_prerequisites: end tex/0.3.0 + trace: collect_build_postponed (3): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (3): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (2): end {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (1): end {tix^ | libbar->{tix/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_drop tex tix + } + + : dependency-downgrade + : + : Note that here we also specify the existing dependent tix on the + : command line to make sure that its noop recursive collection + : doesn't prevent it from being properly re-evaluated afterwords. + : + : Also note that tex/0.1.0 doesn't depend on libbar. + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tix: depends: libbar(c) + # depends: tex(c) + # + $* tex tix 2>!; + + $* tix tex/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build: add tex/0.1.0 + trace: collect_build_prerequisites: skip configured tix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/0.1.0 of existing dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: reeval tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + %.* + trace: collect_build: pick tex/0.1.0 over tex/1.0.0 + trace: postponed_configurations::add: add {tix^ 2,1: tex} to {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent tix/1.0.0 results in {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluated tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/0.1.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/0.1.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix^ | tex->{tix/2,1}}! + trace: collect_build_postponed (2): begin {tex | libfoo->{tex/1,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (2): cfg-negotiate begin {tex | libfoo->{tex/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/0.1.0 + trace: collect_build_prerequisites: resume tex/0.1.0 + trace: collect_build_prerequisites: end tex/0.1.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex | libfoo->{tex/1,1}}! + trace: collect_build_postponed (2): end {tex | libfoo->{tex/1,1}} + trace: collect_build_postponed (1): end {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_drop tex tix + } + + : dependency-downgrade-unhold + : + : As above but the dependency is also unheld. + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tix: depends: libbar(c) + # depends: tex(c) + # + $* tex tix 2>!; + + $* tix ?tex/0.1.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: skip configured tix/1.0.0 + trace: execute_plan: simulate: yes + %.* + trace: evaluate_dependency: tex/1.0.0: update to tex/0.1.0 + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/0.1.0 of existing dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: reeval tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + %.* + trace: collect_build: pick tex/0.1.0 over tex/1.0.0 + trace: postponed_configurations::add: add {tix^ 2,1: tex} to {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent tix/1.0.0 results in {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluated tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/0.1.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/0.1.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/1,1}} + trace: collect_build_prerequisites: postpone tex/0.1.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix^ | tex->{tix/2,1}}! + trace: collect_build_postponed (2): begin {tex | libfoo->{tex/1,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (2): cfg-negotiate begin {tex | libfoo->{tex/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/0.1.0 + trace: collect_build_prerequisites: resume tex/0.1.0 + trace: collect_build_prerequisites: end tex/0.1.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex | libfoo->{tex/1,1}}! + trace: collect_build_postponed (2): end {tex | libfoo->{tex/1,1}} + trace: collect_build_postponed (1): end {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_drop tix + } + + : dependency-downgrade-unhold-premature + : + : As above but the dependency (tex) depends on libbar without + : configuration clause. + : + { + $clone_cfg; + + # Dependencies: + # + # tex: depends: libbar(c) + # depends: libfoo(c) + # + # tix: depends: libbar(c) + # depends: tex(c) + # + $* tex tix 2>!; + + $* tix ?tex/0.2.0 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tix/1.0.0 + trace: collect_build_prerequisites: skip configured tix/1.0.0 + trace: execute_plan: simulate: yes + %.* + trace: evaluate_dependency: tex/1.0.0: update to tex/0.2.0 + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: collect_build_prerequisites: cfg-postpone dependency tex/0.2.0 of existing dependent tix/1.0.0 + trace: postponed_configurations::add: create {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: reeval tix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + %.* + trace: collect_build: pick tex/0.2.0 over tex/1.0.0 + trace: postponed_configurations::add: add {tix^ 2,1: tex} to {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluating dependent tix/1.0.0 results in {tix^ | tex->{tix/2,1}} + trace: collect_build_prerequisites: re-evaluated tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {tix^ | tex->{tix/2,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin tex/0.2.0 + %.* + trace: collect_build_prerequisites: no cfg-clause for dependency libbar/1.0.0 of dependent tex/0.2.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent tex/0.2.0 + trace: postponed_configurations::add: create {tex | libfoo->{tex/2,1}} + trace: collect_build_prerequisites: postpone tex/0.2.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tix/1.0.0 + trace: collect_build_prerequisites: resume tix/1.0.0 + trace: collect_build_prerequisites: end tix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tix^ | tex->{tix/2,1}}! + trace: collect_build_postponed (2): begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (2): skip being built existing dependent tex of dependency libfoo + trace: collect_build_postponed (2): cfg-negotiate begin {tex | libfoo->{tex/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent tex/0.2.0 + trace: collect_build_prerequisites: resume tex/0.2.0 + trace: collect_build_prerequisites: end tex/0.2.0 + trace: collect_build_postponed (2): cfg-negotiate end {tex | libfoo->{tex/2,1}}! + trace: collect_build_postponed (2): end {tex | libfoo->{tex/2,1}} + trace: collect_build_postponed (1): end {tix^ | tex->{tix/2,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_drop tix + } + } + } + + : indirect + : + { + +$clone_cfg + + : args-tax-dex-dix + : + { + $clone_cfg; + + # Dependencies: + # + # tax: depends: libbar(c) + # depends: libfoo + # + # bar: depends: libbar(c) + # + # dex: depends: bar(c) + # depends: libfoo(c) + # + # dox: dex(c) + # + # dix: depends: libbar(c) + # depends: libbox(c) # causes postponment and initial cluster finished negotiating + # depends: dox(c) + # + $* tax dex dix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add dix/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_prerequisites: begin dix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: add {dix 1,1: libbar} to {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dix tax | libbar->{dix/1,1 tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {dix tax | libbar->{dix/1,1 tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbox->{dix/2,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dix tax | libbar->{dix/1,1 tax/1,1}}! + trace: collect_build_postponed (2): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {dix tax | libbar->{dix/1,1 tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add dix/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_prerequisites: begin dix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: add {dix 1,1: libbar} to {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dix tax | libbar->{dix/1,1 tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {dix tax | libbar->{dix/1,1 tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbox->{dix/2,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dix tax | libbar->{dix/1,1 tax/1,1}}! + trace: collect_build_postponed (2): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {dix tax | libbar->{dix/1,1 tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (3): begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | dox->{dix/3,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {dix | libbox->{dix/2,1}}! + trace: collect_build_postponed (4): begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {dex | libfoo->{dex/2,1}}! + trace: collect_build_postponed (5): begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (5): cfg-negotiate begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (dex), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add dix/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: pkg_build: dep-postpone user-specified dex + trace: collect_build_prerequisites: begin dix/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: add {dix 1,1: libbar} to {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dix tax | libbar->{dix/1,1 tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {dix tax | libbar->{dix/1,1 tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbox->{dix/2,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dix tax | libbar->{dix/1,1 tax/1,1}}! + trace: collect_build_postponed (2): begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | dox->{dix/3,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dix | libbox->{dix/2,1}}! + trace: collect_build_postponed (3): begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 + trace: postponed_configurations::add: create {dox | dex->{dox/1,1}} + trace: collect_build_prerequisites: postpone dox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + trace: collect_build_prerequisites: end dix/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {dix | dox->{dix/3,1}}! + trace: collect_build_postponed (4): begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent dox/1.0.0 + trace: collect_build_prerequisites: resume dox/1.0.0 + trace: collect_build_prerequisites: end dox/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {dox | dex->{dox/1,1}}! + trace: collect_build_postponed (5): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {dix tax | libbar->{dix/1,1 tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (5): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (5): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (6): begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): cfg-negotiate begin {dex | libfoo->{dex/2,1}} + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (6): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (6): cfg-negotiate end {dex | libfoo->{dex/2,1}}! + trace: collect_build_postponed (6): end {dex | libfoo->{dex/2,1}} + trace: collect_build_postponed (5): end {dex | bar->{dex/1,1}} + trace: collect_build_postponed (4): end {dox | dex->{dox/1,1}} + trace: collect_build_postponed (3): end {dix | dox->{dix/3,1}} + trace: collect_build_postponed (2): end {dix | libbox->{dix/2,1}} + trace: collect_build_postponed (1): end {dix tax | libbar->{dix/1,1 tax/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tax configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !dix configured 1.0.0 + dox configured 1.0.0 + !dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + libbar configured 1.0.0 + libbox configured 1.0.0 + EOO + + $pkg_drop tax dex dix --drop-dependent + } + + : args-dix + : + : As above but with the different command-line arguments which results + : in the different cluster list at the moment of the cycle detection. + : + { + $clone_cfg; + + # Dependencies: + # + # bar: depends: libbar(c) + # + # dex: depends: bar(c) + # depends: libfoo(c) + # + # dox: dex(c) + # + # dix: depends: libbar(c) + # depends: libbox(c) # causes postponment and initial cluster finished negotiating + # depends: dox(c) + # + $* dix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add dix/1.0.0 + trace: collect_build_prerequisites: begin dix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbar->{dix/1,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dix | libbar->{dix/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {dix | libbar->{dix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbox->{dix/2,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dix | libbar->{dix/1,1}}! + trace: collect_build_postponed (2): begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | dox->{dix/3,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dix | libbox->{dix/2,1}}! + trace: collect_build_postponed (3): begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build: add dex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 + trace: postponed_configurations::add: create {dox | dex->{dox/1,1}} + trace: collect_build_prerequisites: postpone dox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + trace: collect_build_prerequisites: end dix/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {dix | dox->{dix/3,1}}! + trace: collect_build_postponed (4): begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent dox/1.0.0 + trace: collect_build_prerequisites: resume dox/1.0.0 + trace: collect_build_prerequisites: end dox/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {dox | dex->{dox/1,1}}! + trace: collect_build_postponed (5): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {dix | libbar->{dix/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (5): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (5): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (6): begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): cfg-negotiate begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (6): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (6): cfg-negotiate end {dex | libfoo->{dex/2,1}}! + trace: collect_build_postponed (6): end {dex | libfoo->{dex/2,1}} + trace: collect_build_postponed (5): end {dex | bar->{dex/1,1}} + trace: collect_build_postponed (4): end {dox | dex->{dox/1,1}} + trace: collect_build_postponed (3): end {dix | dox->{dix/3,1}} + trace: collect_build_postponed (2): end {dix | libbox->{dix/2,1}} + trace: collect_build_postponed (1): end {dix | libbar->{dix/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !dix configured 1.0.0 + dox configured 1.0.0 + dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + libbar configured 1.0.0 + libbox configured 1.0.0 + EOO + + $pkg_drop dix + } + + : args-tax-dex-diz + : + : Note that diz is a correct version of dix, which fixes the + : configuration cycle. + : + { + $clone_cfg; + + # Dependencies: + # + # tax: depends: libbar(c) + # depends: libfoo + # + # bar: depends: libbar(c) + # + # dex: depends: bar(c) + # depends: libfoo(c) + # + # dox: dex(c) + # + # diz: depends: dox(c) + # depends: libbox(c) + # depends: libbar(c) + # + $* tax dex diz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add diz/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_prerequisites: begin diz/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | dox->{diz/1,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: no cfg-clause for dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {tax | libbar->{tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (libfoo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add diz/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_prerequisites: begin diz/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | dox->{diz/1,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {tax | libbar->{tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (3): begin {diz | dox->{diz/1,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {diz | dox->{diz/1,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (dex), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add tax/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add diz/1.0.0 + trace: collect_build_prerequisites: begin tax/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent tax/1.0.0 + trace: postponed_configurations::add: create {tax | libbar->{tax/1,1}} + trace: collect_build_prerequisites: postpone tax/1.0.0 + trace: pkg_build: dep-postpone user-specified dex + trace: collect_build_prerequisites: begin diz/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | dox->{diz/1,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {tax | libbar->{tax/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent tax/1.0.0 + trace: collect_build_prerequisites: resume tax/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: dep-postpone dependency libfoo/1.0.0 of dependent tax/1.0.0 + trace: collect_build_prerequisites: end tax/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {tax | libbar->{tax/1,1}}! + trace: collect_build_postponed (2): begin {diz | dox->{diz/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {diz | dox->{diz/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 + trace: postponed_configurations::add: create {dox | dex->{dox/1,1}} + trace: collect_build_prerequisites: postpone dox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent diz/1.0.0 + trace: collect_build_prerequisites: resume diz/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | libbox->{diz/2,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {diz | dox->{diz/1,1}}! + trace: collect_build_postponed (3): begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent dox/1.0.0 + trace: collect_build_prerequisites: resume dox/1.0.0 + trace: collect_build_prerequisites: end dox/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {dox | dex->{dox/1,1}}! + trace: collect_build_postponed (4): begin {diz | libbox->{diz/2,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {diz | libbox->{diz/2,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent diz/1.0.0 + trace: collect_build_prerequisites: resume diz/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: add {diz 3,1: libbar} to {tax | libbar->{tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent diz/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent diz/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end diz/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {diz | libbox->{diz/2,1}}! + trace: collect_build_postponed (5): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {diz tax | libbar->{diz/3,1 tax/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (5): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (5): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (6): begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): cfg-negotiate begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (6): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (6): cfg-negotiate end {dex | libfoo->{dex/2,1}}! + trace: collect_build_postponed (6): end {dex | libfoo->{dex/2,1}} + trace: collect_build_postponed (5): end {dex | bar->{dex/1,1}} + trace: collect_build_postponed (4): end {diz | libbox->{diz/2,1}} + trace: collect_build_postponed (3): end {dox | dex->{dox/1,1}} + trace: collect_build_postponed (2): end {diz | dox->{diz/1,1}} + trace: collect_build_postponed (1): end {tax | libbar->{tax/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !tax configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !diz configured 1.0.0 + dox configured 1.0.0 + !dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + libbar configured 1.0.0 + libbox configured 1.0.0 + EOO + + $pkg_drop tax dex diz --drop-dependent + } + + : args-diz + : + : Note that diz is a correct version of dix, which fixes the + : configuration cycle. + : + { + $clone_cfg; + + # Dependencies: + # + # bar: depends: libbar(c) + # + # dex: depends: bar(c) + # depends: libfoo(c) + # + # dox: dex(c) + # + # diz: depends: dox(c) + # depends: libbox(c) + # depends: libbar(c) + # + $* diz 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add diz/1.0.0 + trace: collect_build_prerequisites: begin diz/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | dox->{diz/1,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {diz | dox->{diz/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {diz | dox->{diz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build: add dex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 + trace: postponed_configurations::add: create {dox | dex->{dox/1,1}} + trace: collect_build_prerequisites: postpone dox/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent diz/1.0.0 + trace: collect_build_prerequisites: resume diz/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | libbox->{diz/2,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {diz | dox->{diz/1,1}}! + trace: collect_build_postponed (2): begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dox/1.0.0 + trace: collect_build_prerequisites: resume dox/1.0.0 + trace: collect_build_prerequisites: end dox/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dox | dex->{dox/1,1}}! + trace: collect_build_postponed (3): begin {diz | libbox->{diz/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {diz | libbox->{diz/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent diz/1.0.0 + trace: collect_build_prerequisites: resume diz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent diz/1.0.0 + trace: postponed_configurations::add: create {diz | libbar->{diz/3,1}} + trace: collect_build_prerequisites: postpone diz/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {diz | libbox->{diz/2,1}}! + trace: collect_build_postponed (4): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {diz | libbar->{diz/3,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (5): begin {bar diz | libbar->{bar/1,1 diz/3,1}} + %.* + trace: collect_build_postponed (5): cfg-negotiate begin {bar diz | libbar->{bar/1,1 diz/3,1}} + %.* + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (5): select cfg-negotiated dependency alternative for dependent bar/1.0.0 + trace: collect_build_prerequisites: resume bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (5): select cfg-negotiated dependency alternative for dependent diz/1.0.0 + trace: collect_build_prerequisites: resume diz/1.0.0 + trace: collect_build_prerequisites: end diz/1.0.0 + trace: collect_build_postponed (5): cfg-negotiate end {bar diz | libbar->{bar/1,1 diz/3,1}}! + trace: collect_build_postponed (6): begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): cfg-negotiate begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (6): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (6): cfg-negotiate end {dex | libfoo->{dex/2,1}}! + trace: collect_build_postponed (6): end {dex | libfoo->{dex/2,1}} + trace: collect_build_postponed (5): end {bar diz | libbar->{bar/1,1 diz/3,1}} + trace: collect_build_postponed (4): end {dex | bar->{dex/1,1}} + trace: collect_build_postponed (3): end {diz | libbox->{diz/2,1}} + trace: collect_build_postponed (2): end {dox | dex->{dox/1,1}} + trace: collect_build_postponed (1): end {diz | dox->{diz/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !diz configured 1.0.0 + dox configured 1.0.0 + dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + libbar configured 1.0.0 + libbox configured 1.0.0 + EOO + + $pkg_drop diz + } + + : depends-depends-conflict + : + { + $clone_cfg; + + # Dependencies: + # + # bar: depends: libbar(c) + # + # dex: depends: bar(c) + # depends: libfoo(c) + # + # bux: depends: libbar(c) + # + # buc: depends: libfoo(c) + # depends: bux(c) + # + $* bar dex buc bux 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add buc/1.0.0 + trace: collect_build: add bux/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (bar), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add buc/1.0.0 + trace: collect_build: add bux/1.0.0 + trace: pkg_build: dep-postpone user-specified bar + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_prerequisites: begin buc/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent buc/1.0.0 + trace: postponed_configurations::add: create {buc | libfoo->{buc/1,1}} + trace: collect_build_prerequisites: postpone buc/1.0.0 + trace: collect_build_prerequisites: begin bux/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bux/1.0.0 + trace: postponed_configurations::add: create {bux | libbar->{bux/1,1}} + trace: collect_build_prerequisites: postpone bux/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {bux | libbar->{bux/1,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: add {dex 2,1: libfoo} to {buc | libfoo->{buc/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (2): begin {buc dex | libfoo->{buc/1,1 dex/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {buc dex | libfoo->{buc/1,1 dex/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent buc/1.0.0 + trace: collect_build_prerequisites: resume buc/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency bux/1.0.0 of dependent buc/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (bux), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add buc/1.0.0 + trace: collect_build: add bux/1.0.0 + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (bar), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build: add dex/1.0.0 + trace: collect_build: add buc/1.0.0 + trace: collect_build: add bux/1.0.0 + trace: pkg_build: dep-postpone user-specified bar + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_prerequisites: begin buc/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent buc/1.0.0 + trace: postponed_configurations::add: create {buc | libfoo->{buc/1,1}} + trace: collect_build_prerequisites: postpone buc/1.0.0 + trace: pkg_build: dep-postpone user-specified bux + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: create {bar | libbar->{bar/1,1}} + trace: collect_build_prerequisites: postpone bar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: add {dex 2,1: libfoo} to {buc | libfoo->{buc/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (2): begin {buc dex | libfoo->{buc/1,1 dex/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {buc dex | libfoo->{buc/1,1 dex/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent buc/1.0.0 + trace: collect_build_prerequisites: resume buc/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency bux/1.0.0 of dependent buc/1.0.0 + trace: postponed_configurations::add: create {buc | bux->{buc/2,1}} + trace: collect_build_prerequisites: postpone buc/1.0.0 + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {buc dex | libfoo->{buc/1,1 dex/2,1}}! + trace: collect_build_postponed (3): begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {bar | libbar->{bar/1,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent bar/1.0.0 + trace: collect_build_prerequisites: resume bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {bar | libbar->{bar/1,1}}! + trace: collect_build_postponed (4): begin {buc | bux->{buc/2,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {buc | bux->{buc/2,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bux/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bux/1.0.0 + trace: postponed_configurations::add: add {bux 1,1: libbar} to {bar | libbar->{bar/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bux/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bux/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bux/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent buc/1.0.0 + trace: collect_build_prerequisites: resume buc/1.0.0 + trace: collect_build_prerequisites: end buc/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {buc | bux->{buc/2,1}}! + trace: collect_build_postponed (4): end {buc | bux->{buc/2,1}} + trace: collect_build_postponed (3): end {bar | libbar->{bar/1,1}} + trace: collect_build_postponed (2): end {buc dex | libfoo->{buc/1,1 dex/2,1}} + trace: collect_build_postponed (1): end {dex | bar->{dex/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + libbar configured 1.0.0 + !dex configured 1.0.0 + !bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !bux configured 1.0.0 + libbar configured 1.0.0 + !buc configured 1.0.0 + !bux configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + EOO + + $pkg_drop bar dex buc bux + } + + : package-depends-conflict + : + { + $clone_cfg; + + # Dependencies: + # + # baz: depends: {libbar libfoo} (c) + # + # bas: depends: libbar(c) + # depends: bus (c) + # + # bus: depends: libbaz(c) + # depends: foo (c) + # + # foo: depends: libfoo(c) + # + # bat: depends: libbaz(c) + # + $* baz bas bus foo bat 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add baz/1.0.0 + trace: collect_build: add bas/1.0.0 + trace: collect_build: add bus/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bat/1.0.0 + trace: collect_build_prerequisites: begin baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent baz/1.0.0 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent baz/1.0.0 + trace: postponed_configurations::add: create {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone baz/1.0.0 + trace: collect_build_prerequisites: begin bas/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bas/1.0.0 + trace: postponed_configurations::add: add {bas 1,1: libbar} to {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone bas/1.0.0 + trace: collect_build_prerequisites: begin bus/1.0.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bus/1.0.0 + trace: postponed_configurations::add: create {bus | libbaz->{bus/1,1}} + trace: collect_build_prerequisites: postpone bus/1.0.0 + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: add {foo 1,1: libfoo} to {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin bat/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bat/1.0.0 + trace: postponed_configurations::add: add {bat 1,1: libbaz} to {bus | libbaz->{bus/1,1}} + trace: collect_build_prerequisites: postpone bat/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bas baz foo | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1 foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {bas baz foo | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1 foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bas/1.0.0 + trace: collect_build_prerequisites: resume bas/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency bus/1.0.0 of dependent bas/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (bus), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add baz/1.0.0 + trace: collect_build: add bas/1.0.0 + trace: collect_build: add bus/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bat/1.0.0 + trace: collect_build_prerequisites: begin baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent baz/1.0.0 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent baz/1.0.0 + trace: postponed_configurations::add: create {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone baz/1.0.0 + trace: collect_build_prerequisites: begin bas/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bas/1.0.0 + trace: postponed_configurations::add: add {bas 1,1: libbar} to {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone bas/1.0.0 + trace: pkg_build: dep-postpone user-specified bus + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: add {foo 1,1: libfoo} to {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone foo/1.0.0 + trace: collect_build_prerequisites: begin bat/1.0.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bat/1.0.0 + trace: postponed_configurations::add: create {bat | libbaz->{bat/1,1}} + trace: collect_build_prerequisites: postpone bat/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bas baz foo | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1 foo/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {bas baz foo | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1 foo/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bas/1.0.0 + trace: collect_build_prerequisites: resume bas/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency bus/1.0.0 of dependent bas/1.0.0 + trace: postponed_configurations::add: create {bas | bus->{bas/2,1}} + trace: collect_build_prerequisites: postpone bas/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent foo/1.0.0 + trace: collect_build_prerequisites: resume foo/1.0.0 + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bas baz foo | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1 foo/1,1}}! + trace: collect_build_postponed (2): begin {bat | libbaz->{bat/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {bat | libbaz->{bat/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbaz/1.0.0 + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bat/1.0.0 + trace: collect_build_prerequisites: resume bat/1.0.0 + trace: collect_build_prerequisites: end bat/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {bat | libbaz->{bat/1,1}}! + trace: collect_build_postponed (3): begin {bas | bus->{bas/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {bas | bus->{bas/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bus/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bus/1.0.0 + trace: postponed_configurations::add: add {bus 1,1: libbaz} to {bat | libbaz->{bat/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bus/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbaz/1.0.0 of dependent bus/1.0.0 is already (being) recursively collected, skipping + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency foo/1.0.0 of dependent bus/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (foo), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add baz/1.0.0 + trace: collect_build: add bas/1.0.0 + trace: collect_build: add bus/1.0.0 + trace: collect_build: add foo/1.0.0 + trace: collect_build: add bat/1.0.0 + trace: collect_build_prerequisites: begin baz/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent baz/1.0.0 + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent baz/1.0.0 + trace: postponed_configurations::add: create {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone baz/1.0.0 + trace: collect_build_prerequisites: begin bas/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bas/1.0.0 + trace: postponed_configurations::add: add {bas 1,1: libbar} to {baz | libbar->{baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_prerequisites: postpone bas/1.0.0 + trace: pkg_build: dep-postpone user-specified bus + trace: pkg_build: dep-postpone user-specified foo + trace: collect_build_prerequisites: begin bat/1.0.0 + %.* + trace: collect_build: add libbaz/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bat/1.0.0 + trace: postponed_configurations::add: create {bat | libbaz->{bat/1,1}} + trace: collect_build_prerequisites: postpone bat/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build_postponed (1): cfg-negotiate begin {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbar/1.0.0 + trace: collect_build_prerequisites: end libbar/1.0.0 + trace: collect_build_prerequisites: begin libfoo/1.0.0 + trace: collect_build_prerequisites: end libfoo/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bas/1.0.0 + trace: collect_build_prerequisites: resume bas/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency bus/1.0.0 of dependent bas/1.0.0 + trace: postponed_configurations::add: create {bas | bus->{bas/2,1}} + trace: collect_build_prerequisites: postpone bas/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent baz/1.0.0 + trace: collect_build_prerequisites: resume baz/1.0.0 + trace: collect_build_prerequisites: end baz/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}}! + trace: collect_build_postponed (2): begin {bat | libbaz->{bat/1,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {bat | libbaz->{bat/1,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbaz/1.0.0 + trace: collect_build_prerequisites: end libbaz/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent bat/1.0.0 + trace: collect_build_prerequisites: resume bat/1.0.0 + trace: collect_build_prerequisites: end bat/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {bat | libbaz->{bat/1,1}}! + trace: collect_build_postponed (3): begin {bas | bus->{bas/2,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {bas | bus->{bas/2,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bus/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbaz/1.0.0 of dependent bus/1.0.0 + trace: postponed_configurations::add: add {bus 1,1: libbaz} to {bat | libbaz->{bat/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bus/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbaz/1.0.0 of dependent bus/1.0.0 is already (being) recursively collected, skipping + %.* + trace: collect_build_prerequisites: cfg-postpone dependency foo/1.0.0 of dependent bus/1.0.0 + trace: postponed_configurations::add: create {bus | foo->{bus/2,1}} + trace: collect_build_prerequisites: postpone bus/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent bas/1.0.0 + trace: collect_build_prerequisites: resume bas/1.0.0 + trace: collect_build_prerequisites: end bas/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {bas | bus->{bas/2,1}}! + trace: collect_build_postponed (4): begin {bus | foo->{bus/2,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {bus | foo->{bus/2,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent foo/1.0.0 + trace: postponed_configurations::add: add {foo 1,1: libfoo} to {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent foo/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libfoo/1.0.0 of dependent foo/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end foo/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent bus/1.0.0 + trace: collect_build_prerequisites: resume bus/1.0.0 + trace: collect_build_prerequisites: end bus/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {bus | foo->{bus/2,1}}! + trace: collect_build_postponed (4): end {bus | foo->{bus/2,1}} + trace: collect_build_postponed (3): end {bas | bus->{bas/2,1}} + trace: collect_build_postponed (2): end {bat | libbaz->{bat/1,1}} + trace: collect_build_postponed (1): end {bas baz | libbar->{bas/1,1 baz/1,1} libfoo->{baz/1,1}} + trace: collect_build_postponed (0): end + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !foo configured 1.0.0 + libfoo configured 1.0.0 + !bus configured 1.0.0 + !foo configured 1.0.0 + libfoo configured 1.0.0 + libbaz configured 1.0.0 + !bas configured 1.0.0 + !bus configured 1.0.0 + !foo configured 1.0.0 + libfoo configured 1.0.0 + libbaz configured 1.0.0 + libbar configured 1.0.0 + !bat configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $pkg_drop baz bas bus foo bat + } + + : existing + : + { + +$clone_cfg + + : negotiate + : + { + $clone_cfg; + + # Dependencies: + # + # bar: depends: libbar(c) + # + # dex: depends: bar(c) + # depends: libfoo(c) + # + # dox: dex(c) + # + # dix: depends: libbar(c) + # depends: libbox(c) # causes postponment and initial cluster finished negotiating + # depends: dox(c) + # + $* dex 2>!; + + $* dix 2>>~%EOE%; + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add dix/1.0.0 + trace: collect_build_prerequisites: begin dix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbar->{dix/1,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dix | libbar->{dix/1,1}} + %.* + trace: collect_build_postponed (1): re-evaluate existing dependents for {dix | libbar->{dix/1,1}} + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: reeval bar/1.0.0 + %.* + trace: postponed_configurations::add: add {bar^ 1,1: libbar} to {dix | libbar->{dix/1,1}} + trace: collect_build_prerequisites: re-evaluating dependent bar/1.0.0 results in {bar^ dix | libbar->{bar/1,1 dix/1,1}} + trace: collect_build_prerequisites: re-evaluated bar/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate begin {bar^ dix | libbar->{bar/1,1 dix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent bar/1.0.0 + trace: collect_build_prerequisites: resume bar/1.0.0 + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbox->{dix/2,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {bar^ dix | libbar->{bar/1,1 dix/1,1}}! + trace: collect_build_postponed (2): begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | dox->{dix/3,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dix | libbox->{dix/2,1}}! + trace: collect_build_postponed (3): begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build: add dex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 + trace: postponed_configurations::add: create {dox | dex->{dox/1,1}} + trace: collect_build_prerequisites: postpone dox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + trace: collect_build_prerequisites: end dix/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {dix | dox->{dix/3,1}}! + trace: collect_build_postponed (4): begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build_prerequisites: cannot cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 (collected prematurely), throwing postpone_dependency + trace: pkg_build: collection failed due to prematurely collected dependency (bar), retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + trace: collect_build: add dix/1.0.0 + trace: collect_build_prerequisites: begin dix/1.0.0 + %.* + trace: collect_build: add libbar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbar->{dix/1,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (0): begin + trace: collect_build_postponed (1): begin {dix | libbar->{dix/1,1}} + %.* + trace: collect_build_postponed (1): skip dep-postponed existing dependent bar of dependency libbar + trace: collect_build_postponed (1): cfg-negotiate begin {dix | libbar->{dix/1,1}} + %.* + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libbar/1.0.0 + trace: collect_build_postponed (1): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (1): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add libbox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libbox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | libbox->{dix/2,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (1): cfg-negotiate end {dix | libbar->{dix/1,1}}! + trace: collect_build_postponed (2): begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): cfg-negotiate begin {dix | libbox->{dix/2,1}} + %.* + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin libbox/1.0.0 + trace: collect_build_prerequisites: end libbox/1.0.0 + trace: collect_build_postponed (2): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (2): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + %.* + trace: collect_build: add dox/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dox/1.0.0 of dependent dix/1.0.0 + trace: postponed_configurations::add: create {dix | dox->{dix/3,1}} + trace: collect_build_prerequisites: postpone dix/1.0.0 + trace: collect_build_postponed (2): cfg-negotiate end {dix | libbox->{dix/2,1}}! + trace: collect_build_postponed (3): begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): cfg-negotiate begin {dix | dox->{dix/3,1}} + %.* + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dox/1.0.0 + %.* + trace: collect_build: add dex/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency dex/1.0.0 of dependent dox/1.0.0 + trace: postponed_configurations::add: create {dox | dex->{dox/1,1}} + trace: collect_build_prerequisites: postpone dox/1.0.0 + trace: collect_build_postponed (3): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (3): select cfg-negotiated dependency alternative for dependent dix/1.0.0 + trace: collect_build_prerequisites: resume dix/1.0.0 + trace: collect_build_prerequisites: end dix/1.0.0 + trace: collect_build_postponed (3): cfg-negotiate end {dix | dox->{dix/3,1}}! + trace: collect_build_postponed (4): begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): cfg-negotiate begin {dox | dex->{dox/1,1}} + %.* + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin dex/1.0.0 + %.* + trace: collect_build: add bar/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency bar/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | bar->{dex/1,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (4): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (4): select cfg-negotiated dependency alternative for dependent dox/1.0.0 + trace: collect_build_prerequisites: resume dox/1.0.0 + trace: collect_build_prerequisites: end dox/1.0.0 + trace: collect_build_postponed (4): cfg-negotiate end {dox | dex->{dox/1,1}}! + trace: collect_build_postponed (5): begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): skip being built existing dependent dex of dependency bar + trace: collect_build_postponed (5): cfg-negotiate begin {dex | bar->{dex/1,1}} + %.* + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: begin bar/1.0.0 + %.* + trace: collect_build_prerequisites: cfg-postpone dependency libbar/1.0.0 of dependent bar/1.0.0 + trace: postponed_configurations::add: add {bar 1,1: libbar} to {dix | libbar->{dix/1,1}}! + trace: collect_build_prerequisites: configuration for cfg-postponed dependencies of dependent bar/1.0.0 is negotiated + trace: collect_build_prerequisites: dependency libbar/1.0.0 of dependent bar/1.0.0 is already (being) recursively collected, skipping + trace: collect_build_prerequisites: end bar/1.0.0 + trace: collect_build_postponed (5): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (5): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + %.* + trace: collect_build: add libfoo/1.0.0 + trace: collect_build_prerequisites: cfg-postpone dependency libfoo/1.0.0 of dependent dex/1.0.0 + trace: postponed_configurations::add: create {dex | libfoo->{dex/2,1}} + trace: collect_build_prerequisites: postpone dex/1.0.0 + trace: collect_build_postponed (5): cfg-negotiate end {dex | bar->{dex/1,1}}! + trace: collect_build_postponed (6): begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): skip being built existing dependent dex of dependency libfoo + trace: collect_build_postponed (6): cfg-negotiate begin {dex | libfoo->{dex/2,1}} + %.* + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependencies + trace: collect_build_prerequisites: skip configured libfoo/1.0.0 + trace: collect_build_postponed (6): recursively collect cfg-negotiated dependents + trace: collect_build_postponed (6): select cfg-negotiated dependency alternative for dependent dex/1.0.0 + trace: collect_build_prerequisites: resume dex/1.0.0 + trace: collect_build_prerequisites: end dex/1.0.0 + trace: collect_build_postponed (6): cfg-negotiate end {dex | libfoo->{dex/2,1}}! + trace: collect_build_postponed (6): end {dex | libfoo->{dex/2,1}} + trace: collect_build_postponed (5): end {dex | bar->{dex/1,1}} + trace: collect_build_postponed (4): end {dox | dex->{dox/1,1}} + trace: collect_build_postponed (3): end {dix | dox->{dix/3,1}} + trace: collect_build_postponed (2): end {dix | libbox->{dix/2,1}} + trace: collect_build_postponed (1): end {dix | libbar->{dix/1,1}} + trace: collect_build_postponed (0): end + %.* + trace: execute_plan: simulate: yes + %.* + EOE + + $pkg_status -r >>EOO; + !dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + !dix configured 1.0.0 + dox configured 1.0.0 + !dex configured 1.0.0 + bar configured 1.0.0 + libbar configured 1.0.0 + libfoo configured 1.0.0 + libbar configured 1.0.0 + libbox configured 1.0.0 + EOO + + # @@ Note that if --drop-dependent is unspecified, the following + # command fails with: + # + # following dependent packages will have to be dropped as well: + # dox (requires dex) + # error: refusing to drop dependent packages with just --yes + # info: specify --drop-dependent to confirm + # + # Feels wrong. + # + $pkg_drop --drop-dependent dex dix + } + } + } + } + + : all-repo-packages + : + : Don't match the tracing but just make sure that pkg-build doesn't crash + : or hang and ends up with an expected packages setup. + : + { + $clone_cfg; + + $* libfoo libbar ?libbaz/0.1.0 ?libbox/0.1.0 \ + foo fox fux fix fex bar baz bac bat bas bus \ + box bax bux bix bex boo biz buz buc tax tex \ + tix tiz toz tez tux dex dix diz dox 2>!; + + $pkg_status -r >>EOO; + !libfoo configured 1.0.0 + !libbar configured 1.0.0 + !bat configured 1.0.0 + libbaz configured !0.1.0 available 1.0.0 + !tix configured 0.1.0 available 1.0.0 + !toz configured 0.1.0 available 1.0.0 0.2.0 + !tux configured 1.0.0 + libbox configured !0.1.0 available 1.0.0 + !tix configured 0.1.0 available 1.0.0 + !bar configured 1.0.0 + !libbar configured 1.0.0 + !bux configured 1.0.0 + !libbar configured 1.0.0 + !bex configured 1.0.0 + !libbar configured 1.0.0 + !boo configured 1.0.0 + !libbar configured 1.0.0 + !biz configured 1.0.0 + !boo configured 1.0.0 + !libbar configured 1.0.0 + !buz configured 1.0.0 + !bux configured 1.0.0 + !libbar configured 1.0.0 + !tez configured 1.0.0 + !libbar configured 1.0.0 + libbox configured !0.1.0 available 1.0.0 + !toz configured 0.1.0 available 1.0.0 0.2.0 + !bix configured 1.0.0 + !bar configured 1.0.0 + !libbar configured 1.0.0 + !bux configured 1.0.0 + !libbar configured 1.0.0 + !libbar configured 1.0.0 + !foo configured 1.0.0 + !libfoo configured 1.0.0 + !fox configured 1.0.0 + !libfoo configured 1.0.0 + !fux configured 1.0.0 + !libfoo configured 1.0.0 + !baz configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !bac configured 1.0.0 + !libbar configured 1.0.0 + libbaz configured !0.1.0 available 1.0.0 + !libfoo configured 1.0.0 + !fix configured 1.0.0 + !foo configured 1.0.0 + !libfoo configured 1.0.0 + !fex configured 1.0.0 + !foo configured 1.0.0 + !libfoo configured 1.0.0 + !libfoo configured 1.0.0 + !bus configured 1.0.0 + !foo configured 1.0.0 + !libfoo configured 1.0.0 + libbaz configured !0.1.0 available 1.0.0 + !bas configured 1.0.0 + !bus configured 1.0.0 + !foo configured 1.0.0 + !libfoo configured 1.0.0 + libbaz configured !0.1.0 available 1.0.0 + !libbar configured 1.0.0 + !box configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !bax configured 1.0.0 + !libbar configured 1.0.0 + libbox configured !0.1.0 available 1.0.0 + !libfoo configured 1.0.0 + !buc configured 1.0.0 + !bux configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !tax configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !tex configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !tiz configured 1.0.0 + !libbar configured 1.0.0 + !tex configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !dex configured 1.0.0 + !bar configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !dox configured 1.0.0 + !dex configured 1.0.0 + !bar configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !dix configured 1.0.0 + !dox configured 1.0.0 + !dex configured 1.0.0 + !bar configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !libbar configured 1.0.0 + libbox configured !0.1.0 available 1.0.0 + !diz configured 1.0.0 + !dox configured 1.0.0 + !dex configured 1.0.0 + !bar configured 1.0.0 + !libbar configured 1.0.0 + !libfoo configured 1.0.0 + !libbar configured 1.0.0 + libbox configured !0.1.0 available 1.0.0 + EOO + + $pkg_drop libfoo libbar foo fox fux fix fex bar \ + baz bac bat bas bus box bax bux bix bex \ + boo biz buz buc tax tex tix tiz toz tez \ + tux dex dix diz dox + } + } + + : configuration-negotiation + : + { + test.arguments += --yes --plan 'build plan:' + + : proj-better-choice + : + { + +$clone_root_cfg && $rep_add $rep/t13a && $rep_fetch + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + config.liba.backend=cli (set by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.backend = cli + EOO + + $pkg_drop bar + } + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by baz) + config.liba.backend=gui (set by baz) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.backend = gui + EOO + + $pkg_drop baz + } + + : bar-baz + : + { + $clone_cfg; + + $* bar baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar, baz) + config.liba.backend=cli (set by bar) + new bar/1.0.0 + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured baz/1.0.0 + %info: .+bar.+ is up to date% + %info: .+baz.+ is up to date% + updated bar/1.0.0 + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.backend = cli + EOO + + $pkg_drop bar baz + } + + : baz-bar + : + { + $clone_cfg; + + $* baz bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar, baz) + config.liba.backend=cli (set by bar) + new baz/1.0.0 + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + configured bar/1.0.0 + %info: .+baz.+ is up to date% + %info: .+bar.+ is up to date% + updated baz/1.0.0 + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + # Would have been cool to end up with gui but looks like we have a map + # in configuration cluster. + # + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.backend = cli + EOO + + $pkg_drop baz bar + } + + : bar-baz-biz + : + { + $clone_cfg; + + $* bar baz biz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar, baz, biz) + config.liba.backend=gui (set by biz) + new bar/1.0.0 + new baz/1.0.0 + new biz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + fetched biz/1.0.0 + unpacked biz/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured baz/1.0.0 + configured biz/1.0.0 + %info: .+bar.+ is up to date% + %info: .+baz.+ is up to date% + %info: .+biz.+ is up to date% + updated bar/1.0.0 + updated baz/1.0.0 + updated biz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + !baz configured 1.0.0 + liba configured 1.0.0 + !biz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.backend = gui + EOO + + $pkg_drop bar baz biz + } + } + + : proj-better-value + : + { + +$clone_root_cfg && $rep_add $rep/t13b && $rep_fetch + + : liba + : + { + $clone_cfg; + + $* liba 2>>~%EOE%; + build plan: + new liba/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + configured liba/1.0.0 + %info: .+liba.+ is up to date% + updated liba/1.0.0 + EOE + + $pkg_status -r >>EOO; + !liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = false + config.liba.buffer = 1024 + EOO + + $pkg_drop liba + } + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + config.liba.buffer=2048 (set by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = false + config.liba.buffer = 2048 + EOO + + $pkg_drop bar + } + + : bar-liba + : + { + $clone_cfg; + + $* bar ?liba +{ config.liba.x=true } 2>>~%EOE%; + build plan: + new liba/1.0.0 + config.liba.x=true (user configuration) + config.liba.buffer=10240 (set by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+liba.+ is up to date% + %info: .+bar.+ is up to date% + updated liba/1.0.0 + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 10240 + EOO + + $pkg_drop bar + } + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by baz) + config.liba.buffer=4096 (set by baz) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = false + config.liba.buffer = 4096 + EOO + + $pkg_drop baz + } + + : baz-liba + : + { + $clone_cfg; + + $* baz ?liba +{ config.liba.x=true } 2>>~%EOE%; + build plan: + new liba/1.0.0 + config.liba.x=true (user configuration) + config.liba.buffer=10240 (set by baz) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + %info: .+liba.+ is up to date% + %info: .+baz.+ is up to date% + updated liba/1.0.0 + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 10240 + EOO + + $pkg_drop baz + } + + : baz-biz + : + { + $clone_cfg; + + $* baz biz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by baz, biz) + %( + config.liba.buffer=10240 (set by baz) + config.liba.x=true (set by biz) + %| + config.liba.x=true (set by biz) + config.liba.buffer=10240 (set by baz) + %) + new baz/1.0.0 + new biz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + fetched biz/1.0.0 + unpacked biz/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + configured biz/1.0.0 + %info: .+baz.+ is up to date% + %info: .+biz.+ is up to date% + updated baz/1.0.0 + updated biz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + !biz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 10240 + EOO + + $pkg_drop baz biz + } + } + + : proj-disable-unused + : + { + +$clone_root_cfg && $rep_add $rep/t13c && $rep_fetch + + : liba + : + { + $clone_cfg; + + $* liba 2>>~%EOE%; + build plan: + new liba/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + configured liba/1.0.0 + %info: .+liba.+ is up to date% + updated liba/1.0.0 + EOE + + $pkg_status -r >>EOO; + !liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + EOO + + $pkg_drop liba + } + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + :1:5: info: false + info: reflect clause: + info $config.liba.x + info: in depends manifest value of package bar + build plan: + new liba/1.0.0 (required by bar) + config.liba.x=false (set by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = false + EOO + + $pkg_drop bar + } + + : bar-baz + : + { + $clone_cfg; + + $* bar baz 2>>~%EOE%; + :1:5: info: true + info: reflect clause: + info $config.liba.x + info: in depends manifest value of package bar + build plan: + new liba/1.0.0 (required by bar, baz) + config.liba.x=true (set by baz) + new bar/1.0.0 + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured baz/1.0.0 + %info: .+bar.+ is up to date% + %info: .+baz.+ is up to date% + updated bar/1.0.0 + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + EOO + + $pkg_drop bar baz + } + + : bar-liba + : + { + $clone_cfg; + + $* bar ?liba +{ config.liba.x=true } 2>>~%EOE%; + :1:5: info: true + info: reflect clause: + info $config.liba.x + info: in depends manifest value of package bar + build plan: + new liba/1.0.0 + config.liba.x=true (user configuration) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+liba.+ is up to date% + %info: .+bar.+ is up to date% + updated liba/1.0.0 + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + EOO + + $pkg_drop bar + } + } + + : proj-use-if-available + : + { + +$clone_root_cfg && $rep_add $rep/t13d && $rep_fetch + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar, baz) + config.liba.x=true (set by baz) + new bar/1.0.0 (required by baz) + config.bar.liba_x=true (set by bar) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + bar configured 1.0.0 + liba configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + EOO + + $pkg_drop baz + } + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new libb/1.0.0 (required by bar) + new bar/1.0.0 + config.bar.liba_x=false (set by bar) + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured libb/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + libb configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = false + EOO + + $pkg_drop bar + } + } + + : proj-bogus-config + : + { + +$clone_root_cfg && $rep_add $rep/t13e && $rep_fetch + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by baz, biz) + config.liba.x=true (set by biz) + new biz/1.0.0 (required by baz) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched biz/1.0.0 + unpacked biz/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured biz/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + biz configured 1.0.0 + liba configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 1024 + EOO + + $pkg_drop baz + } + } + + : proj-bogus-config-cycle + : + { + +$clone_root_cfg && $rep_add $rep/t13f && $rep_fetch + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE% != 0; + error: unable to remove bogus configuration values without causing configuration refinement cycle + info: consider manually specifying one or more of the following variables as user configuration + %( + config.liba.buffer=2048 + config.liba.x=true + %| + config.liba.x=true + config.liba.buffer=2048 + %) + EOE + + $* baz ?liba +{ config.liba.x=true } 2>>~%EOE%; + build plan: + new liba/1.0.0 + config.liba.x=true (user configuration) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + %info: .+liba.+ is up to date% + %info: .+baz.+ is up to date% + updated liba/1.0.0 + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 1024 + EOO + + $pkg_drop baz + } + } + + : proj-detect-unset + : + { + +$clone_root_cfg && $rep_add $rep/t13g && $rep_fetch + + : bar + : + { + $clone_cfg; + + $* bar 2>>EOE != 0 + :1:3: error: undefined dependency configuration variable config.liba.x + info: was config.liba.x set in earlier prefer or require clause? + info: accept condition: ($config.liba.x) + info: in depends manifest value of package bar + EOE + } + + : baz + : + { + $clone_cfg; + + $* baz 2>>EOE != 0 + :1:3: error: undefined dependency configuration variable config.liba.x + info: was config.liba.x set in earlier prefer or require clause? + info: enable condition: ($config.liba.x) + info: in depends manifest value of package baz + info: while satisfying baz/1.0.0 + EOE + } + + : biz + : + { + $clone_cfg; + + $* biz 2>>EOE != 0 + :1:12: error: undefined dependency configuration variable config.liba.x + info: was config.liba.x set in earlier prefer or require clause? + info: prefer clause: + x = (!$config.liba.x) # Error. + info: in depends manifest value of package biz + EOE + } + + : box + : + { + $clone_cfg; + + $* box 2>>EOE != 0 + :1:12: error: undefined dependency configuration variable config.liba.x + info: was config.liba.x set in earlier prefer or require clause? + info: reflect clause: + x = (!$config.liba.x) # Error. + info: in depends manifest value of package box + EOE + } + } + + : proj-cycle + : + { + +$clone_root_cfg && $rep_add $rep/t13h && $rep_fetch + + : bar baz + : + { + $clone_cfg; + + $* bar baz 2>>EOE != 0 + error: unable to negotiate acceptable configuration between dependents baz, bar for dependencies liba + info: configuration before negotiation: + config.liba.buffer=4096 (set by bar) + info: configuration after negotiation: + config.liba.buffer=8192 (set by baz) + EOE + } + } + + : proj-dependency-reflect + : + { + +$clone_root_cfg && $rep_add $rep/t13i && $rep_fetch + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + :1:5: info: yes + info: reflect clause: + info ($config.liba.x ? yes : no) + info: in depends manifest value of package bar + build plan: + new liba/1.0.0 (required by bar) + config.liba.x=true (set by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 1024 + EOO + + $pkg_drop bar + } + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + :1:5: info: 4096 + info: reflect clause: + info $config.liba.buffer + info: in depends manifest value of package baz + build plan: + new liba/1.0.0 (required by baz) + config.liba.buffer=4096 (set by baz) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = false + config.liba.buffer = 4096 + EOO + + $pkg_drop baz + } + } + + : proj-non-sensible + : + { + +$clone_root_cfg && $rep_add $rep/t13j && $rep_fetch + + : bar-baz + : + { + $clone_cfg; + + $* bar baz 2>>/~%EOE% != 0 + error: unable to negotiate sensible configuration for dependency liba + % .+/root.build:5:1: error: buffer must be at least 4096 if feature x is enabled% + info: negotiated configuration: + %( + config.liba.buffer=2048 (set by bar) + config.liba.x=true (set by baz) + %| + config.liba.x=true (set by baz) + config.liba.buffer=2048 (set by bar) + %) + EOE + } + + : biz-baz + : + { + $clone_cfg; + + $* biz baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by baz, biz) + %( + config.liba.buffer=4096 (set by biz) + config.liba.x=true (set by baz) + %| + config.liba.x=true (set by baz) + config.liba.buffer=4096 (set by biz) + %) + new biz/1.0.0 + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched biz/1.0.0 + unpacked biz/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured biz/1.0.0 + configured baz/1.0.0 + %info: .+biz.+ is up to date% + %info: .+baz.+ is up to date% + updated biz/1.0.0 + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !biz configured 1.0.0 + liba configured 1.0.0 + !baz configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.buffer = 4096 + EOO + + $pkg_drop biz baz + } + } + + : proj-unaccept + : + { + +$clone_root_cfg && $rep_add $rep/t13k && $rep_fetch + + : bar-baz + : + { + $clone_cfg; + + $* bar baz 2>>EOE != 0 + error: unable to negotiate acceptable configuration with dependent bar for dependencies liba + info: configuration before negotiation: + config.liba.buffer=8192 (set by baz) + EOE + } + } + + : proj-require-system + : + { + +$clone_root_cfg && $rep_add $rep/t13l && $rep_fetch + + : bar + : + { + +$clone_cfg + + : basic + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + config.liba.x=true (set by bar) + new libb/1.0.0 (required by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured libb/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + libb configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + EOO + + $pkg_drop bar + } + + : sys-liba-1 + : + { + $clone_cfg; + + $* bar '?sys:liba' 2>>~%EOE%; + build plan: + configure sys:liba/* + config.liba.x=true (expected by bar) + new libb/1.0.0 (required by bar) + new bar/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured sys:liba/* + configured libb/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured,system !* available 1.0.0 + libb configured 1.0.0 + EOO + + $pkg_drop bar + } + + : sys-liba-2 + : + { + $clone_cfg; + + $* bar '?sys:liba' +{ config.liba.x=false } 2>>EOE != 0 + error: unable to negotiate acceptable configuration with dependent bar for dependencies liba + info: configuration before negotiation: + config.liba.x=false (user configuration) + EOE + } + + : bar-sys-liba-3 + : + { + $clone_cfg; + + $* bar '?sys:liba' +{ config.liba.x=[null] } 2>>EOE != 0 + error: unable to negotiate acceptable configuration with dependent bar for dependencies liba + info: configuration before negotiation: + config.liba.x=[null] (user configuration) + EOE + } + + : sys-liba-4 + : + { + $clone_cfg; + + $* bar '?sys:liba' +{ config.liba.x=true } 2>>~%EOE%; + build plan: + configure sys:liba/* + config.liba.x=true (expected user configuration) + new libb/1.0.0 (required by bar) + new bar/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured sys:liba/* + configured libb/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured,system !* available 1.0.0 + libb configured 1.0.0 + EOO + + $pkg_drop bar + } + } + + : baz + : + { + +$clone_cfg + + : basic + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by baz) + config.liba.x=true (set by baz) + new libb/1.0.0 (required by baz) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured libb/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured 1.0.0 + libb configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + EOO + + $pkg_drop baz + } + + : sys-liba-1 + : + { + $clone_cfg; + + $* baz '?sys:liba' +{ config.liba.x=false } 2>>EOE != 0 + error: unable to negotiate configuration for system dependency liba without configuration information + info: consider specifying system dependency version that has corresponding available package + info: dependent baz has prefer/accept clauses that cannot be evaluated without configuration information + EOE + } + + : sys-liba-2 + : + { + $clone_cfg; + + $* baz '?sys:liba/1.0.0' 2>>~%EOE%; + build plan: + configure sys:liba/1.0.0 + config.liba.x=true (expected by baz) + new libb/1.0.0 (required by baz) + new baz/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured sys:liba/1.0.0 + configured libb/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured,system !1.0.0 + libb configured 1.0.0 + EOO + + $pkg_drop baz + } + + : sys-liba-3 + : + { + $clone_cfg; + + $* baz '?sys:liba/1.0.0' +{ config.liba.x=false } 2>>EOE != 0 + error: unable to negotiate acceptable configuration with dependent baz for dependencies liba + info: configuration before negotiation: + config.liba.x=false (user configuration) + EOE + } + + : bar-sys-liba-4 + : + { + $clone_cfg; + + $* baz '?sys:liba/1.0.0' +{ config.liba.x=[null] } 2>>EOE != 0 + :1: error: invalid bool value: null + info: accept condition: ($config.liba.x) + info: in depends manifest value of package baz + EOE + } + + : sys-liba-5 + : + { + $clone_cfg; + + $* baz '?sys:liba/1.0.0' +{ config.liba.x=true } 2>>~%EOE%; + build plan: + configure sys:liba/1.0.0 + config.liba.x=true (expected user configuration) + new libb/1.0.0 (required by baz) + new baz/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured sys:liba/1.0.0 + configured libb/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + liba configured,system !1.0.0 + libb configured 1.0.0 + EOO + + $pkg_drop baz + } + } + } + + : proj-require-basics + : + { + +$clone_root_cfg && $rep_add $rep/t13m && $rep_fetch + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + config.liba.x=true (set by bar) + new bar/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.y = false + config.liba.n = 1024 + EOO + + $pkg_drop bar + } + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar, baz) + %( + config.liba.y=true (set by baz) + config.liba.x=true (set by bar) + %| + config.liba.x=true (set by bar) + config.liba.y=true (set by baz) + %) + new bar/1.0.0 (required by baz) + new baz/1.0.0 + config.baz.bar=true (set by baz) + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + bar configured 1.0.0 + liba configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/liba-1.0.0/build/config.build >>~%EOO%; + %.* + config.liba.x = true + config.liba.y = true + config.liba.n = 1024 + EOO + + cat cfg/baz-1.0.0/build/config.build >>~%EOO%; + %.* + config.baz.bar = true + EOO + + $pkg_drop baz + } + + : biz + : + { + $clone_cfg; + + $* biz 2>>EOE != 0 + error: configuration variable config.liba.x is not set to true + info: config.liba.x set in require clause of dependent biz + info: require clause: + config.liba.x = false # Error: not true + info: in depends manifest value of package biz + EOE + } + + : bix + : + { + $clone_cfg; + + $* bix 2>>EOE != 0 + error: configuration variable config.liba.n is not of bool type + info: config.liba.n set in require clause of dependent bix + info: require clause: + config.liba.n = 1 # Error: not bool + info: in depends manifest value of package bix + EOE + } + + : box + : + { + $clone_cfg; + + $* box 2>>EOE != 0 + error: package liba has no configuration variable config.liba.z + info: config.liba.z set in require clause of dependent box + info: require clause: + config.liba.z = true # Error: no such variable + info: in depends manifest value of package box + EOE + } + } + + : proj-reflect-append + : + { + +$clone_root_cfg && $rep_add $rep/t13n && $rep_fetch + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new libb/1.0.0 (required by bar) + new bar/1.0.0 + config.bar.libs=liba libb (set by bar) + config.bar.x=true (set by bar) + config.bar.y=true (set by bar) + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched libb/1.0.0 + unpacked libb/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured libb/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + libb configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.libs = liba libb + EOO + + $pkg_drop bar + } + } + + : proj-reflect-override + : + { + +$clone_root_cfg && $rep_add $rep/t13o && $rep_fetch + + : bar + : + { + $clone_cfg; + + $* bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new bar/1.0.0 + config.bar.x=true (set by bar) + config.bar.y=true (set by bar) + config.bar.z=true (set by bar) + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.z = true + EOO + + $pkg_drop bar + } + + : bar-config-1 + : + { + $clone_cfg; + + $* config.bar.y=true -- bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new bar/1.0.0 + config.bar.y=true (user configuration) + config.bar.x=true (set by bar) + config.bar.z=true (set by bar) + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.z = true + EOO + + $pkg_drop bar + } + + : bar-config-2 + : + { + $clone_cfg; + + $* config.bar.y=false -- bar 2>>EOE != 0 + error: reflect variable config.bar.y overriden by user configuration + info: reflect value: config.bar.y=true + info: user value: config.bar.y=false + info: reflect clause: + config.bar.x = true + config.bar.y = true + if ($config.origin(config.bar.z) != 'override') + config.bar.z = true + info: in depends manifest value of package bar + info: while satisfying bar/1.0.0 + EOE + } + + : bar-config-3 + : + { + $clone_cfg; + + $* config.bar.z=false -- bar 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new bar/1.0.0 + config.bar.z=false (user configuration) + config.bar.x=true (set by bar) + config.bar.y=true (set by bar) + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + %info: .+bar.+ is up to date% + updated bar/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.z = false + EOO + + $pkg_drop bar + } + + : baz + : + { + $clone_cfg; + + $* baz 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new bar/1.0.0 (required by baz) + config.bar.y=true (set by baz) + config.bar.x=true (set by bar) + config.bar.z=true (set by bar) + new baz/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched baz/1.0.0 + unpacked baz/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured baz/1.0.0 + %info: .+baz.+ is up to date% + updated baz/1.0.0 + EOE + + $pkg_status -r >>EOO; + !baz configured 1.0.0 + bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.z = true + EOO + + $pkg_drop baz + } + + : biz + : + { + $clone_cfg; + + $* biz 2>>EOE != 0 + error: reflect variable config.bar.y overriden by dependent biz + info: reflect value: config.bar.y=true + info: dependent value: config.bar.y=false + info: reflect clause: + config.bar.x = true + config.bar.y = true + if ($config.origin(config.bar.z) != 'override') + config.bar.z = true + info: in depends manifest value of package bar + info: while satisfying bar/1.0.0 + EOE + } + + : biz-bar + : + { + $clone_cfg; + + $* biz ?bar +{ config.bar.y=false } 2>>EOE != 0 + error: reflect variable config.bar.y overriden by user configuration + info: reflect value: config.bar.y=true + info: user value: config.bar.y=false + info: reflect clause: + config.bar.x = true + config.bar.y = true + if ($config.origin(config.bar.z) != 'override') + config.bar.z = true + info: in depends manifest value of package bar + info: while satisfying bar/1.0.0 + EOE + } + + : bix + : + { + $clone_cfg; + + $* bix 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new bar/1.0.0 (required by bix) + config.bar.z=false (set by bix) + config.bar.x=true (set by bar) + config.bar.y=true (set by bar) + new bix/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched bix/1.0.0 + unpacked bix/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured bix/1.0.0 + %info: .+bix.+ is up to date% + updated bix/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bix configured 1.0.0 + bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.z = false + EOO + + $pkg_drop bix + } + + : bix-bar + : + { + $clone_cfg; + + $* bix ?bar +{ config.bar.z=false } 2>>~%EOE%; + build plan: + new liba/1.0.0 (required by bar) + new bar/1.0.0 + config.bar.z=false (user configuration) + config.bar.x=true (set by bar) + config.bar.y=true (set by bar) + new bix/1.0.0 + fetched liba/1.0.0 + unpacked liba/1.0.0 + fetched bar/1.0.0 + unpacked bar/1.0.0 + fetched bix/1.0.0 + unpacked bix/1.0.0 + configured liba/1.0.0 + configured bar/1.0.0 + configured bix/1.0.0 + %info: .+bar.+ is up to date% + %info: .+bix.+ is up to date% + updated bar/1.0.0 + updated bix/1.0.0 + EOE + + $pkg_status -r >>EOO; + !bix configured 1.0.0 + bar configured 1.0.0 + liba configured 1.0.0 + EOO + + cat cfg/bar-1.0.0/build/config.build >>~%EOO%; + %.* + config.bar.x = true + config.bar.y = true + config.bar.z = false + EOO + + $pkg_drop bix + } + } + } +} + +: test-dependency +: +{ + +$clone_cfg + +$rep_add $rep/t10 && $rep_fetch + + : simultaneously + : + { + $clone_cfg; + + $* libfoo-bar libfoo-tests 2>>~%EOE%; + fetched libfoo-bar/1.0.0 + unpacked libfoo-bar/1.0.0 + fetched libfoo-tests/1.0.0 + unpacked libfoo-tests/1.0.0 + configured libfoo-bar/1.0.0 + configured libfoo-tests/1.0.0 + %info: .+libfoo-bar-1.0.0.+ is up to date% + %info: .+libfoo-tests-1.0.0.+ is up to date% + updated libfoo-bar/1.0.0 + updated libfoo-tests/1.0.0 + EOE + + cat cfg/libfoo-tests-1.0.0/build/config.build >>~%EOO%; + %.* + config.libfoo_tests.test = libfoo-bar + %.* + EOO + + $pkg_drop libfoo-bar libfoo-tests + } + + : sequentially + : + { + $clone_cfg; + + $* libfoo-baz 2>>~%EOE%; + fetched libfoo-baz/1.0.0 + unpacked libfoo-baz/1.0.0 + configured libfoo-baz/1.0.0 + %info: .+libfoo-baz-1.0.0.+ is up to date% + updated libfoo-baz/1.0.0 + EOE + + $* libfoo-tests 2>>~%EOE%; + fetched libfoo-tests/1.0.0 + unpacked libfoo-tests/1.0.0 + configured libfoo-tests/1.0.0 + %info: .+libfoo-tests-1.0.0.+ is up to date% + updated libfoo-tests/1.0.0 + EOE + + cat cfg/libfoo-tests-1.0.0/build/config.build >>~%EOO%; + %.* + config.libfoo_tests.test = libfoo-baz + %.* + EOO + + $pkg_drop libfoo-baz libfoo-tests + } + + : tests-only + : + { + $clone_cfg; + + $* libfoo-tests 2>>~%EOE% != 0 + error: unable to select dependency alternative for package libfoo-tests/1.0.0 + info: explicitly specify dependency packages to manually select the alternative + info: alternative: libfoo-bar + info: alternative: libfoo-baz + info: while satisfying libfoo-tests/1.0.0 + EOE + } +} + +: dependent +: +{ + +$clone_cfg + +$rep_add $rep/t2 && $rep_fetch + + : update + : + : Test --update-dependent option. + : + { + $clone_cfg; + + $* --yes libbar 2>>~%EOE%; + fetched libfoo/1.0.0 + unpacked libfoo/1.0.0 + fetched libbar/1.0.0 + unpacked libbar/1.0.0 + configured libfoo/1.0.0 + configured libbar/1.0.0 + %info: .+ is up to date% + updated libbar/1.0.0 + EOE + + $rep_add $rep/t4a && $rep_fetch; + + $* --update-dependent libfoo/1.1.0 <'y' 2>>~%EOE%; + upgrade libfoo/1.1.0 + reconfigure libbar (dependent of libfoo) + continue? [Y/n] disfigured libbar/1.0.0 + disfigured libfoo/1.0.0 + fetched libfoo/1.1.0 + unpacked libfoo/1.1.0 + configured libfoo/1.1.0 + configured libbar/1.0.0 + %info: .+ is up to date%{2} + updated libfoo/1.1.0 + updated libbar/1.0.0 + EOE + + $pkg_disfigure libbar 2>'disfigured libbar/1.0.0'; + $pkg_purge libbar 2>'purged libbar/1.0.0'; + + $pkg_disfigure libfoo 2>'disfigured libfoo/1.1.0'; + $pkg_purge libfoo 2>'purged libfoo/1.1.0' + } + + : leave + : + : Test --leave-dependent option. + : + { + $clone_cfg; + + $* --yes libbar 2>>~%EOE%; + fetched libfoo/1.0.0 + unpacked libfoo/1.0.0 + fetched libbar/1.0.0 + unpacked libbar/1.0.0 + configured libfoo/1.0.0 + configured libbar/1.0.0 + %info: .+ is up to date% + updated libbar/1.0.0 + EOE + + $rep_add $rep/t4a && $rep_fetch; $* --leave-dependent libfoo/1.1.0 <'y' 2>>~%EOE%; upgrade libfoo/1.1.0 @@ -5335,7 +13995,8 @@ test.options += --no-progress +cp -r $src/libhello-1.0.0 ./libhello +cat <+libhello/build/root.build config [bool] config.libhello.develop ?= false - text "develop=$config.libhello.develop" + if ($build.mode != 'skeleton') + text "develop=$config.libhello.develop" EOI +$rep_add libhello --type dir +$rep_fetch @@ -6838,24 +15499,76 @@ else $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; - $* ?foo libbaz +{ --config-name t2 } <>~%EOE%; + $* ?foo libbaz +{ --config-name t2 } --verbose 5 <>~%EOE%; y EOI + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + %trace: collect_build: add libbaz/1.0.0 \[t2.\]% + %trace: collect_build_prerequisites: begin libbaz/1.0.0 \[t2.\]% + %trace: collect_build_prerequisites: end libbaz/1.0.0 \[t2.\]% + %.* + trace: collect_build_prerequisites: begin foo/1.0.0 + %.* + %trace: collect_build_prerequisites: no cfg-clause for dependency libbaz/1.0.0 \[t2.\] of dependent foo/1.0.0% + trace: collect_build_prerequisites: end foo/1.0.0 + %.* + trace: execute_plan: simulate: yes + %.* + trace: evaluate_dependency: libbaz/1.0.0: unused + %.* + trace: evaluate_dependency: foo/1.0.0: unused + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: collect_drop: foo/1.0.0 package version needs to be replaced with drop + trace: pkg_build: collection failed due to package version replacement, retry from scratch + %.* + trace: pkg_build: refine package collection/plan execution from scratch + %.* + %trace: collect_build: add libbaz/1.0.0 \[t2.\]% + %trace: collect_build_prerequisites: begin libbaz/1.0.0 \[t2.\]% + %trace: collect_build_prerequisites: end libbaz/1.0.0 \[t2.\]% + trace: collect_build: apply version replacement for foo/1.0.0 + trace: collect_build: replacement: drop + %.* + trace: execute_plan: simulate: yes + %.* + %trace: evaluate_dependency: libbuild2-bar/1.0.0 \[t1..bpkg.build2.\]: unused% + %.* + trace: pkg_build: refine package collection/plan execution + %.* + trace: execute_plan: simulate: yes + %.* % new libbaz/1.0.0 \[t2.\]% % drop libbuild2-bar/1.0.0 \[t1..bpkg.build2.\] \(unused\)% drop libbaz/1.0.0 (unused) drop foo/1.0.0 (unused) - continue? [Y/n] disfigured foo/1.0.0 + continue? [Y/n] trace: execute_plan: simulate: no + %.* + disfigured foo/1.0.0 + %.* disfigured libbaz/1.0.0 + %.* %disfigured libbuild2-bar/1.0.0 \[t1..bpkg.build2.\]% + %.* %fetched libbaz/1.0.0 \[t2.\]% + %.* %unpacked libbaz/1.0.0 \[t2.\]% + %.* %purged libbuild2-bar/1.0.0 \[t1..bpkg.build2.\]% + %.* purged libbaz/1.0.0 + %.* purged foo/1.0.0 + %.* %configured libbaz/1.0.0 \[t2.\]% - %info: t2.+libbaz-1.0.0.+ is up to date% + %.* + %info: .+t2.+libbaz-1.0.0.+ is up to date% + %.* %updated libbaz/1.0.0 \[t2.\]% + %.* EOE $pkg_status -d t1 --link -r >>/EOO diff --git a/tests/pkg-build/t11a b/tests/pkg-build/t11a new file mode 120000 index 0000000..4f78412 --- /dev/null +++ b/tests/pkg-build/t11a @@ -0,0 +1 @@ +../common/dependency-alternatives/t11a \ No newline at end of file diff --git a/tests/pkg-build/t12a b/tests/pkg-build/t12a new file mode 120000 index 0000000..d421f92 --- /dev/null +++ b/tests/pkg-build/t12a @@ -0,0 +1 @@ +../common/satisfy/t12a/ \ No newline at end of file diff --git a/tests/pkg-build/t12b b/tests/pkg-build/t12b new file mode 120000 index 0000000..0fbba8a --- /dev/null +++ b/tests/pkg-build/t12b @@ -0,0 +1 @@ +../common/satisfy/t12b/ \ No newline at end of file diff --git a/tests/pkg-build/t13a b/tests/pkg-build/t13a new file mode 120000 index 0000000..9d8fb23 --- /dev/null +++ b/tests/pkg-build/t13a @@ -0,0 +1 @@ +../common/dependency-alternatives/t13a/ \ No newline at end of file diff --git a/tests/pkg-build/t13b b/tests/pkg-build/t13b new file mode 120000 index 0000000..d17701b --- /dev/null +++ b/tests/pkg-build/t13b @@ -0,0 +1 @@ +../common/dependency-alternatives/t13b/ \ No newline at end of file diff --git a/tests/pkg-build/t13c b/tests/pkg-build/t13c new file mode 120000 index 0000000..1c534d0 --- /dev/null +++ b/tests/pkg-build/t13c @@ -0,0 +1 @@ +../common/dependency-alternatives/t13c/ \ No newline at end of file diff --git a/tests/pkg-build/t13d b/tests/pkg-build/t13d new file mode 120000 index 0000000..6933497 --- /dev/null +++ b/tests/pkg-build/t13d @@ -0,0 +1 @@ +../common/dependency-alternatives/t13d/ \ No newline at end of file diff --git a/tests/pkg-build/t13e b/tests/pkg-build/t13e new file mode 120000 index 0000000..d8d84cc --- /dev/null +++ b/tests/pkg-build/t13e @@ -0,0 +1 @@ +../common/dependency-alternatives/t13e/ \ No newline at end of file diff --git a/tests/pkg-build/t13f b/tests/pkg-build/t13f new file mode 120000 index 0000000..bf556bc --- /dev/null +++ b/tests/pkg-build/t13f @@ -0,0 +1 @@ +../common/dependency-alternatives/t13f/ \ No newline at end of file diff --git a/tests/pkg-build/t13g b/tests/pkg-build/t13g new file mode 120000 index 0000000..4dc8eb4 --- /dev/null +++ b/tests/pkg-build/t13g @@ -0,0 +1 @@ +../common/dependency-alternatives/t13g/ \ No newline at end of file diff --git a/tests/pkg-build/t13h b/tests/pkg-build/t13h new file mode 120000 index 0000000..f99413a --- /dev/null +++ b/tests/pkg-build/t13h @@ -0,0 +1 @@ +../common/dependency-alternatives/t13h/ \ No newline at end of file diff --git a/tests/pkg-build/t13i b/tests/pkg-build/t13i new file mode 120000 index 0000000..bba4fd3 --- /dev/null +++ b/tests/pkg-build/t13i @@ -0,0 +1 @@ +../common/dependency-alternatives/t13i/ \ No newline at end of file diff --git a/tests/pkg-build/t13j b/tests/pkg-build/t13j new file mode 120000 index 0000000..da120da --- /dev/null +++ b/tests/pkg-build/t13j @@ -0,0 +1 @@ +../common/dependency-alternatives/t13j/ \ No newline at end of file diff --git a/tests/pkg-build/t13k b/tests/pkg-build/t13k new file mode 120000 index 0000000..b1e5a14 --- /dev/null +++ b/tests/pkg-build/t13k @@ -0,0 +1 @@ +../common/dependency-alternatives/t13k/ \ No newline at end of file diff --git a/tests/pkg-build/t13l b/tests/pkg-build/t13l new file mode 120000 index 0000000..40d9561 --- /dev/null +++ b/tests/pkg-build/t13l @@ -0,0 +1 @@ +../common/dependency-alternatives/t13l/ \ No newline at end of file diff --git a/tests/pkg-build/t13m b/tests/pkg-build/t13m new file mode 120000 index 0000000..0154455 --- /dev/null +++ b/tests/pkg-build/t13m @@ -0,0 +1 @@ +../common/dependency-alternatives/t13m/ \ No newline at end of file diff --git a/tests/pkg-build/t13n b/tests/pkg-build/t13n new file mode 120000 index 0000000..1ed57ca --- /dev/null +++ b/tests/pkg-build/t13n @@ -0,0 +1 @@ +../common/dependency-alternatives/t13n/ \ No newline at end of file diff --git a/tests/pkg-build/t13o b/tests/pkg-build/t13o new file mode 120000 index 0000000..9516f8f --- /dev/null +++ b/tests/pkg-build/t13o @@ -0,0 +1 @@ +../common/dependency-alternatives/t13o/ \ No newline at end of file -- cgit v1.1