1// Copyright 2019 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "fmt" 19 "runtime" 20 "testing" 21 22 "android/soong/android" 23 "github.com/google/blueprint" 24) 25 26var prepareForPrebuiltTest = android.GroupFixturePreparers( 27 prepareForCcTest, 28 android.PrepareForTestWithAndroidMk, 29) 30 31func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext { 32 t.Helper() 33 result := android.GroupFixturePreparers( 34 prepareForPrebuiltTest, 35 fs.AddToFixture(), 36 android.GroupFixturePreparers(handlers...), 37 ).RunTestWithBp(t, bp) 38 39 return result.TestContext 40} 41 42type configCustomizer func(config android.Config) 43 44func TestPrebuilt(t *testing.T) { 45 bp := ` 46 cc_library { 47 name: "liba", 48 } 49 50 cc_prebuilt_library_shared { 51 name: "liba", 52 srcs: ["liba.so"], 53 prefer: true, 54 } 55 56 cc_library { 57 name: "libb", 58 } 59 60 cc_prebuilt_library_static { 61 name: "libb", 62 srcs: ["libb.a"], 63 prefer: true, 64 } 65 66 cc_library_shared { 67 name: "libd", 68 } 69 70 cc_prebuilt_library_shared { 71 name: "libd", 72 srcs: ["libd.so"], 73 } 74 75 cc_library_static { 76 name: "libe", 77 } 78 79 cc_prebuilt_library_static { 80 name: "libe", 81 srcs: ["libe.a"], 82 } 83 84 cc_library { 85 name: "libf", 86 } 87 88 cc_prebuilt_library { 89 name: "libf", 90 static: { 91 srcs: ["libf.a"], 92 }, 93 shared: { 94 srcs: ["libf.so"], 95 }, 96 } 97 98 cc_object { 99 name: "crtx", 100 } 101 102 cc_prebuilt_object { 103 name: "crtx", 104 srcs: ["crtx.o"], 105 } 106 ` 107 108 ctx := testPrebuilt(t, bp, map[string][]byte{ 109 "liba.so": nil, 110 "libb.a": nil, 111 "libd.so": nil, 112 "libe.a": nil, 113 "libf.a": nil, 114 "libf.so": nil, 115 "crtx.o": nil, 116 }) 117 118 // Verify that all the modules exist and that their dependencies were connected correctly 119 liba := ctx.ModuleForTests("liba", "android_arm64_armv8-a_shared").Module() 120 libb := ctx.ModuleForTests("libb", "android_arm64_armv8-a_static").Module() 121 libd := ctx.ModuleForTests("libd", "android_arm64_armv8-a_shared").Module() 122 libe := ctx.ModuleForTests("libe", "android_arm64_armv8-a_static").Module() 123 libfStatic := ctx.ModuleForTests("libf", "android_arm64_armv8-a_static").Module() 124 libfShared := ctx.ModuleForTests("libf", "android_arm64_armv8-a_shared").Module() 125 crtx := ctx.ModuleForTests("crtx", "android_arm64_armv8-a").Module() 126 127 prebuiltLiba := ctx.ModuleForTests("prebuilt_liba", "android_arm64_armv8-a_shared").Module() 128 prebuiltLibb := ctx.ModuleForTests("prebuilt_libb", "android_arm64_armv8-a_static").Module() 129 prebuiltLibd := ctx.ModuleForTests("prebuilt_libd", "android_arm64_armv8-a_shared").Module() 130 prebuiltLibe := ctx.ModuleForTests("prebuilt_libe", "android_arm64_armv8-a_static").Module() 131 prebuiltLibfStatic := ctx.ModuleForTests("prebuilt_libf", "android_arm64_armv8-a_static").Module() 132 prebuiltLibfShared := ctx.ModuleForTests("prebuilt_libf", "android_arm64_armv8-a_shared").Module() 133 prebuiltCrtx := ctx.ModuleForTests("prebuilt_crtx", "android_arm64_armv8-a").Module() 134 135 hasDep := func(m android.Module, wantDep android.Module) bool { 136 t.Helper() 137 var found bool 138 ctx.VisitDirectDeps(m, func(dep blueprint.Module) { 139 if dep == wantDep { 140 found = true 141 } 142 }) 143 return found 144 } 145 146 if !hasDep(liba, prebuiltLiba) { 147 t.Errorf("liba missing dependency on prebuilt_liba") 148 } 149 150 if !hasDep(libb, prebuiltLibb) { 151 t.Errorf("libb missing dependency on prebuilt_libb") 152 } 153 154 if !hasDep(libd, prebuiltLibd) { 155 t.Errorf("libd missing dependency on prebuilt_libd") 156 } 157 158 if !hasDep(libe, prebuiltLibe) { 159 t.Errorf("libe missing dependency on prebuilt_libe") 160 } 161 162 if !hasDep(libfStatic, prebuiltLibfStatic) { 163 t.Errorf("libf static missing dependency on prebuilt_libf") 164 } 165 166 if !hasDep(libfShared, prebuiltLibfShared) { 167 t.Errorf("libf shared missing dependency on prebuilt_libf") 168 } 169 170 if !hasDep(crtx, prebuiltCrtx) { 171 t.Errorf("crtx missing dependency on prebuilt_crtx") 172 } 173 174 entries := android.AndroidMkInfoForTest(t, ctx, prebuiltLiba).PrimaryInfo 175 android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0]) 176 entries = android.AndroidMkInfoForTest(t, ctx, prebuiltLibb).PrimaryInfo 177 android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_static", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0]) 178} 179 180func TestPrebuiltLibraryShared(t *testing.T) { 181 ctx := testPrebuilt(t, ` 182 cc_prebuilt_library_shared { 183 name: "libtest", 184 srcs: ["libf.so"], 185 strip: { 186 none: true, 187 }, 188 } 189 `, map[string][]byte{ 190 "libf.so": nil, 191 }) 192 193 shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module) 194 assertString(t, shared.OutputFile().Path().Base(), "libtest.so") 195} 196 197func TestPrebuiltLibraryStatic(t *testing.T) { 198 ctx := testPrebuilt(t, ` 199 cc_prebuilt_library_static { 200 name: "libtest", 201 srcs: ["libf.a"], 202 } 203 `, map[string][]byte{ 204 "libf.a": nil, 205 }) 206 207 static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module) 208 assertString(t, static.OutputFile().Path().Base(), "libf.a") 209} 210 211func TestPrebuiltLibrary(t *testing.T) { 212 ctx := testPrebuilt(t, ` 213 cc_prebuilt_library { 214 name: "libtest", 215 static: { 216 srcs: ["libf.a"], 217 }, 218 shared: { 219 srcs: ["libf.so"], 220 }, 221 strip: { 222 none: true, 223 }, 224 } 225 `, map[string][]byte{ 226 "libf.a": nil, 227 "libf.so": nil, 228 }) 229 230 shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module) 231 assertString(t, shared.OutputFile().Path().Base(), "libtest.so") 232 233 static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module) 234 assertString(t, static.OutputFile().Path().Base(), "libf.a") 235} 236 237func TestPrebuiltLibraryStem(t *testing.T) { 238 ctx := testPrebuilt(t, ` 239 cc_prebuilt_library { 240 name: "libfoo", 241 stem: "libbar", 242 static: { 243 srcs: ["libfoo.a"], 244 }, 245 shared: { 246 srcs: ["libfoo.so"], 247 }, 248 strip: { 249 none: true, 250 }, 251 } 252 `, map[string][]byte{ 253 "libfoo.a": nil, 254 "libfoo.so": nil, 255 }) 256 257 static := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module) 258 assertString(t, static.OutputFile().Path().Base(), "libfoo.a") 259 260 shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module) 261 assertString(t, shared.OutputFile().Path().Base(), "libbar.so") 262} 263 264func TestPrebuiltLibrarySharedStem(t *testing.T) { 265 ctx := testPrebuilt(t, ` 266 cc_prebuilt_library_shared { 267 name: "libfoo", 268 stem: "libbar", 269 srcs: ["libfoo.so"], 270 strip: { 271 none: true, 272 }, 273 } 274 `, map[string][]byte{ 275 "libfoo.so": nil, 276 }) 277 278 shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module) 279 assertString(t, shared.OutputFile().Path().Base(), "libbar.so") 280} 281 282func TestPrebuiltSymlinkedHostBinary(t *testing.T) { 283 if runtime.GOOS != "linux" { 284 t.Skipf("Skipping host prebuilt testing that is only supported on linux not %s", runtime.GOOS) 285 } 286 287 ctx := testPrebuilt(t, ` 288 cc_prebuilt_library_shared { 289 name: "libfoo", 290 device_supported: false, 291 host_supported: true, 292 target: { 293 linux_glibc_x86_64: { 294 srcs: ["linux_glibc_x86_64/lib64/libfoo.so"], 295 }, 296 }, 297 } 298 299 cc_prebuilt_binary { 300 name: "foo", 301 device_supported: false, 302 host_supported: true, 303 shared_libs: ["libfoo"], 304 target: { 305 linux_glibc_x86_64: { 306 srcs: ["linux_glibc_x86_64/bin/foo"], 307 }, 308 }, 309 } 310 `, map[string][]byte{ 311 "libfoo.so": nil, 312 "foo": nil, 313 }) 314 315 fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink") 316 assertString(t, fooRule.Output.String(), "out/soong/.intermediates/foo/linux_glibc_x86_64/foo") 317 assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo") 318 319 var libfooDep android.Path 320 for _, dep := range fooRule.Implicits { 321 if dep.Base() == "libfoo.so" { 322 libfooDep = dep 323 break 324 } 325 } 326 assertString(t, libfooDep.String(), "out/soong/.intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so") 327} 328 329func TestPrebuiltLibrarySanitized(t *testing.T) { 330 bp := `cc_prebuilt_library { 331 name: "libtest", 332 static: { 333 sanitized: { none: { srcs: ["libf.a"], }, hwaddress: { srcs: ["libf.hwasan.a"], }, }, 334 }, 335 shared: { 336 sanitized: { none: { srcs: ["libf.so"], }, hwaddress: { srcs: ["hwasan/libf.so"], }, }, 337 }, 338 } 339 cc_prebuilt_library_static { 340 name: "libtest_static", 341 sanitized: { none: { srcs: ["libf.a"], }, hwaddress: { srcs: ["libf.hwasan.a"], }, }, 342 } 343 cc_prebuilt_library_shared { 344 name: "libtest_shared", 345 sanitized: { none: { srcs: ["libf.so"], }, hwaddress: { srcs: ["hwasan/libf.so"], }, }, 346 }` 347 348 fs := map[string][]byte{ 349 "libf.a": nil, 350 "libf.hwasan.a": nil, 351 "libf.so": nil, 352 "hwasan/libf.so": nil, 353 } 354 355 // Without SANITIZE_TARGET. 356 ctx := testPrebuilt(t, bp, fs) 357 358 shared_rule := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Rule("android/soong/cc.strip") 359 assertString(t, shared_rule.Input.String(), "libf.so") 360 361 static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module) 362 assertString(t, static.OutputFile().Path().Base(), "libf.a") 363 364 shared_rule2 := ctx.ModuleForTests("libtest_shared", "android_arm64_armv8-a_shared").Rule("android/soong/cc.strip") 365 assertString(t, shared_rule2.Input.String(), "libf.so") 366 367 static2 := ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static").Module().(*Module) 368 assertString(t, static2.OutputFile().Path().Base(), "libf.a") 369 370 // With SANITIZE_TARGET=hwaddress 371 ctx = testPrebuilt(t, bp, fs, 372 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 373 variables.SanitizeDevice = []string{"hwaddress"} 374 }), 375 ) 376 377 shared_rule = ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip") 378 assertString(t, shared_rule.Input.String(), "hwasan/libf.so") 379 380 static = ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static_hwasan").Module().(*Module) 381 assertString(t, static.OutputFile().Path().Base(), "libf.hwasan.a") 382 383 shared_rule2 = ctx.ModuleForTests("libtest_shared", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip") 384 assertString(t, shared_rule2.Input.String(), "hwasan/libf.so") 385 386 static2 = ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static_hwasan").Module().(*Module) 387 assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a") 388} 389 390func TestPrebuiltBinaryNoSrcsNoError(t *testing.T) { 391 const bp = ` 392cc_prebuilt_binary { 393 name: "bintest", 394 srcs: [], 395}` 396 ctx := testPrebuilt(t, bp, map[string][]byte{}) 397 mod := ctx.ModuleForTests("bintest", "android_arm64_armv8-a").Module().(*Module) 398 android.AssertBoolEquals(t, `expected no srcs to yield no output file`, false, mod.OutputFile().Valid()) 399} 400 401func TestPrebuiltBinaryMultipleSrcs(t *testing.T) { 402 const bp = ` 403cc_prebuilt_binary { 404 name: "bintest", 405 srcs: ["foo", "bar"], 406}` 407 testCcError(t, `Android.bp:4:6: module "bintest" variant "android_arm64_armv8-a": srcs: multiple prebuilt source files`, bp) 408} 409 410func TestMultiplePrebuilts(t *testing.T) { 411 bp := ` 412 // an rdep 413 cc_library { 414 name: "libfoo", 415 shared_libs: ["libbar"], 416 } 417 418 // multiple variations of dep 419 // source 420 cc_library { 421 name: "libbar", 422 } 423 // prebuilt "v1" 424 cc_prebuilt_library_shared { 425 name: "libbar", 426 srcs: ["libbar.so"], 427 } 428 // prebuilt "v2" 429 cc_prebuilt_library_shared { 430 name: "libbar.v2", 431 stem: "libbar", 432 source_module_name: "libbar", 433 srcs: ["libbar.so"], 434 } 435 436 // selectors 437 apex_contributions { 438 name: "myapex_contributions", 439 contents: ["%v"], 440 } 441 all_apex_contributions {name: "all_apex_contributions"} 442 ` 443 hasDep := func(ctx *android.TestContext, m android.Module, wantDep android.Module) bool { 444 t.Helper() 445 var found bool 446 ctx.VisitDirectDeps(m, func(dep blueprint.Module) { 447 if dep == wantDep { 448 found = true 449 } 450 }) 451 return found 452 } 453 454 testCases := []struct { 455 desc string 456 selectedDependencyName string 457 expectedDependencyName string 458 }{ 459 { 460 desc: "Source library is selected using apex_contributions", 461 selectedDependencyName: "libbar", 462 expectedDependencyName: "libbar", 463 }, 464 { 465 desc: "Prebuilt library v1 is selected using apex_contributions", 466 selectedDependencyName: "prebuilt_libbar", 467 expectedDependencyName: "prebuilt_libbar", 468 }, 469 { 470 desc: "Prebuilt library v2 is selected using apex_contributions", 471 selectedDependencyName: "prebuilt_libbar.v2", 472 expectedDependencyName: "prebuilt_libbar.v2", 473 }, 474 } 475 476 for _, tc := range testCases { 477 preparer := android.GroupFixturePreparers( 478 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 479 android.RegisterApexContributionsBuildComponents(ctx) 480 }), 481 android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), 482 ) 483 ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{ 484 "libbar.so": nil, 485 "crtx.o": nil, 486 }, preparer) 487 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() 488 expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module() 489 android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency)) 490 // check that LOCAL_SHARED_LIBRARIES contains libbar and not libbar.v<N> 491 entries := android.AndroidMkInfoForTest(t, ctx, libfoo).PrimaryInfo 492 android.AssertStringListContains(t, "Version should not be present in LOCAL_SHARED_LIBRARIES", entries.EntryMap["LOCAL_SHARED_LIBRARIES"], "libbar") 493 494 // check installation rules 495 // the selected soong module should be exported to make 496 libbar := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module() 497 android.AssertBoolEquals(t, fmt.Sprintf("dependency %s should be exported to make\n", expectedDependency), true, !libbar.IsHideFromMake()) 498 499 // check LOCAL_MODULE of the selected module name 500 // the prebuilt should have the same LOCAL_MODULE when exported to make 501 entries = android.AndroidMkInfoForTest(t, ctx, libbar).PrimaryInfo 502 android.AssertStringEquals(t, "unexpected LOCAL_MODULE", "libbar", entries.EntryMap["LOCAL_MODULE"][0]) 503 } 504} 505 506// Setting prefer on multiple prebuilts is an error, unless one of them is also listed in apex_contributions 507func TestMultiplePrebuiltsPreferredUsingLegacyFlags(t *testing.T) { 508 bp := ` 509 // an rdep 510 cc_library { 511 name: "libfoo", 512 shared_libs: ["libbar"], 513 } 514 515 // multiple variations of dep 516 // source 517 cc_library { 518 name: "libbar", 519 } 520 // prebuilt "v1" 521 cc_prebuilt_library_shared { 522 name: "libbar", 523 srcs: ["libbar.so"], 524 prefer: true, 525 } 526 // prebuilt "v2" 527 cc_prebuilt_library_shared { 528 name: "libbar.v2", 529 stem: "libbar", 530 source_module_name: "libbar", 531 srcs: ["libbar.so"], 532 prefer: true, 533 } 534 535 // selectors 536 apex_contributions { 537 name: "myapex_contributions", 538 contents: [%v], 539 } 540 all_apex_contributions {name: "all_apex_contributions"} 541 ` 542 hasDep := func(ctx *android.TestContext, m android.Module, wantDep android.Module) bool { 543 t.Helper() 544 var found bool 545 ctx.VisitDirectDeps(m, func(dep blueprint.Module) { 546 if dep == wantDep { 547 found = true 548 } 549 }) 550 return found 551 } 552 553 testCases := []struct { 554 desc string 555 selectedDependencyName string 556 expectedDependencyName string 557 expectedErr string 558 }{ 559 { 560 desc: "Multiple prebuilts have prefer: true", 561 expectedErr: "Multiple prebuilt modules prebuilt_libbar and prebuilt_libbar.v2 have been marked as preferred for this source module", 562 }, 563 { 564 desc: "Multiple prebuilts have prefer: true. The prebuilt listed in apex_contributions wins.", 565 selectedDependencyName: `"prebuilt_libbar"`, 566 expectedDependencyName: "prebuilt_libbar", 567 }, 568 } 569 570 for _, tc := range testCases { 571 preparer := android.GroupFixturePreparers( 572 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 573 android.RegisterApexContributionsBuildComponents(ctx) 574 }), 575 android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), 576 ) 577 if tc.expectedErr != "" { 578 preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedErr)) 579 } 580 581 ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{ 582 "libbar.so": nil, 583 "crtx.o": nil, 584 }, preparer) 585 if tc.expectedErr != "" { 586 return // the fixture will assert that the excepted err has been raised 587 } 588 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() 589 expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module() 590 android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency)) 591 } 592} 593 594// If module sdk cannot provide a cc module variant (e.g. static), then the module variant from source should be used 595func TestMissingVariantInModuleSdk(t *testing.T) { 596 bp := ` 597 // an rdep 598 cc_library { 599 name: "libfoo", 600 static_libs: ["libbar"], 601 } 602 603 // source 604 cc_library { 605 name: "libbar", 606 } 607 // prebuilt 608 // libbar only exists as a shared library 609 cc_prebuilt_library_shared { 610 name: "libbar", 611 srcs: ["libbar.so"], 612 } 613 // selectors 614 apex_contributions { 615 name: "myapex_contributions", 616 contents: ["prebuilt_libbar"], 617 } 618 all_apex_contributions {name: "all_apex_contributions"} 619 ` 620 hasDep := func(ctx *android.TestContext, m android.Module, wantDep android.Module) bool { 621 t.Helper() 622 var found bool 623 ctx.VisitDirectDeps(m, func(dep blueprint.Module) { 624 if dep == wantDep { 625 found = true 626 } 627 }) 628 return found 629 } 630 631 preparer := android.GroupFixturePreparers( 632 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 633 android.RegisterApexContributionsBuildComponents(ctx) 634 }), 635 android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), 636 ) 637 ctx := testPrebuilt(t, bp, map[string][]byte{ 638 "libbar.so": nil, 639 "crtx.o": nil, 640 }, preparer) 641 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() 642 sourceLibBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module() 643 // Even though the prebuilt is listed in apex_contributions, the prebuilt does not have a static variant. 644 // Therefore source of libbar should be used. 645 android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from libfoo to source libbar"), true, hasDep(ctx, libfoo, sourceLibBar)) 646} 647