1// Copyright 2021 Google LLC 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 mk2rbc 16 17import ( 18 "bytes" 19 "io/fs" 20 "path/filepath" 21 "strings" 22 "testing" 23) 24 25var testCases = []struct { 26 desc string 27 mkname string 28 in string 29 expected string 30}{ 31 { 32 desc: "Comment", 33 mkname: "product.mk", 34 in: ` 35# Comment 36# FOO= a\ 37 b 38`, 39 expected: `# Comment 40# FOO= a 41# b 42load("//build/make/core:product_config.rbc", "rblf") 43 44def init(g, handle): 45 cfg = rblf.cfg(handle) 46`, 47 }, 48 { 49 desc: "Name conversion", 50 mkname: "path/bar-baz.mk", 51 in: ` 52# Comment 53`, 54 expected: `# Comment 55load("//build/make/core:product_config.rbc", "rblf") 56 57def init(g, handle): 58 cfg = rblf.cfg(handle) 59`, 60 }, 61 { 62 desc: "Item variable", 63 mkname: "pixel3.mk", 64 in: ` 65PRODUCT_NAME := Pixel 3 66PRODUCT_MODEL := 67local_var = foo 68local-var-with-dashes := bar 69$(warning local-var-with-dashes: $(local-var-with-dashes)) 70GLOBAL-VAR-WITH-DASHES := baz 71$(warning GLOBAL-VAR-WITH-DASHES: $(GLOBAL-VAR-WITH-DASHES)) 72`, 73 expected: `load("//build/make/core:product_config.rbc", "rblf") 74 75def init(g, handle): 76 cfg = rblf.cfg(handle) 77 cfg["PRODUCT_NAME"] = "Pixel 3" 78 cfg["PRODUCT_MODEL"] = "" 79 _local_var = "foo" 80 _local_var_with_dashes = "bar" 81 rblf.mkwarning("pixel3.mk", "local-var-with-dashes: %s" % _local_var_with_dashes) 82 g["GLOBAL-VAR-WITH-DASHES"] = "baz" 83 rblf.mkwarning("pixel3.mk", "GLOBAL-VAR-WITH-DASHES: %s" % g["GLOBAL-VAR-WITH-DASHES"]) 84`, 85 }, 86 { 87 desc: "List variable", 88 mkname: "pixel4.mk", 89 in: ` 90PRODUCT_PACKAGES = package1 package2 91PRODUCT_COPY_FILES += file2:target 92PRODUCT_PACKAGES += package3 93PRODUCT_COPY_FILES = 94`, 95 expected: `load("//build/make/core:product_config.rbc", "rblf") 96 97def init(g, handle): 98 cfg = rblf.cfg(handle) 99 cfg["PRODUCT_PACKAGES"] = [ 100 "package1", 101 "package2", 102 ] 103 rblf.setdefault(handle, "PRODUCT_COPY_FILES") 104 cfg["PRODUCT_COPY_FILES"] += ["file2:target"] 105 cfg["PRODUCT_PACKAGES"] += ["package3"] 106 cfg["PRODUCT_COPY_FILES"] = [] 107`, 108 }, 109 { 110 desc: "Unknown function", 111 mkname: "product.mk", 112 in: ` 113PRODUCT_NAME := $(call foo1, bar) 114PRODUCT_NAME := $(call foo0) 115`, 116 expected: `load("//build/make/core:product_config.rbc", "rblf") 117 118def init(g, handle): 119 cfg = rblf.cfg(handle) 120 cfg["PRODUCT_NAME"] = rblf.mk2rbc_error("product.mk:2", "cannot handle invoking foo1") 121 cfg["PRODUCT_NAME"] = rblf.mk2rbc_error("product.mk:3", "cannot handle invoking foo0") 122`, 123 }, 124 { 125 desc: "Inherit configuration always", 126 mkname: "product.mk", 127 in: ` 128$(call inherit-product, part.mk) 129ifdef PRODUCT_NAME 130$(call inherit-product, part1.mk) 131else # Comment 132$(call inherit-product, $(LOCAL_PATH)/part.mk) 133endif 134`, 135 expected: `load("//build/make/core:product_config.rbc", "rblf") 136load(":part.star", _part_init = "init") 137load(":part1.star|init", _part1_init = "init") 138 139def init(g, handle): 140 cfg = rblf.cfg(handle) 141 rblf.inherit(handle, "part", _part_init) 142 if cfg.get("PRODUCT_NAME", ""): 143 if not _part1_init: 144 rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star")) 145 rblf.inherit(handle, "part1", _part1_init) 146 else: 147 # Comment 148 rblf.inherit(handle, "part", _part_init) 149`, 150 }, 151 { 152 desc: "Inherit configuration if it exists", 153 mkname: "product.mk", 154 in: ` 155$(call inherit-product-if-exists, part.mk) 156`, 157 expected: `load("//build/make/core:product_config.rbc", "rblf") 158load(":part.star|init", _part_init = "init") 159 160def init(g, handle): 161 cfg = rblf.cfg(handle) 162 if _part_init: 163 rblf.inherit(handle, "part", _part_init) 164`, 165 }, 166 167 { 168 desc: "Include configuration", 169 mkname: "product.mk", 170 in: ` 171include part.mk 172ifdef PRODUCT_NAME 173include part1.mk 174else 175-include $(LOCAL_PATH)/part1.mk) 176endif 177`, 178 expected: `load("//build/make/core:product_config.rbc", "rblf") 179load(":part.star", _part_init = "init") 180load(":part1.star|init", _part1_init = "init") 181 182def init(g, handle): 183 cfg = rblf.cfg(handle) 184 _part_init(g, handle) 185 if cfg.get("PRODUCT_NAME", ""): 186 if not _part1_init: 187 rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star")) 188 _part1_init(g, handle) 189 else: 190 if _part1_init != None: 191 _part1_init(g, handle) 192`, 193 }, 194 195 { 196 desc: "Synonymous inherited configurations", 197 mkname: "path/product.mk", 198 in: ` 199$(call inherit-product, */font.mk) 200$(call inherit-product, $(sort $(wildcard */font.mk))) 201$(call inherit-product, $(wildcard */font.mk)) 202 203include */font.mk 204include $(sort $(wildcard */font.mk)) 205include $(wildcard */font.mk) 206`, 207 expected: `load("//build/make/core:product_config.rbc", "rblf") 208load("//bar:font.star", _font_init = "init") 209load("//foo:font.star", _font1_init = "init") 210 211def init(g, handle): 212 cfg = rblf.cfg(handle) 213 rblf.inherit(handle, "bar/font", _font_init) 214 rblf.inherit(handle, "foo/font", _font1_init) 215 rblf.inherit(handle, "bar/font", _font_init) 216 rblf.inherit(handle, "foo/font", _font1_init) 217 rblf.inherit(handle, "bar/font", _font_init) 218 rblf.inherit(handle, "foo/font", _font1_init) 219 _font_init(g, handle) 220 _font1_init(g, handle) 221 _font_init(g, handle) 222 _font1_init(g, handle) 223 _font_init(g, handle) 224 _font1_init(g, handle) 225`, 226 }, 227 { 228 desc: "Directive define", 229 mkname: "product.mk", 230 in: ` 231define some-macro 232 $(info foo) 233endef 234`, 235 expected: `load("//build/make/core:product_config.rbc", "rblf") 236 237def init(g, handle): 238 cfg = rblf.cfg(handle) 239 rblf.mk2rbc_error("product.mk:2", "define is not supported: some-macro") 240`, 241 }, 242 { 243 desc: "Ifdef", 244 mkname: "product.mk", 245 in: ` 246ifdef PRODUCT_NAME 247 PRODUCT_NAME = gizmo 248else 249endif 250local_var := 251ifdef local_var 252endif 253`, 254 expected: `load("//build/make/core:product_config.rbc", "rblf") 255 256def init(g, handle): 257 cfg = rblf.cfg(handle) 258 if cfg.get("PRODUCT_NAME", ""): 259 cfg["PRODUCT_NAME"] = "gizmo" 260 else: 261 pass 262 _local_var = "" 263 if _local_var: 264 pass 265`, 266 }, 267 { 268 desc: "Simple functions", 269 mkname: "product.mk", 270 in: ` 271$(warning this is the warning) 272$(warning) 273$(warning # this warning starts with a pound) 274$(warning this warning has a # in the middle) 275$(info this is the info) 276$(error this is the error) 277PRODUCT_NAME:=$(shell echo *) 278`, 279 expected: `load("//build/make/core:product_config.rbc", "rblf") 280 281def init(g, handle): 282 cfg = rblf.cfg(handle) 283 rblf.mkwarning("product.mk", "this is the warning") 284 rblf.mkwarning("product.mk", "") 285 rblf.mkwarning("product.mk", "# this warning starts with a pound") 286 rblf.mkwarning("product.mk", "this warning has a # in the middle") 287 rblf.mkinfo("product.mk", "this is the info") 288 rblf.mkerror("product.mk", "this is the error") 289 cfg["PRODUCT_NAME"] = rblf.shell("echo *") 290`, 291 }, 292 { 293 desc: "Empty if", 294 mkname: "product.mk", 295 in: ` 296ifdef PRODUCT_NAME 297# Comment 298else 299 TARGET_COPY_OUT_RECOVERY := foo 300endif 301`, 302 expected: `load("//build/make/core:product_config.rbc", "rblf") 303 304def init(g, handle): 305 cfg = rblf.cfg(handle) 306 if cfg.get("PRODUCT_NAME", ""): 307 # Comment 308 pass 309 else: 310 rblf.mk2rbc_error("product.mk:5", "cannot set predefined variable TARGET_COPY_OUT_RECOVERY to \"foo\", its value should be \"recovery\"") 311`, 312 }, 313 { 314 desc: "if/else/endif", 315 mkname: "product.mk", 316 in: ` 317ifndef PRODUCT_NAME 318 PRODUCT_NAME=gizmo1 319else 320 PRODUCT_NAME=gizmo2 321endif 322`, 323 expected: `load("//build/make/core:product_config.rbc", "rblf") 324 325def init(g, handle): 326 cfg = rblf.cfg(handle) 327 if not cfg.get("PRODUCT_NAME", ""): 328 cfg["PRODUCT_NAME"] = "gizmo1" 329 else: 330 cfg["PRODUCT_NAME"] = "gizmo2" 331`, 332 }, 333 { 334 desc: "else if", 335 mkname: "product.mk", 336 in: ` 337ifdef PRODUCT_NAME 338 PRODUCT_NAME = gizmo 339else ifndef PRODUCT_PACKAGES # Comment 340endif 341 `, 342 expected: `load("//build/make/core:product_config.rbc", "rblf") 343 344def init(g, handle): 345 cfg = rblf.cfg(handle) 346 if cfg.get("PRODUCT_NAME", ""): 347 cfg["PRODUCT_NAME"] = "gizmo" 348 elif not cfg.get("PRODUCT_PACKAGES", []): 349 # Comment 350 pass 351`, 352 }, 353 { 354 desc: "ifeq / ifneq", 355 mkname: "product.mk", 356 in: ` 357ifeq (aosp_arm, $(TARGET_PRODUCT)) 358 PRODUCT_MODEL = pix2 359else 360 PRODUCT_MODEL = pix21 361endif 362ifneq (aosp_x86, $(TARGET_PRODUCT)) 363 PRODUCT_MODEL = pix3 364endif 365`, 366 expected: `load("//build/make/core:product_config.rbc", "rblf") 367 368def init(g, handle): 369 cfg = rblf.cfg(handle) 370 if "aosp_arm" == g["TARGET_PRODUCT"]: 371 cfg["PRODUCT_MODEL"] = "pix2" 372 else: 373 cfg["PRODUCT_MODEL"] = "pix21" 374 if "aosp_x86" != g["TARGET_PRODUCT"]: 375 cfg["PRODUCT_MODEL"] = "pix3" 376`, 377 }, 378 { 379 desc: "ifeq with soong_config_get", 380 mkname: "product.mk", 381 in: ` 382ifeq (true,$(call soong_config_get,art_module,source_build)) 383endif 384`, 385 expected: `load("//build/make/core:product_config.rbc", "rblf") 386 387def init(g, handle): 388 cfg = rblf.cfg(handle) 389 if "true" == rblf.soong_config_get(g, "art_module", "source_build"): 390 pass 391`, 392 }, 393 { 394 desc: "ifeq with $(NATIVE_COVERAGE)", 395 mkname: "product.mk", 396 in: ` 397ifeq ($(NATIVE_COVERAGE),true) 398endif 399`, 400 expected: `load("//build/make/core:product_config.rbc", "rblf") 401 402def init(g, handle): 403 cfg = rblf.cfg(handle) 404 if g.get("NATIVE_COVERAGE", False): 405 pass 406`, 407 }, 408 { 409 desc: "Check filter result", 410 mkname: "product.mk", 411 in: ` 412ifeq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) 413endif 414ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)) 415endif 416ifneq (,$(filter plaf,$(PLATFORM_LIST))) 417endif 418ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng)) 419endif 420ifneq (, $(filter $(TARGET_BUILD_VARIANT), userdebug eng)) 421endif 422ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) 423endif 424ifneq (,$(filter true, $(v1)$(v2))) 425endif 426ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT))) 427else ifneq (,$(filter barbet%,$(TARGET_PRODUCT))) 428endif 429ifeq (,$(filter-out sunfish_kasan, $(TARGET_PRODUCT))) 430endif 431`, 432 expected: `load("//build/make/core:product_config.rbc", "rblf") 433 434def init(g, handle): 435 cfg = rblf.cfg(handle) 436 if not rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]): 437 pass 438 if rblf.filter("userdebug", g["TARGET_BUILD_VARIANT"]): 439 pass 440 if "plaf" in g.get("PLATFORM_LIST", []): 441 pass 442 if g["TARGET_BUILD_VARIANT"] == " ".join(rblf.filter(g["TARGET_BUILD_VARIANT"], "userdebug eng")): 443 pass 444 if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]: 445 pass 446 if rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]): 447 pass 448 if rblf.filter("true", "%s%s" % (_v1, _v2)): 449 pass 450 if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]): 451 pass 452 elif rblf.filter("barbet%", g["TARGET_PRODUCT"]): 453 pass 454 if not rblf.filter_out("sunfish_kasan", g["TARGET_PRODUCT"]): 455 pass 456`, 457 }, 458 { 459 desc: "Get filter result", 460 mkname: "product.mk", 461 in: ` 462PRODUCT_LIST2=$(filter-out %/foo.ko,$(wildcard path/*.ko)) 463`, 464 expected: `load("//build/make/core:product_config.rbc", "rblf") 465 466def init(g, handle): 467 cfg = rblf.cfg(handle) 468 cfg["PRODUCT_LIST2"] = rblf.filter_out("%/foo.ko", rblf.expand_wildcard("path/*.ko")) 469`, 470 }, 471 { 472 desc: "filter $(VAR), values", 473 mkname: "product.mk", 474 in: ` 475ifeq (,$(filter $(TARGET_PRODUCT), yukawa_gms yukawa_sei510_gms) 476 ifneq (,$(filter $(TARGET_PRODUCT), yukawa_gms) 477 endif 478endif 479 480`, 481 expected: `load("//build/make/core:product_config.rbc", "rblf") 482 483def init(g, handle): 484 cfg = rblf.cfg(handle) 485 if g["TARGET_PRODUCT"] not in ["yukawa_gms", "yukawa_sei510_gms"]: 486 if g["TARGET_PRODUCT"] == "yukawa_gms": 487 pass 488`, 489 }, 490 { 491 desc: "filter $(V1), $(V2)", 492 mkname: "product.mk", 493 in: ` 494ifneq (, $(filter $(PRODUCT_LIST), $(TARGET_PRODUCT))) 495endif 496`, 497 expected: `load("//build/make/core:product_config.rbc", "rblf") 498 499def init(g, handle): 500 cfg = rblf.cfg(handle) 501 if rblf.filter(g.get("PRODUCT_LIST", []), g["TARGET_PRODUCT"]): 502 pass 503`, 504 }, 505 { 506 desc: "ifeq", 507 mkname: "product.mk", 508 in: ` 509ifeq (aosp, $(TARGET_PRODUCT)) # Comment 510else ifneq (, $(TARGET_PRODUCT)) 511endif 512`, 513 expected: `load("//build/make/core:product_config.rbc", "rblf") 514 515def init(g, handle): 516 cfg = rblf.cfg(handle) 517 if "aosp" == g["TARGET_PRODUCT"]: 518 # Comment 519 pass 520 elif g["TARGET_PRODUCT"]: 521 pass 522`, 523 }, 524 { 525 desc: "Nested if", 526 mkname: "product.mk", 527 in: ` 528ifdef PRODUCT_NAME 529 PRODUCT_PACKAGES = pack-if0 530 ifdef PRODUCT_MODEL 531 PRODUCT_PACKAGES = pack-if-if 532 else ifdef PRODUCT_NAME 533 PRODUCT_PACKAGES = pack-if-elif 534 else 535 PRODUCT_PACKAGES = pack-if-else 536 endif 537 PRODUCT_PACKAGES = pack-if 538else ifneq (,$(TARGET_PRODUCT)) 539 PRODUCT_PACKAGES = pack-elif 540else 541 PRODUCT_PACKAGES = pack-else 542endif 543`, 544 expected: `load("//build/make/core:product_config.rbc", "rblf") 545 546def init(g, handle): 547 cfg = rblf.cfg(handle) 548 if cfg.get("PRODUCT_NAME", ""): 549 cfg["PRODUCT_PACKAGES"] = ["pack-if0"] 550 if cfg.get("PRODUCT_MODEL", ""): 551 cfg["PRODUCT_PACKAGES"] = ["pack-if-if"] 552 elif cfg.get("PRODUCT_NAME", ""): 553 cfg["PRODUCT_PACKAGES"] = ["pack-if-elif"] 554 else: 555 cfg["PRODUCT_PACKAGES"] = ["pack-if-else"] 556 cfg["PRODUCT_PACKAGES"] = ["pack-if"] 557 elif g["TARGET_PRODUCT"]: 558 cfg["PRODUCT_PACKAGES"] = ["pack-elif"] 559 else: 560 cfg["PRODUCT_PACKAGES"] = ["pack-else"] 561`, 562 }, 563 { 564 desc: "Wildcard", 565 mkname: "product.mk", 566 in: ` 567ifeq (,$(wildcard foo.mk)) 568endif 569ifneq (,$(wildcard foo*.mk)) 570endif 571ifeq (foo1.mk foo2.mk barxyz.mk,$(wildcard foo*.mk bar*.mk)) 572endif 573`, 574 expected: `load("//build/make/core:product_config.rbc", "rblf") 575 576def init(g, handle): 577 cfg = rblf.cfg(handle) 578 if not rblf.expand_wildcard("foo.mk"): 579 pass 580 if rblf.expand_wildcard("foo*.mk"): 581 pass 582 if rblf.expand_wildcard("foo*.mk bar*.mk") == ["foo1.mk", "foo2.mk", "barxyz.mk"]: 583 pass 584`, 585 }, 586 { 587 desc: "if with interpolation", 588 mkname: "product.mk", 589 in: ` 590ifeq ($(VARIABLE1)text$(VARIABLE2),true) 591endif 592`, 593 expected: `load("//build/make/core:product_config.rbc", "rblf") 594 595def init(g, handle): 596 cfg = rblf.cfg(handle) 597 if "%stext%s" % (g.get("VARIABLE1", ""), g.get("VARIABLE2", "")) == "true": 598 pass 599`, 600 }, 601 { 602 desc: "ifneq $(X),true", 603 mkname: "product.mk", 604 in: ` 605ifneq ($(VARIABLE),true) 606endif 607`, 608 expected: `load("//build/make/core:product_config.rbc", "rblf") 609 610def init(g, handle): 611 cfg = rblf.cfg(handle) 612 if g.get("VARIABLE", "") != "true": 613 pass 614`, 615 }, 616 { 617 desc: "Const neq", 618 mkname: "product.mk", 619 in: ` 620ifneq (1,0) 621endif 622`, 623 expected: `load("//build/make/core:product_config.rbc", "rblf") 624 625def init(g, handle): 626 cfg = rblf.cfg(handle) 627 if "1" != "0": 628 pass 629`, 630 }, 631 { 632 desc: "is-board calls", 633 mkname: "product.mk", 634 in: ` 635ifeq ($(call is-board-platform-in-list,msm8998), true) 636else ifneq ($(call is-board-platform,copper),true) 637else ifneq ($(call is-vendor-board-platform,QCOM),true) 638else ifeq ($(call is-product-in-list, $(PLATFORM_LIST)), true) 639endif 640`, 641 expected: `load("//build/make/core:product_config.rbc", "rblf") 642 643def init(g, handle): 644 cfg = rblf.cfg(handle) 645 if rblf.board_platform_in(g, "msm8998"): 646 pass 647 elif not rblf.board_platform_is(g, "copper"): 648 pass 649 elif g.get("TARGET_BOARD_PLATFORM", "") not in g.get("QCOM_BOARD_PLATFORMS", ""): 650 pass 651 elif g["TARGET_PRODUCT"] in g.get("PLATFORM_LIST", []): 652 pass 653`, 654 }, 655 { 656 desc: "new is-board calls", 657 mkname: "product.mk", 658 in: ` 659ifneq (,$(call is-board-platform-in-list2,msm8998 $(X)) 660else ifeq (,$(call is-board-platform2,copper) 661else ifneq (,$(call is-vendor-board-qcom)) 662endif 663`, 664 expected: `load("//build/make/core:product_config.rbc", "rblf") 665 666def init(g, handle): 667 cfg = rblf.cfg(handle) 668 if rblf.board_platform_in(g, "msm8998 %s" % g.get("X", "")): 669 pass 670 elif not rblf.board_platform_is(g, "copper"): 671 pass 672 elif g.get("TARGET_BOARD_PLATFORM", "") in g.get("QCOM_BOARD_PLATFORMS", ""): 673 pass 674`, 675 }, 676 { 677 desc: "findstring call", 678 mkname: "product.mk", 679 in: ` 680result := $(findstring a,a b c) 681result := $(findstring b,x y z) 682`, 683 expected: `load("//build/make/core:product_config.rbc", "rblf") 684 685def init(g, handle): 686 cfg = rblf.cfg(handle) 687 _result = rblf.findstring("a", "a b c") 688 _result = rblf.findstring("b", "x y z") 689`, 690 }, 691 { 692 desc: "findstring in if statement", 693 mkname: "product.mk", 694 in: ` 695ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),) 696endif 697ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),) 698endif 699ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),foo) 700endif 701ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),foo) 702endif 703`, 704 expected: `load("//build/make/core:product_config.rbc", "rblf") 705 706def init(g, handle): 707 cfg = rblf.cfg(handle) 708 if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1: 709 pass 710 if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1: 711 pass 712 if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1: 713 pass 714 if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1: 715 pass 716`, 717 }, 718 { 719 desc: "rhs call", 720 mkname: "product.mk", 721 in: ` 722PRODUCT_COPY_FILES = $(call add-to-product-copy-files-if-exists, path:distpath) \ 723 $(call find-copy-subdir-files, *, fromdir, todir) $(wildcard foo.*) 724`, 725 expected: `load("//build/make/core:product_config.rbc", "rblf") 726 727def init(g, handle): 728 cfg = rblf.cfg(handle) 729 cfg["PRODUCT_COPY_FILES"] = (rblf.copy_if_exists("path:distpath") + 730 rblf.find_and_copy("*", "fromdir", "todir") + 731 rblf.expand_wildcard("foo.*")) 732`, 733 }, 734 { 735 desc: "inferred type", 736 mkname: "product.mk", 737 in: ` 738HIKEY_MODS := $(wildcard foo/*.ko) 739BOARD_VENDOR_KERNEL_MODULES += $(HIKEY_MODS) 740`, 741 expected: `load("//build/make/core:product_config.rbc", "rblf") 742 743def init(g, handle): 744 cfg = rblf.cfg(handle) 745 g["HIKEY_MODS"] = rblf.expand_wildcard("foo/*.ko") 746 g.setdefault("BOARD_VENDOR_KERNEL_MODULES", []) 747 g["BOARD_VENDOR_KERNEL_MODULES"] += g["HIKEY_MODS"] 748`, 749 }, 750 { 751 desc: "list with vars", 752 mkname: "product.mk", 753 in: ` 754PRODUCT_COPY_FILES += path1:$(TARGET_PRODUCT)/path1 $(PRODUCT_MODEL)/path2:$(TARGET_PRODUCT)/path2 755`, 756 expected: `load("//build/make/core:product_config.rbc", "rblf") 757 758def init(g, handle): 759 cfg = rblf.cfg(handle) 760 rblf.setdefault(handle, "PRODUCT_COPY_FILES") 761 cfg["PRODUCT_COPY_FILES"] += (("path1:%s/path1" % g["TARGET_PRODUCT"]).split() + 762 ("%s/path2:%s/path2" % (cfg.get("PRODUCT_MODEL", ""), g["TARGET_PRODUCT"])).split()) 763`, 764 }, 765 { 766 desc: "misc calls", 767 mkname: "product.mk", 768 in: ` 769$(call enforce-product-packages-exist,) 770$(call enforce-product-packages-exist, foo) 771$(call require-artifacts-in-path, foo, bar) 772$(call require-artifacts-in-path-relaxed, foo, bar) 773$(call dist-for-goals, goal, from:to) 774$(call add-product-dex-preopt-module-config,MyModule,disable) 775`, 776 expected: `load("//build/make/core:product_config.rbc", "rblf") 777 778def init(g, handle): 779 cfg = rblf.cfg(handle) 780 rblf.enforce_product_packages_exist(handle, "") 781 rblf.enforce_product_packages_exist(handle, "foo") 782 rblf.require_artifacts_in_path(handle, "foo", "bar") 783 rblf.require_artifacts_in_path_relaxed(handle, "foo", "bar") 784 rblf.mkdist_for_goals(g, "goal", "from:to") 785 rblf.add_product_dex_preopt_module_config(handle, "MyModule", "disable") 786`, 787 }, 788 { 789 desc: "list with functions", 790 mkname: "product.mk", 791 in: ` 792PRODUCT_COPY_FILES := $(call find-copy-subdir-files,*.kl,from1,to1) \ 793 $(call find-copy-subdir-files,*.kc,from2,to2) \ 794 foo bar 795`, 796 expected: `load("//build/make/core:product_config.rbc", "rblf") 797 798def init(g, handle): 799 cfg = rblf.cfg(handle) 800 cfg["PRODUCT_COPY_FILES"] = (rblf.find_and_copy("*.kl", "from1", "to1") + 801 rblf.find_and_copy("*.kc", "from2", "to2") + 802 [ 803 "foo", 804 "bar", 805 ]) 806`, 807 }, 808 { 809 desc: "Text functions", 810 mkname: "product.mk", 811 in: ` 812PRODUCT_COPY_FILES := $(addprefix pfx-,a b c) 813PRODUCT_COPY_FILES := $(addsuffix .sff, a b c) 814PRODUCT_NAME := $(word 1, $(subst ., ,$(TARGET_BOARD_PLATFORM))) 815ifeq (1,$(words $(SOME_UNKNOWN_VARIABLE))) 816endif 817ifeq ($(words $(SOME_OTHER_VARIABLE)),$(SOME_INT_VARIABLE)) 818endif 819$(info $(patsubst %.pub,$(PRODUCT_NAME)%,$(PRODUCT_ADB_KEYS))) 820$(info $$(dir foo/bar): $(dir foo/bar)) 821$(info $(firstword $(PRODUCT_COPY_FILES))) 822$(info $(lastword $(PRODUCT_COPY_FILES))) 823$(info $(dir $(lastword $(MAKEFILE_LIST)))) 824$(info $(dir $(lastword $(PRODUCT_COPY_FILES)))) 825$(info $(dir $(lastword $(foobar)))) 826$(info $(abspath foo/bar)) 827$(info $(notdir foo/bar)) 828$(call add_soong_config_namespace,snsconfig) 829$(call add_soong_config_var_value,snsconfig,imagetype,odm_image) 830$(call soong_config_set, snsconfig, foo, foo_value) 831$(call soong_config_append, snsconfig, bar, bar_value) 832PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc) 833PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c) 834`, 835 expected: `load("//build/make/core:product_config.rbc", "rblf") 836 837def init(g, handle): 838 cfg = rblf.cfg(handle) 839 cfg["PRODUCT_COPY_FILES"] = rblf.addprefix("pfx-", "a b c") 840 cfg["PRODUCT_COPY_FILES"] = rblf.addsuffix(".sff", "a b c") 841 cfg["PRODUCT_NAME"] = rblf.words((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " "))[0] 842 if len(rblf.words(g.get("SOME_UNKNOWN_VARIABLE", ""))) == 1: 843 pass 844 if ("%d" % (len(rblf.words(g.get("SOME_OTHER_VARIABLE", ""))))) == g.get("SOME_INT_VARIABLE", ""): 845 pass 846 rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%s%%" % cfg["PRODUCT_NAME"], g.get("PRODUCT_ADB_KEYS", ""))) 847 rblf.mkinfo("product.mk", "$(dir foo/bar): %s" % rblf.dir("foo/bar")) 848 rblf.mkinfo("product.mk", rblf.first_word(cfg["PRODUCT_COPY_FILES"])) 849 rblf.mkinfo("product.mk", rblf.last_word(cfg["PRODUCT_COPY_FILES"])) 850 rblf.mkinfo("product.mk", rblf.dir(rblf.last_word("product.mk"))) 851 rblf.mkinfo("product.mk", rblf.dir(rblf.last_word(cfg["PRODUCT_COPY_FILES"]))) 852 rblf.mkinfo("product.mk", rblf.dir(rblf.last_word(_foobar))) 853 rblf.mkinfo("product.mk", rblf.abspath("foo/bar")) 854 rblf.mkinfo("product.mk", rblf.notdir("foo/bar")) 855 rblf.soong_config_namespace(g, "snsconfig") 856 rblf.soong_config_set(g, "snsconfig", "imagetype", "odm_image") 857 rblf.soong_config_set(g, "snsconfig", "foo", "foo_value") 858 rblf.soong_config_append(g, "snsconfig", "bar", "bar_value") 859 cfg["PRODUCT_COPY_FILES"] = rblf.copy_files(rblf.expand_wildcard("foo*.mk"), "etc") 860 cfg["PRODUCT_COPY_FILES"] = rblf.product_copy_files_by_pattern("from/%", "to/%", "a b c") 861`, 862 }, 863 { 864 desc: "subst in list", 865 mkname: "product.mk", 866 in: ` 867files = $(call find-copy-subdir-files,*,from,to) 868PRODUCT_COPY_FILES += $(subst foo,bar,$(files)) 869`, 870 expected: `load("//build/make/core:product_config.rbc", "rblf") 871 872def init(g, handle): 873 cfg = rblf.cfg(handle) 874 _files = rblf.find_and_copy("*", "from", "to") 875 rblf.setdefault(handle, "PRODUCT_COPY_FILES") 876 cfg["PRODUCT_COPY_FILES"] += rblf.mksubst("foo", "bar", _files) 877`, 878 }, 879 { 880 desc: "assignment flavors", 881 mkname: "product.mk", 882 in: ` 883PRODUCT_LIST1 := a 884PRODUCT_LIST2 += a 885PRODUCT_LIST1 += b 886PRODUCT_LIST2 += b 887PRODUCT_LIST3 ?= a 888PRODUCT_LIST1 = c 889PLATFORM_LIST += x 890PRODUCT_PACKAGES := $(PLATFORM_LIST) 891`, 892 expected: `load("//build/make/core:product_config.rbc", "rblf") 893 894def init(g, handle): 895 cfg = rblf.cfg(handle) 896 cfg["PRODUCT_LIST1"] = ["a"] 897 rblf.setdefault(handle, "PRODUCT_LIST2") 898 cfg["PRODUCT_LIST2"] += ["a"] 899 cfg["PRODUCT_LIST1"] += ["b"] 900 cfg["PRODUCT_LIST2"] += ["b"] 901 if cfg.get("PRODUCT_LIST3") == None: 902 cfg["PRODUCT_LIST3"] = ["a"] 903 cfg["PRODUCT_LIST1"] = ["c"] 904 g.setdefault("PLATFORM_LIST", []) 905 g["PLATFORM_LIST"] += ["x"] 906 cfg["PRODUCT_PACKAGES"] = g["PLATFORM_LIST"][:] 907`, 908 }, 909 { 910 desc: "assigment flavors2", 911 mkname: "product.mk", 912 in: ` 913PRODUCT_LIST1 = a 914ifeq (0,1) 915 PRODUCT_LIST1 += b 916 PRODUCT_LIST2 += b 917endif 918PRODUCT_LIST1 += c 919PRODUCT_LIST2 += c 920`, 921 expected: `load("//build/make/core:product_config.rbc", "rblf") 922 923def init(g, handle): 924 cfg = rblf.cfg(handle) 925 cfg["PRODUCT_LIST1"] = ["a"] 926 if "0" == "1": 927 cfg["PRODUCT_LIST1"] += ["b"] 928 rblf.setdefault(handle, "PRODUCT_LIST2") 929 cfg["PRODUCT_LIST2"] += ["b"] 930 cfg["PRODUCT_LIST1"] += ["c"] 931 rblf.setdefault(handle, "PRODUCT_LIST2") 932 cfg["PRODUCT_LIST2"] += ["c"] 933`, 934 }, 935 { 936 desc: "assigment setdefaults", 937 mkname: "product.mk", 938 in: ` 939# All of these should have a setdefault because they're self-referential and not defined before 940PRODUCT_LIST1 = a $(PRODUCT_LIST1) 941PRODUCT_LIST2 ?= a $(PRODUCT_LIST2) 942PRODUCT_LIST3 += a 943 944# Now doing them again should not have a setdefault because they've already been set 945PRODUCT_LIST1 = a $(PRODUCT_LIST1) 946PRODUCT_LIST2 ?= a $(PRODUCT_LIST2) 947PRODUCT_LIST3 += a 948`, 949 expected: `# All of these should have a setdefault because they're self-referential and not defined before 950load("//build/make/core:product_config.rbc", "rblf") 951 952def init(g, handle): 953 cfg = rblf.cfg(handle) 954 rblf.setdefault(handle, "PRODUCT_LIST1") 955 cfg["PRODUCT_LIST1"] = (["a"] + 956 cfg.get("PRODUCT_LIST1", [])) 957 if cfg.get("PRODUCT_LIST2") == None: 958 rblf.setdefault(handle, "PRODUCT_LIST2") 959 cfg["PRODUCT_LIST2"] = (["a"] + 960 cfg.get("PRODUCT_LIST2", [])) 961 rblf.setdefault(handle, "PRODUCT_LIST3") 962 cfg["PRODUCT_LIST3"] += ["a"] 963 # Now doing them again should not have a setdefault because they've already been set 964 cfg["PRODUCT_LIST1"] = (["a"] + 965 cfg["PRODUCT_LIST1"]) 966 if cfg.get("PRODUCT_LIST2") == None: 967 cfg["PRODUCT_LIST2"] = (["a"] + 968 cfg["PRODUCT_LIST2"]) 969 cfg["PRODUCT_LIST3"] += ["a"] 970`, 971 }, 972 { 973 desc: "soong namespace assignments", 974 mkname: "product.mk", 975 in: ` 976SOONG_CONFIG_NAMESPACES += cvd 977SOONG_CONFIG_cvd += launch_configs 978SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json 979SOONG_CONFIG_cvd += grub_config 980SOONG_CONFIG_cvd_grub_config += grub.cfg 981x := $(SOONG_CONFIG_cvd_grub_config) 982`, 983 expected: `load("//build/make/core:product_config.rbc", "rblf") 984 985def init(g, handle): 986 cfg = rblf.cfg(handle) 987 rblf.soong_config_namespace(g, "cvd") 988 rblf.soong_config_set(g, "cvd", "launch_configs", "cvd_config_auto.json") 989 rblf.soong_config_append(g, "cvd", "grub_config", "grub.cfg") 990 _x = rblf.mk2rbc_error("product.mk:7", "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: SOONG_CONFIG_cvd_grub_config") 991`, 992 }, { 993 desc: "soong namespace accesses", 994 mkname: "product.mk", 995 in: ` 996SOONG_CONFIG_NAMESPACES += cvd 997SOONG_CONFIG_cvd += launch_configs 998SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json 999SOONG_CONFIG_cvd += grub_config 1000SOONG_CONFIG_cvd_grub_config += grub.cfg 1001x := $(call soong_config_get,cvd,grub_config) 1002`, 1003 expected: `load("//build/make/core:product_config.rbc", "rblf") 1004 1005def init(g, handle): 1006 cfg = rblf.cfg(handle) 1007 rblf.soong_config_namespace(g, "cvd") 1008 rblf.soong_config_set(g, "cvd", "launch_configs", "cvd_config_auto.json") 1009 rblf.soong_config_append(g, "cvd", "grub_config", "grub.cfg") 1010 _x = rblf.soong_config_get(g, "cvd", "grub_config") 1011`, 1012 }, 1013 { 1014 desc: "string split", 1015 mkname: "product.mk", 1016 in: ` 1017PRODUCT_LIST1 = a 1018local = b 1019local += c 1020FOO = d 1021FOO += e 1022PRODUCT_LIST1 += $(local) 1023PRODUCT_LIST1 += $(FOO) 1024`, 1025 expected: `load("//build/make/core:product_config.rbc", "rblf") 1026 1027def init(g, handle): 1028 cfg = rblf.cfg(handle) 1029 cfg["PRODUCT_LIST1"] = ["a"] 1030 _local = "b" 1031 _local += " " + "c" 1032 g["FOO"] = "d" 1033 g["FOO"] += " " + "e" 1034 cfg["PRODUCT_LIST1"] += (_local).split() 1035 cfg["PRODUCT_LIST1"] += (g["FOO"]).split() 1036`, 1037 }, 1038 { 1039 desc: "apex_jars", 1040 mkname: "product.mk", 1041 in: ` 1042PRODUCT_BOOT_JARS := $(ART_APEX_JARS) framework-minus-apex 1043`, 1044 expected: `load("//build/make/core:product_config.rbc", "rblf") 1045 1046def init(g, handle): 1047 cfg = rblf.cfg(handle) 1048 cfg["PRODUCT_BOOT_JARS"] = (g.get("ART_APEX_JARS", []) + 1049 ["framework-minus-apex"]) 1050`, 1051 }, 1052 { 1053 desc: "strip/sort functions", 1054 mkname: "product.mk", 1055 in: ` 1056ifeq ($(filter hwaddress,$(PRODUCT_PACKAGES)),) 1057 PRODUCT_PACKAGES := $(strip $(PRODUCT_PACKAGES) hwaddress) 1058endif 1059MY_VAR := $(sort b a c) 1060`, 1061 expected: `load("//build/make/core:product_config.rbc", "rblf") 1062 1063def init(g, handle): 1064 cfg = rblf.cfg(handle) 1065 if "hwaddress" not in cfg.get("PRODUCT_PACKAGES", []): 1066 rblf.setdefault(handle, "PRODUCT_PACKAGES") 1067 cfg["PRODUCT_PACKAGES"] = (rblf.mkstrip("%s hwaddress" % " ".join(cfg.get("PRODUCT_PACKAGES", [])))).split() 1068 g["MY_VAR"] = rblf.mksort("b a c") 1069`, 1070 }, 1071 { 1072 desc: "strip func in condition", 1073 mkname: "product.mk", 1074 in: ` 1075ifneq ($(strip $(TARGET_VENDOR)),) 1076endif 1077`, 1078 expected: `load("//build/make/core:product_config.rbc", "rblf") 1079 1080def init(g, handle): 1081 cfg = rblf.cfg(handle) 1082 if rblf.mkstrip(g.get("TARGET_VENDOR", "")): 1083 pass 1084`, 1085 }, 1086 { 1087 desc: "ref after set", 1088 mkname: "product.mk", 1089 in: ` 1090PRODUCT_ADB_KEYS:=value 1091FOO := $(PRODUCT_ADB_KEYS) 1092ifneq (,$(PRODUCT_ADB_KEYS)) 1093endif 1094`, 1095 expected: `load("//build/make/core:product_config.rbc", "rblf") 1096 1097def init(g, handle): 1098 cfg = rblf.cfg(handle) 1099 g["PRODUCT_ADB_KEYS"] = "value" 1100 g["FOO"] = g["PRODUCT_ADB_KEYS"] 1101 if g["PRODUCT_ADB_KEYS"]: 1102 pass 1103`, 1104 }, 1105 { 1106 desc: "ref before set", 1107 mkname: "product.mk", 1108 in: ` 1109V1 := $(PRODUCT_ADB_KEYS) 1110ifeq (,$(PRODUCT_ADB_KEYS)) 1111 V2 := $(PRODUCT_ADB_KEYS) 1112 PRODUCT_ADB_KEYS:=foo 1113 V3 := $(PRODUCT_ADB_KEYS) 1114endif`, 1115 expected: `load("//build/make/core:product_config.rbc", "rblf") 1116 1117def init(g, handle): 1118 cfg = rblf.cfg(handle) 1119 g["V1"] = g.get("PRODUCT_ADB_KEYS", "") 1120 if not g.get("PRODUCT_ADB_KEYS", ""): 1121 g["V2"] = g.get("PRODUCT_ADB_KEYS", "") 1122 g["PRODUCT_ADB_KEYS"] = "foo" 1123 g["V3"] = g["PRODUCT_ADB_KEYS"] 1124`, 1125 }, 1126 { 1127 desc: "Dynamic inherit path", 1128 mkname: "product.mk", 1129 in: ` 1130MY_PATH:=foo 1131$(call inherit-product,vendor/$(MY_PATH)/cfg.mk) 1132`, 1133 expected: `load("//build/make/core:product_config.rbc", "rblf") 1134load("//vendor/foo1:cfg.star|init", _cfg_init = "init") 1135load("//vendor/bar/baz:cfg.star|init", _cfg1_init = "init") 1136 1137def init(g, handle): 1138 cfg = rblf.cfg(handle) 1139 g["MY_PATH"] = "foo" 1140 _entry = { 1141 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1142 "vendor/bar/baz/cfg.mk": ("vendor/bar/baz/cfg", _cfg1_init), 1143 }.get("vendor/%s/cfg.mk" % g["MY_PATH"]) 1144 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1145 if not _varmod_init: 1146 rblf.mkerror("product.mk", "Cannot find %s" % ("vendor/%s/cfg.mk" % g["MY_PATH"])) 1147 rblf.inherit(handle, _varmod, _varmod_init) 1148`, 1149 }, 1150 { 1151 desc: "Dynamic inherit with hint", 1152 mkname: "product.mk", 1153 in: ` 1154MY_PATH:=foo 1155#RBC# include_top vendor/foo1 1156$(call inherit-product,$(MY_PATH)/cfg.mk) 1157#RBC# include_top vendor/foo1 1158$(call inherit-product,$(MY_OTHER_PATH)) 1159#RBC# include_top vendor/foo1 1160$(call inherit-product,vendor/$(MY_OTHER_PATH)) 1161#RBC# include_top vendor/foo1 1162$(foreach f,$(MY_MAKEFILES), \ 1163 $(call inherit-product,$(f))) 1164`, 1165 expected: `load("//build/make/core:product_config.rbc", "rblf") 1166load("//vendor/foo1:cfg.star|init", _cfg_init = "init") 1167 1168def init(g, handle): 1169 cfg = rblf.cfg(handle) 1170 g["MY_PATH"] = "foo" 1171 _entry = { 1172 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1173 }.get("%s/cfg.mk" % g["MY_PATH"]) 1174 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1175 if not _varmod_init: 1176 rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"])) 1177 rblf.inherit(handle, _varmod, _varmod_init) 1178 _entry = { 1179 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1180 }.get(g.get("MY_OTHER_PATH", "")) 1181 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1182 if not _varmod_init: 1183 rblf.mkerror("product.mk", "Cannot find %s" % (g.get("MY_OTHER_PATH", ""))) 1184 rblf.inherit(handle, _varmod, _varmod_init) 1185 _entry = { 1186 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1187 }.get("vendor/%s" % g.get("MY_OTHER_PATH", "")) 1188 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1189 if not _varmod_init: 1190 rblf.mkerror("product.mk", "Cannot find %s" % ("vendor/%s" % g.get("MY_OTHER_PATH", ""))) 1191 rblf.inherit(handle, _varmod, _varmod_init) 1192 for f in rblf.words(g.get("MY_MAKEFILES", "")): 1193 _entry = { 1194 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1195 }.get(f) 1196 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1197 if not _varmod_init: 1198 rblf.mkerror("product.mk", "Cannot find %s" % (f)) 1199 rblf.inherit(handle, _varmod, _varmod_init) 1200`, 1201 }, 1202 { 1203 desc: "Dynamic inherit with duplicated hint", 1204 mkname: "product.mk", 1205 in: ` 1206MY_PATH:=foo 1207#RBC# include_top vendor/foo1 1208$(call inherit-product,$(MY_PATH)/cfg.mk) 1209#RBC# include_top vendor/foo1 1210#RBC# include_top vendor/foo1 1211$(call inherit-product,$(MY_PATH)/cfg.mk) 1212`, 1213 expected: `load("//build/make/core:product_config.rbc", "rblf") 1214load("//vendor/foo1:cfg.star|init", _cfg_init = "init") 1215 1216def init(g, handle): 1217 cfg = rblf.cfg(handle) 1218 g["MY_PATH"] = "foo" 1219 _entry = { 1220 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1221 }.get("%s/cfg.mk" % g["MY_PATH"]) 1222 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1223 if not _varmod_init: 1224 rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"])) 1225 rblf.inherit(handle, _varmod, _varmod_init) 1226 _entry = { 1227 "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init), 1228 }.get("%s/cfg.mk" % g["MY_PATH"]) 1229 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1230 if not _varmod_init: 1231 rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"])) 1232 rblf.inherit(handle, _varmod, _varmod_init) 1233`, 1234 }, 1235 { 1236 desc: "Dynamic inherit path that lacks hint", 1237 mkname: "product.mk", 1238 in: ` 1239#RBC# include_top foo 1240$(call inherit-product,$(MY_VAR)/font.mk) 1241 1242#RBC# include_top foo 1243 1244# There's some space and even this comment between the include_top and the inherit-product 1245 1246$(call inherit-product,$(MY_VAR)/font.mk) 1247 1248$(call inherit-product,$(MY_VAR)/font.mk) 1249`, 1250 expected: `load("//build/make/core:product_config.rbc", "rblf") 1251load("//foo:font.star|init", _font_init = "init") 1252load("//bar:font.star|init", _font1_init = "init") 1253 1254def init(g, handle): 1255 cfg = rblf.cfg(handle) 1256 _entry = { 1257 "foo/font.mk": ("foo/font", _font_init), 1258 }.get("%s/font.mk" % g.get("MY_VAR", "")) 1259 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1260 if not _varmod_init: 1261 rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", ""))) 1262 rblf.inherit(handle, _varmod, _varmod_init) 1263 # There's some space and even this comment between the include_top and the inherit-product 1264 _entry = { 1265 "foo/font.mk": ("foo/font", _font_init), 1266 }.get("%s/font.mk" % g.get("MY_VAR", "")) 1267 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1268 if not _varmod_init: 1269 rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", ""))) 1270 rblf.inherit(handle, _varmod, _varmod_init) 1271 rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.") 1272 _entry = { 1273 "foo/font.mk": ("foo/font", _font_init), 1274 "bar/font.mk": ("bar/font", _font1_init), 1275 }.get("%s/font.mk" % g.get("MY_VAR", "")) 1276 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1277 if not _varmod_init: 1278 rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", ""))) 1279 rblf.inherit(handle, _varmod, _varmod_init) 1280`, 1281 }, 1282 { 1283 desc: "Ignore make rules", 1284 mkname: "product.mk", 1285 in: ` 1286foo: PRIVATE_VARIABLE = some_tool $< $@ 1287foo: foo.c 1288 gcc -o $@ $*`, 1289 expected: `load("//build/make/core:product_config.rbc", "rblf") 1290 1291def init(g, handle): 1292 cfg = rblf.cfg(handle) 1293 rblf.mk2rbc_error("product.mk:2", "Only simple variables are handled") 1294 rblf.mk2rbc_error("product.mk:3", "unsupported line rule: foo: foo.c\n#gcc -o $@ $*") 1295`, 1296 }, 1297 { 1298 desc: "Flag override", 1299 mkname: "product.mk", 1300 in: ` 1301override FOO:=`, 1302 expected: `load("//build/make/core:product_config.rbc", "rblf") 1303 1304def init(g, handle): 1305 cfg = rblf.cfg(handle) 1306 rblf.mk2rbc_error("product.mk:2", "cannot handle override directive") 1307`, 1308 }, 1309 { 1310 desc: "Bad expression", 1311 mkname: "build/product.mk", 1312 in: ` 1313ifeq (,$(call foobar)) 1314endif 1315my_sources := $(local-generated-sources-dir) 1316`, 1317 expected: `load("//build/make/core:product_config.rbc", "rblf") 1318 1319def init(g, handle): 1320 cfg = rblf.cfg(handle) 1321 if rblf.mk2rbc_error("build/product.mk:2", "cannot handle invoking foobar"): 1322 pass 1323 _my_sources = rblf.mk2rbc_error("build/product.mk:4", "local-generated-sources-dir is not supported") 1324`, 1325 }, 1326 { 1327 desc: "if expression", 1328 mkname: "product.mk", 1329 in: ` 1330TEST_VAR := foo 1331TEST_VAR_LIST := foo 1332TEST_VAR_LIST += bar 1333TEST_VAR_2 := $(if $(TEST_VAR),bar) 1334TEST_VAR_3 := $(if $(TEST_VAR),bar,baz) 1335TEST_VAR_4 := $(if $(TEST_VAR),$(TEST_VAR_LIST)) 1336`, 1337 expected: `load("//build/make/core:product_config.rbc", "rblf") 1338 1339def init(g, handle): 1340 cfg = rblf.cfg(handle) 1341 g["TEST_VAR"] = "foo" 1342 g["TEST_VAR_LIST"] = ["foo"] 1343 g["TEST_VAR_LIST"] += ["bar"] 1344 g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "") 1345 g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz") 1346 g["TEST_VAR_4"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else []) 1347`, 1348 }, 1349 { 1350 desc: "substitution references", 1351 mkname: "product.mk", 1352 in: ` 1353SOURCES := foo.c bar.c 1354OBJECTS := $(SOURCES:.c=.o) 1355OBJECTS2 := $(SOURCES:%.c=%.o) 1356`, 1357 expected: `load("//build/make/core:product_config.rbc", "rblf") 1358 1359def init(g, handle): 1360 cfg = rblf.cfg(handle) 1361 g["SOURCES"] = "foo.c bar.c" 1362 g["OBJECTS"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"]) 1363 g["OBJECTS2"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"]) 1364`, 1365 }, 1366 { 1367 desc: "foreach expressions", 1368 mkname: "product.mk", 1369 in: ` 1370BOOT_KERNEL_MODULES := foo.ko bar.ko 1371BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m)) 1372BOOT_KERNEL_MODULES_LIST := foo.ko 1373BOOT_KERNEL_MODULES_LIST += bar.ko 1374BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m)) 1375NESTED_LISTS := $(foreach m,$(SOME_VAR),$(BOOT_KERNEL_MODULES_LIST)) 1376NESTED_LISTS_2 := $(foreach x,$(SOME_VAR),$(foreach y,$(x),prefix$(y))) 1377 1378FOREACH_WITH_IF := $(foreach module,\ 1379 $(BOOT_KERNEL_MODULES_LIST),\ 1380 $(if $(filter $(module),foo.ko),,$(error module "$(module)" has an error!))) 1381 1382# Same as above, but not assigning it to a variable allows it to be converted to statements 1383$(foreach module,\ 1384 $(BOOT_KERNEL_MODULES_LIST),\ 1385 $(if $(filter $(module),foo.ko),,$(error module "$(module)" has an error!))) 1386`, 1387 expected: `load("//build/make/core:product_config.rbc", "rblf") 1388 1389def init(g, handle): 1390 cfg = rblf.cfg(handle) 1391 g["BOOT_KERNEL_MODULES"] = "foo.ko bar.ko" 1392 g["BOOT_KERNEL_MODULES_FILTER"] = ["%%/%s" % m for m in rblf.words(g["BOOT_KERNEL_MODULES"])] 1393 g["BOOT_KERNEL_MODULES_LIST"] = ["foo.ko"] 1394 g["BOOT_KERNEL_MODULES_LIST"] += ["bar.ko"] 1395 g["BOOT_KERNEL_MODULES_FILTER_2"] = ["%%/%s" % m for m in g["BOOT_KERNEL_MODULES_LIST"]] 1396 g["NESTED_LISTS"] = rblf.flatten_2d_list([g["BOOT_KERNEL_MODULES_LIST"] for m in rblf.words(g.get("SOME_VAR", ""))]) 1397 g["NESTED_LISTS_2"] = rblf.flatten_2d_list([["prefix%s" % y for y in rblf.words(x)] for x in rblf.words(g.get("SOME_VAR", ""))]) 1398 g["FOREACH_WITH_IF"] = [("" if rblf.filter(module, "foo.ko") else rblf.mkerror("product.mk", "module \"%s\" has an error!" % module)) for module in g["BOOT_KERNEL_MODULES_LIST"]] 1399 # Same as above, but not assigning it to a variable allows it to be converted to statements 1400 for module in g["BOOT_KERNEL_MODULES_LIST"]: 1401 if not rblf.filter(module, "foo.ko"): 1402 rblf.mkerror("product.mk", "module \"%s\" has an error!" % module) 1403`, 1404 }, 1405 { 1406 desc: "List appended to string", 1407 mkname: "product.mk", 1408 in: ` 1409NATIVE_BRIDGE_PRODUCT_PACKAGES := \ 1410 libnative_bridge_vdso.native_bridge \ 1411 native_bridge_guest_app_process.native_bridge \ 1412 native_bridge_guest_linker.native_bridge 1413 1414NATIVE_BRIDGE_MODIFIED_GUEST_LIBS := \ 1415 libaaudio \ 1416 libamidi \ 1417 libandroid \ 1418 libandroid_runtime 1419 1420NATIVE_BRIDGE_PRODUCT_PACKAGES += \ 1421 $(addsuffix .native_bridge,$(NATIVE_BRIDGE_ORIG_GUEST_LIBS)) 1422`, 1423 expected: `load("//build/make/core:product_config.rbc", "rblf") 1424 1425def init(g, handle): 1426 cfg = rblf.cfg(handle) 1427 g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] = "libnative_bridge_vdso.native_bridge native_bridge_guest_app_process.native_bridge native_bridge_guest_linker.native_bridge" 1428 g["NATIVE_BRIDGE_MODIFIED_GUEST_LIBS"] = "libaaudio libamidi libandroid libandroid_runtime" 1429 g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] += " " + " ".join(rblf.addsuffix(".native_bridge", g.get("NATIVE_BRIDGE_ORIG_GUEST_LIBS", ""))) 1430`, 1431 }, 1432 { 1433 desc: "Math functions", 1434 mkname: "product.mk", 1435 in: ` 1436# Test the math functions defined in build/make/common/math.mk 1437ifeq ($(call math_max,2,5),5) 1438endif 1439ifeq ($(call math_min,2,5),2) 1440endif 1441ifeq ($(call math_gt_or_eq,2,5),true) 1442endif 1443ifeq ($(call math_gt,2,5),true) 1444endif 1445ifeq ($(call math_lt,2,5),true) 1446endif 1447ifeq ($(call math_gt_or_eq,2,5),) 1448endif 1449ifeq ($(call math_gt,2,5),) 1450endif 1451ifeq ($(call math_lt,2,5),) 1452endif 1453ifeq ($(call math_gt_or_eq,$(MY_VAR), 5),true) 1454endif 1455ifeq ($(call math_gt_or_eq,$(MY_VAR),$(MY_OTHER_VAR)),true) 1456endif 1457ifeq ($(call math_gt_or_eq,100$(MY_VAR),10),true) 1458endif 1459`, 1460 expected: `# Test the math functions defined in build/make/common/math.mk 1461load("//build/make/core:product_config.rbc", "rblf") 1462 1463def init(g, handle): 1464 cfg = rblf.cfg(handle) 1465 if max(2, 5) == 5: 1466 pass 1467 if min(2, 5) == 2: 1468 pass 1469 if 2 >= 5: 1470 pass 1471 if 2 > 5: 1472 pass 1473 if 2 < 5: 1474 pass 1475 if 2 < 5: 1476 pass 1477 if 2 <= 5: 1478 pass 1479 if 2 >= 5: 1480 pass 1481 if int(g.get("MY_VAR", "")) >= 5: 1482 pass 1483 if int(g.get("MY_VAR", "")) >= int(g.get("MY_OTHER_VAR", "")): 1484 pass 1485 if int("100%s" % g.get("MY_VAR", "")) >= 10: 1486 pass 1487`, 1488 }, 1489 { 1490 desc: "Type hints", 1491 mkname: "product.mk", 1492 in: ` 1493# Test type hints 1494#RBC# type_hint list MY_VAR MY_VAR_2 1495# Unsupported type 1496#RBC# type_hint bool MY_VAR_3 1497# Invalid syntax 1498#RBC# type_hint list 1499# Duplicated variable 1500#RBC# type_hint list MY_VAR_2 1501#RBC# type_hint list my-local-var-with-dashes 1502#RBC# type_hint string MY_STRING_VAR 1503 1504MY_VAR := foo 1505MY_VAR_UNHINTED := foo 1506 1507# Vars set after other statements still get the hint 1508MY_VAR_2 := foo 1509 1510# You can't specify a type hint after the first statement 1511#RBC# type_hint list MY_VAR_4 1512MY_VAR_4 := foo 1513 1514my-local-var-with-dashes := foo 1515 1516MY_STRING_VAR := $(wildcard foo/bar.mk) 1517`, 1518 expected: `# Test type hints 1519# Unsupported type 1520load("//build/make/core:product_config.rbc", "rblf") 1521 1522def init(g, handle): 1523 cfg = rblf.cfg(handle) 1524 rblf.mk2rbc_error("product.mk:5", "Invalid type_hint annotation. Only list/string types are accepted, found bool") 1525 # Invalid syntax 1526 rblf.mk2rbc_error("product.mk:7", "Invalid type_hint annotation: list. Must be a variable type followed by a list of variables of that type") 1527 # Duplicated variable 1528 rblf.mk2rbc_error("product.mk:9", "Duplicate type hint for variable MY_VAR_2") 1529 g["MY_VAR"] = ["foo"] 1530 g["MY_VAR_UNHINTED"] = "foo" 1531 # Vars set after other statements still get the hint 1532 g["MY_VAR_2"] = ["foo"] 1533 # You can't specify a type hint after the first statement 1534 rblf.mk2rbc_error("product.mk:20", "type_hint annotations must come before the first Makefile statement") 1535 g["MY_VAR_4"] = "foo" 1536 _my_local_var_with_dashes = ["foo"] 1537 g["MY_STRING_VAR"] = " ".join(rblf.expand_wildcard("foo/bar.mk")) 1538`, 1539 }, 1540 { 1541 desc: "Set LOCAL_PATH to my-dir", 1542 mkname: "product.mk", 1543 in: ` 1544LOCAL_PATH := $(call my-dir) 1545`, 1546 expected: `load("//build/make/core:product_config.rbc", "rblf") 1547 1548def init(g, handle): 1549 cfg = rblf.cfg(handle) 1550 1551`, 1552 }, 1553 { 1554 desc: "Evals", 1555 mkname: "product.mk", 1556 in: ` 1557$(eval) 1558$(eval MY_VAR := foo) 1559$(eval # This is a test of eval functions) 1560$(eval $(TOO_COMPLICATED) := bar) 1561$(eval include foo/font.mk) 1562$(eval $(call inherit-product,vendor/foo1/cfg.mk)) 1563 1564$(foreach x,$(MY_LIST_VAR), \ 1565 $(eval PRODUCT_COPY_FILES += foo/bar/$(x):$(TARGET_COPY_OUT_VENDOR)/etc/$(x)) \ 1566 $(if $(MY_OTHER_VAR),$(eval PRODUCT_COPY_FILES += $(MY_OTHER_VAR):foo/bar/$(x)))) 1567 1568$(foreach x,$(MY_LIST_VAR), \ 1569 $(eval include foo/$(x).mk)) 1570 1571# Check that we get as least close to correct line numbers for errors on statements inside evals 1572$(eval $(call inherit-product,$(A_VAR))) 1573`, 1574 expected: `load("//build/make/core:product_config.rbc", "rblf") 1575load("//foo:font.star", _font_init = "init") 1576load("//vendor/foo1:cfg.star", _cfg_init = "init") 1577 1578def init(g, handle): 1579 cfg = rblf.cfg(handle) 1580 g["MY_VAR"] = "foo" 1581 # This is a test of eval functions 1582 rblf.mk2rbc_error("product.mk:5", "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported") 1583 _font_init(g, handle) 1584 rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init) 1585 for x in rblf.words(g.get("MY_LIST_VAR", "")): 1586 rblf.setdefault(handle, "PRODUCT_COPY_FILES") 1587 cfg["PRODUCT_COPY_FILES"] += ("foo/bar/%s:%s/etc/%s" % (x, g.get("TARGET_COPY_OUT_VENDOR", ""), x)).split() 1588 if g.get("MY_OTHER_VAR", ""): 1589 cfg["PRODUCT_COPY_FILES"] += ("%s:foo/bar/%s" % (g.get("MY_OTHER_VAR", ""), x)).split() 1590 for x in rblf.words(g.get("MY_LIST_VAR", "")): 1591 _entry = { 1592 "foo/font.mk": ("foo/font", _font_init), 1593 }.get("foo/%s.mk" % x) 1594 (_varmod, _varmod_init) = _entry if _entry else (None, None) 1595 if not _varmod_init: 1596 rblf.mkerror("product.mk", "Cannot find %s" % ("foo/%s.mk" % x)) 1597 _varmod_init(g, handle) 1598 # Check that we get as least close to correct line numbers for errors on statements inside evals 1599 rblf.mk2rbc_error("product.mk:17", "inherit-product/include argument is too complex") 1600`, 1601 }, 1602 { 1603 desc: ".KATI_READONLY", 1604 mkname: "product.mk", 1605 in: ` 1606MY_VAR := foo 1607.KATI_READONLY := MY_VAR 1608`, 1609 expected: `load("//build/make/core:product_config.rbc", "rblf") 1610 1611def init(g, handle): 1612 cfg = rblf.cfg(handle) 1613 g["MY_VAR"] = "foo" 1614`, 1615 }, 1616 { 1617 desc: "Complicated variable references", 1618 mkname: "product.mk", 1619 in: ` 1620MY_VAR := foo 1621MY_VAR_2 := MY_VAR 1622MY_VAR_3 := $($(MY_VAR_2)) 1623MY_VAR_4 := $(foo bar) 1624MY_VAR_5 := $($(MY_VAR_2) bar) 1625`, 1626 expected: `load("//build/make/core:product_config.rbc", "rblf") 1627 1628def init(g, handle): 1629 cfg = rblf.cfg(handle) 1630 g["MY_VAR"] = "foo" 1631 g["MY_VAR_2"] = "MY_VAR" 1632 g["MY_VAR_3"] = (cfg).get(g["MY_VAR_2"], (g).get(g["MY_VAR_2"], "")) 1633 g["MY_VAR_4"] = rblf.mk2rbc_error("product.mk:5", "cannot handle invoking foo") 1634 g["MY_VAR_5"] = rblf.mk2rbc_error("product.mk:6", "reference is too complex: $(MY_VAR_2) bar") 1635`, 1636 }, 1637 { 1638 desc: "Conditional functions", 1639 mkname: "product.mk", 1640 in: ` 1641B := foo 1642X := $(or $(A)) 1643X := $(or $(A),$(B)) 1644X := $(or $(A),$(B),$(C)) 1645X := $(and $(A)) 1646X := $(and $(A),$(B)) 1647X := $(and $(A),$(B),$(C)) 1648X := $(or $(A),$(B)) Y 1649 1650D := $(wildcard *.mk) 1651X := $(or $(B),$(D)) 1652`, 1653 expected: `load("//build/make/core:product_config.rbc", "rblf") 1654 1655def init(g, handle): 1656 cfg = rblf.cfg(handle) 1657 g["B"] = "foo" 1658 g["X"] = g.get("A", "") 1659 g["X"] = g.get("A", "") or g["B"] 1660 g["X"] = g.get("A", "") or g["B"] or g.get("C", "") 1661 g["X"] = g.get("A", "") 1662 g["X"] = g.get("A", "") and g["B"] 1663 g["X"] = g.get("A", "") and g["B"] and g.get("C", "") 1664 g["X"] = "%s Y" % g.get("A", "") or g["B"] 1665 g["D"] = rblf.expand_wildcard("*.mk") 1666 g["X"] = rblf.mk2rbc_error("product.mk:12", "Expected all arguments to $(or) or $(and) to have the same type, found \"string\" and \"list\"") 1667`, 1668 }, 1669 { 1670 1671 desc: "is-lower/is-upper", 1672 mkname: "product.mk", 1673 in: ` 1674X := $(call to-lower,aBc) 1675X := $(call to-upper,aBc) 1676X := $(call to-lower,$(VAR)) 1677X := $(call to-upper,$(VAR)) 1678`, 1679 expected: `load("//build/make/core:product_config.rbc", "rblf") 1680 1681def init(g, handle): 1682 cfg = rblf.cfg(handle) 1683 g["X"] = ("aBc").lower() 1684 g["X"] = ("aBc").upper() 1685 g["X"] = (g.get("VAR", "")).lower() 1686 g["X"] = (g.get("VAR", "")).upper() 1687`, 1688 }, 1689} 1690 1691var known_variables = []struct { 1692 name string 1693 class varClass 1694 starlarkType 1695}{ 1696 {"NATIVE_COVERAGE", VarClassSoong, starlarkTypeBool}, 1697 {"PRODUCT_NAME", VarClassConfig, starlarkTypeString}, 1698 {"PRODUCT_MODEL", VarClassConfig, starlarkTypeString}, 1699 {"PRODUCT_PACKAGES", VarClassConfig, starlarkTypeList}, 1700 {"PRODUCT_BOOT_JARS", VarClassConfig, starlarkTypeList}, 1701 {"PRODUCT_COPY_FILES", VarClassConfig, starlarkTypeList}, 1702 {"PRODUCT_IS_64BIT", VarClassConfig, starlarkTypeString}, 1703 {"PRODUCT_LIST1", VarClassConfig, starlarkTypeList}, 1704 {"PRODUCT_LIST2", VarClassConfig, starlarkTypeList}, 1705 {"PRODUCT_LIST3", VarClassConfig, starlarkTypeList}, 1706 {"TARGET_PRODUCT", VarClassSoong, starlarkTypeString}, 1707 {"TARGET_BUILD_VARIANT", VarClassSoong, starlarkTypeString}, 1708 {"TARGET_BOARD_PLATFORM", VarClassSoong, starlarkTypeString}, 1709 {"QCOM_BOARD_PLATFORMS", VarClassSoong, starlarkTypeString}, 1710 {"PLATFORM_LIST", VarClassSoong, starlarkTypeList}, // TODO(asmundak): make it local instead of soong 1711} 1712 1713type testMakefileFinder struct { 1714 fs fs.FS 1715 root string 1716 files []string 1717} 1718 1719func (t *testMakefileFinder) Find(root string) []string { 1720 if t.files != nil || root == t.root { 1721 return t.files 1722 } 1723 t.files = make([]string, 0) 1724 fs.WalkDir(t.fs, root, func(path string, d fs.DirEntry, err error) error { 1725 if err != nil { 1726 return err 1727 } 1728 if d.IsDir() { 1729 base := filepath.Base(path) 1730 if base[0] == '.' && len(base) > 1 { 1731 return fs.SkipDir 1732 } 1733 return nil 1734 } 1735 if strings.HasSuffix(path, ".mk") { 1736 t.files = append(t.files, path) 1737 } 1738 return nil 1739 }) 1740 return t.files 1741} 1742 1743func TestGood(t *testing.T) { 1744 for _, v := range known_variables { 1745 KnownVariables.NewVariable(v.name, v.class, v.starlarkType) 1746 } 1747 fs := NewFindMockFS([]string{ 1748 "vendor/foo1/cfg.mk", 1749 "vendor/bar/baz/cfg.mk", 1750 "part.mk", 1751 "foo/font.mk", 1752 "bar/font.mk", 1753 }) 1754 for _, test := range testCases { 1755 t.Run(test.desc, 1756 func(t *testing.T) { 1757 ss, err := Convert(Request{ 1758 MkFile: test.mkname, 1759 Reader: bytes.NewBufferString(test.in), 1760 OutputSuffix: ".star", 1761 SourceFS: fs, 1762 MakefileFinder: &testMakefileFinder{fs: fs}, 1763 }) 1764 if err != nil { 1765 t.Error(err) 1766 return 1767 } 1768 got := ss.String() 1769 if got != test.expected { 1770 t.Errorf("%q failed\nExpected:\n%s\nActual:\n%s\n", test.desc, 1771 strings.ReplaceAll(test.expected, "\n", "\n"), 1772 strings.ReplaceAll(got, "\n", "\n")) 1773 } 1774 }) 1775 } 1776} 1777