Files
build_soong/mk2rbc/mk2rbc_test.go
Cole Faust dd569aea07 Return starlarkNodes from the functions that parse them
Currently, mk2rbc is structured around having a global
"receiver" object that accepts all starlarkNodes. As soon
as they are parsed they are added to the receiver.

Returning the parsed nodes to the calling function is more
flexible, as it allows the calling function to restructure
them as necessary. This is the first step to supporting
complicated statements involving $(eval), such as
`$(foreach v,$(MY_LIST),$(eval MY_LIST_2 += $(v)))`

Test: go test
Change-Id: Ia194123cf090d2b9559a25b975ccbc5749357cc0
2022-02-02 14:59:59 -08:00

1408 lines
35 KiB
Go

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mk2rbc
import (
"bytes"
"io/fs"
"path/filepath"
"strings"
"testing"
)
var testCases = []struct {
desc string
mkname string
in string
expected string
}{
{
desc: "Comment",
mkname: "product.mk",
in: `
# Comment
# FOO= a\
b
`,
expected: `# Comment
# FOO= a
# b
load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
`,
},
{
desc: "Name conversion",
mkname: "path/bar-baz.mk",
in: `
# Comment
`,
expected: `# Comment
load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
`,
},
{
desc: "Item variable",
mkname: "pixel3.mk",
in: `
PRODUCT_NAME := Pixel 3
PRODUCT_MODEL :=
local_var = foo
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_NAME"] = "Pixel 3"
cfg["PRODUCT_MODEL"] = ""
_local_var = "foo"
`,
},
{
desc: "List variable",
mkname: "pixel4.mk",
in: `
PRODUCT_PACKAGES = package1 package2
PRODUCT_COPY_FILES += file2:target
PRODUCT_PACKAGES += package3
PRODUCT_COPY_FILES =
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_PACKAGES"] = [
"package1",
"package2",
]
rblf.setdefault(handle, "PRODUCT_COPY_FILES")
cfg["PRODUCT_COPY_FILES"] += ["file2:target"]
cfg["PRODUCT_PACKAGES"] += ["package3"]
cfg["PRODUCT_COPY_FILES"] = []
`,
},
{
desc: "Unknown function",
mkname: "product.mk",
in: `
PRODUCT_NAME := $(call foo1, bar)
PRODUCT_NAME := $(call foo0)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.mk2rbc_error("product.mk:2", "cannot handle invoking foo1")
rblf.mk2rbc_error("product.mk:3", "cannot handle invoking foo0")
`,
},
{
desc: "Inherit configuration always",
mkname: "product.mk",
in: `
$(call inherit-product, part.mk)
ifdef PRODUCT_NAME
$(call inherit-product, part1.mk)
else # Comment
$(call inherit-product, $(LOCAL_PATH)/part.mk)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load(":part.star", _part_init = "init")
load(":part1.star|init", _part1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.inherit(handle, "part", _part_init)
if cfg.get("PRODUCT_NAME", ""):
if not _part1_init:
rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
rblf.inherit(handle, "part1", _part1_init)
else:
# Comment
rblf.inherit(handle, "part", _part_init)
`,
},
{
desc: "Inherit configuration if it exists",
mkname: "product.mk",
in: `
$(call inherit-product-if-exists, part.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load(":part.star|init", _part_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
if _part_init:
rblf.inherit(handle, "part", _part_init)
`,
},
{
desc: "Include configuration",
mkname: "product.mk",
in: `
include part.mk
ifdef PRODUCT_NAME
include part1.mk
else
-include $(LOCAL_PATH)/part1.mk)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load(":part.star", _part_init = "init")
load(":part1.star|init", _part1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
_part_init(g, handle)
if cfg.get("PRODUCT_NAME", ""):
if not _part1_init:
rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
_part1_init(g, handle)
else:
if _part1_init != None:
_part1_init(g, handle)
`,
},
{
desc: "Synonymous inherited configurations",
mkname: "path/product.mk",
in: `
$(call inherit-product, */font.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//foo:font.star", _font_init = "init")
load("//bar:font.star", _font1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.inherit(handle, "foo/font", _font_init)
rblf.inherit(handle, "bar/font", _font1_init)
`,
},
{
desc: "Directive define",
mkname: "product.mk",
in: `
define some-macro
$(info foo)
endef
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.mk2rbc_error("product.mk:2", "define is not supported: some-macro")
`,
},
{
desc: "Ifdef",
mkname: "product.mk",
in: `
ifdef PRODUCT_NAME
PRODUCT_NAME = gizmo
else
endif
local_var :=
ifdef local_var
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo"
else:
pass
_local_var = ""
if _local_var:
pass
`,
},
{
desc: "Simple functions",
mkname: "product.mk",
in: `
$(warning this is the warning)
$(warning)
$(info this is the info)
$(error this is the error)
PRODUCT_NAME:=$(shell echo *)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.mkwarning("product.mk", "this is the warning")
rblf.mkwarning("product.mk", "")
rblf.mkinfo("product.mk", "this is the info")
rblf.mkerror("product.mk", "this is the error")
cfg["PRODUCT_NAME"] = rblf.shell("echo *")
`,
},
{
desc: "Empty if",
mkname: "product.mk",
in: `
ifdef PRODUCT_NAME
# Comment
else
TARGET_COPY_OUT_RECOVERY := foo
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if cfg.get("PRODUCT_NAME", ""):
# Comment
pass
else:
rblf.mk2rbc_error("product.mk:5", "cannot set predefined variable TARGET_COPY_OUT_RECOVERY to \"foo\", its value should be \"recovery\"")
`,
},
{
desc: "if/else/endif",
mkname: "product.mk",
in: `
ifndef PRODUCT_NAME
PRODUCT_NAME=gizmo1
else
PRODUCT_NAME=gizmo2
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if not cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo1"
else:
cfg["PRODUCT_NAME"] = "gizmo2"
`,
},
{
desc: "else if",
mkname: "product.mk",
in: `
ifdef PRODUCT_NAME
PRODUCT_NAME = gizmo
else ifndef PRODUCT_PACKAGES # Comment
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_NAME"] = "gizmo"
elif not cfg.get("PRODUCT_PACKAGES", []):
# Comment
pass
`,
},
{
desc: "ifeq / ifneq",
mkname: "product.mk",
in: `
ifeq (aosp_arm, $(TARGET_PRODUCT))
PRODUCT_MODEL = pix2
else
PRODUCT_MODEL = pix21
endif
ifneq (aosp_x86, $(TARGET_PRODUCT))
PRODUCT_MODEL = pix3
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "aosp_arm" == g["TARGET_PRODUCT"]:
cfg["PRODUCT_MODEL"] = "pix2"
else:
cfg["PRODUCT_MODEL"] = "pix21"
if "aosp_x86" != g["TARGET_PRODUCT"]:
cfg["PRODUCT_MODEL"] = "pix3"
`,
},
{
desc: "ifeq with soong_config_get",
mkname: "product.mk",
in: `
ifeq (true,$(call soong_config_get,art_module,source_build))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "true" == rblf.soong_config_get(g, "art_module", "source_build"):
pass
`,
},
{
desc: "ifeq with $(NATIVE_COVERAGE)",
mkname: "product.mk",
in: `
ifeq ($(NATIVE_COVERAGE),true)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if g.get("NATIVE_COVERAGE", False):
pass
`,
},
{
desc: "Check filter result",
mkname: "product.mk",
in: `
ifeq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
endif
ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT))
endif
ifneq (,$(filter plaf,$(PLATFORM_LIST)))
endif
ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
endif
ifneq (,$(filter true, $(v1)$(v2)))
endif
ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT)))
else ifneq (,$(filter barbet%,$(TARGET_PRODUCT)))
endif
ifeq (,$(filter-out sunfish_kasan, $(TARGET_PRODUCT)))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if not rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
pass
if rblf.filter("userdebug", g["TARGET_BUILD_VARIANT"]):
pass
if "plaf" in g.get("PLATFORM_LIST", []):
pass
if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
pass
if rblf.filter("true", "%s%s" % (_v1, _v2)):
pass
if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]):
pass
elif rblf.filter("barbet%", g["TARGET_PRODUCT"]):
pass
if not rblf.filter_out("sunfish_kasan", g["TARGET_PRODUCT"]):
pass
`,
},
{
desc: "Get filter result",
mkname: "product.mk",
in: `
PRODUCT_LIST2=$(filter-out %/foo.ko,$(wildcard path/*.ko))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_LIST2"] = rblf.filter_out("%/foo.ko", rblf.expand_wildcard("path/*.ko"))
`,
},
{
desc: "filter $(VAR), values",
mkname: "product.mk",
in: `
ifeq (,$(filter $(TARGET_PRODUCT), yukawa_gms yukawa_sei510_gms)
ifneq (,$(filter $(TARGET_PRODUCT), yukawa_gms)
endif
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if g["TARGET_PRODUCT"] not in ["yukawa_gms", "yukawa_sei510_gms"]:
if g["TARGET_PRODUCT"] == "yukawa_gms":
pass
`,
},
{
desc: "filter $(V1), $(V2)",
mkname: "product.mk",
in: `
ifneq (, $(filter $(PRODUCT_LIST), $(TARGET_PRODUCT)))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if rblf.filter(g.get("PRODUCT_LIST", []), g["TARGET_PRODUCT"]):
pass
`,
},
{
desc: "ifeq",
mkname: "product.mk",
in: `
ifeq (aosp, $(TARGET_PRODUCT)) # Comment
else ifneq (, $(TARGET_PRODUCT))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "aosp" == g["TARGET_PRODUCT"]:
# Comment
pass
elif g["TARGET_PRODUCT"]:
pass
`,
},
{
desc: "Nested if",
mkname: "product.mk",
in: `
ifdef PRODUCT_NAME
PRODUCT_PACKAGES = pack-if0
ifdef PRODUCT_MODEL
PRODUCT_PACKAGES = pack-if-if
else ifdef PRODUCT_NAME
PRODUCT_PACKAGES = pack-if-elif
else
PRODUCT_PACKAGES = pack-if-else
endif
PRODUCT_PACKAGES = pack-if
else ifneq (,$(TARGET_PRODUCT))
PRODUCT_PACKAGES = pack-elif
else
PRODUCT_PACKAGES = pack-else
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if0"]
if cfg.get("PRODUCT_MODEL", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if-if"]
elif cfg.get("PRODUCT_NAME", ""):
cfg["PRODUCT_PACKAGES"] = ["pack-if-elif"]
else:
cfg["PRODUCT_PACKAGES"] = ["pack-if-else"]
cfg["PRODUCT_PACKAGES"] = ["pack-if"]
elif g["TARGET_PRODUCT"]:
cfg["PRODUCT_PACKAGES"] = ["pack-elif"]
else:
cfg["PRODUCT_PACKAGES"] = ["pack-else"]
`,
},
{
desc: "Wildcard",
mkname: "product.mk",
in: `
ifeq (,$(wildcard foo.mk))
endif
ifneq (,$(wildcard foo*.mk))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if not rblf.file_exists("foo.mk"):
pass
if rblf.file_wildcard_exists("foo*.mk"):
pass
`,
},
{
desc: "if with interpolation",
mkname: "product.mk",
in: `
ifeq ($(VARIABLE1)text$(VARIABLE2),true)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "%stext%s" % (g.get("VARIABLE1", ""), g.get("VARIABLE2", "")) == "true":
pass
`,
},
{
desc: "ifneq $(X),true",
mkname: "product.mk",
in: `
ifneq ($(VARIABLE),true)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if g.get("VARIABLE", "") != "true":
pass
`,
},
{
desc: "Const neq",
mkname: "product.mk",
in: `
ifneq (1,0)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "1" != "0":
pass
`,
},
{
desc: "is-board calls",
mkname: "product.mk",
in: `
ifeq ($(call is-board-platform-in-list,msm8998), true)
else ifneq ($(call is-board-platform,copper),true)
else ifneq ($(call is-vendor-board-platform,QCOM),true)
else ifeq ($(call is-product-in-list, $(PLATFORM_LIST)), true)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if rblf.board_platform_in(g, "msm8998"):
pass
elif not rblf.board_platform_is(g, "copper"):
pass
elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
pass
elif g["TARGET_PRODUCT"] in g.get("PLATFORM_LIST", []):
pass
`,
},
{
desc: "new is-board calls",
mkname: "product.mk",
in: `
ifneq (,$(call is-board-platform-in-list2,msm8998 $(X))
else ifeq (,$(call is-board-platform2,copper)
else ifneq (,$(call is-vendor-board-qcom))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if rblf.board_platform_in(g, "msm8998 %s" % g.get("X", "")):
pass
elif not rblf.board_platform_is(g, "copper"):
pass
elif g.get("TARGET_BOARD_PLATFORM", "") in g["QCOM_BOARD_PLATFORMS"]:
pass
`,
},
{
desc: "findstring call",
mkname: "product.mk",
in: `
result := $(findstring a,a b c)
result := $(findstring b,x y z)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
_result = rblf.findstring("a", "a b c")
_result = rblf.findstring("b", "x y z")
`,
},
{
desc: "findstring in if statement",
mkname: "product.mk",
in: `
ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),)
endif
ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),)
endif
ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),foo)
endif
ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),foo)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1:
pass
if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
pass
if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
pass
if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1:
pass
`,
},
{
desc: "rhs call",
mkname: "product.mk",
in: `
PRODUCT_COPY_FILES = $(call add-to-product-copy-files-if-exists, path:distpath) \
$(call find-copy-subdir-files, *, fromdir, todir) $(wildcard foo.*)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_COPY_FILES"] = (rblf.copy_if_exists("path:distpath") +
rblf.find_and_copy("*", "fromdir", "todir") +
rblf.expand_wildcard("foo.*"))
`,
},
{
desc: "inferred type",
mkname: "product.mk",
in: `
HIKEY_MODS := $(wildcard foo/*.ko)
BOARD_VENDOR_KERNEL_MODULES += $(HIKEY_MODS)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["HIKEY_MODS"] = rblf.expand_wildcard("foo/*.ko")
g.setdefault("BOARD_VENDOR_KERNEL_MODULES", [])
g["BOARD_VENDOR_KERNEL_MODULES"] += g["HIKEY_MODS"]
`,
},
{
desc: "list with vars",
mkname: "product.mk",
in: `
PRODUCT_COPY_FILES += path1:$(TARGET_PRODUCT)/path1 $(PRODUCT_MODEL)/path2:$(TARGET_PRODUCT)/path2
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.setdefault(handle, "PRODUCT_COPY_FILES")
cfg["PRODUCT_COPY_FILES"] += (("path1:%s/path1" % g["TARGET_PRODUCT"]).split() +
("%s/path2:%s/path2" % (cfg.get("PRODUCT_MODEL", ""), g["TARGET_PRODUCT"])).split())
`,
},
{
desc: "misc calls",
mkname: "product.mk",
in: `
$(call enforce-product-packages-exist,)
$(call enforce-product-packages-exist, foo)
$(call require-artifacts-in-path, foo, bar)
$(call require-artifacts-in-path-relaxed, foo, bar)
$(call dist-for-goals, goal, from:to)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.enforce_product_packages_exist("")
rblf.enforce_product_packages_exist("foo")
rblf.require_artifacts_in_path("foo", "bar")
rblf.require_artifacts_in_path_relaxed("foo", "bar")
rblf.mkdist_for_goals(g, "goal", "from:to")
`,
},
{
desc: "list with functions",
mkname: "product.mk",
in: `
PRODUCT_COPY_FILES := $(call find-copy-subdir-files,*.kl,from1,to1) \
$(call find-copy-subdir-files,*.kc,from2,to2) \
foo bar
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_COPY_FILES"] = (rblf.find_and_copy("*.kl", "from1", "to1") +
rblf.find_and_copy("*.kc", "from2", "to2") +
[
"foo",
"bar",
])
`,
},
{
desc: "Text functions",
mkname: "product.mk",
in: `
PRODUCT_COPY_FILES := $(addprefix pfx-,a b c)
PRODUCT_COPY_FILES := $(addsuffix .sff, a b c)
PRODUCT_NAME := $(word 1, $(subst ., ,$(TARGET_BOARD_PLATFORM)))
$(info $(patsubst %.pub,$(PRODUCT_NAME)%,$(PRODUCT_ADB_KEYS)))
$(info $(dir foo/bar))
$(info $(firstword $(PRODUCT_COPY_FILES)))
$(info $(lastword $(PRODUCT_COPY_FILES)))
$(info $(dir $(lastword $(MAKEFILE_LIST))))
$(info $(dir $(lastword $(PRODUCT_COPY_FILES))))
$(info $(dir $(lastword $(foobar))))
$(info $(abspath foo/bar))
$(info $(notdir foo/bar))
$(call add_soong_config_namespace,snsconfig)
$(call add_soong_config_var_value,snsconfig,imagetype,odm_image)
$(call soong_config_set, snsconfig, foo, foo_value)
$(call soong_config_append, snsconfig, bar, bar_value)
PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc)
PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_COPY_FILES"] = rblf.addprefix("pfx-", "a b c")
cfg["PRODUCT_COPY_FILES"] = rblf.addsuffix(".sff", "a b c")
cfg["PRODUCT_NAME"] = ((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " ")).split()[0]
rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%s%%" % cfg["PRODUCT_NAME"], g.get("PRODUCT_ADB_KEYS", "")))
rblf.mkinfo("product.mk", rblf.dir("foo/bar"))
rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][0])
rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][-1])
rblf.mkinfo("product.mk", rblf.dir("product.mk"))
rblf.mkinfo("product.mk", rblf.dir(cfg["PRODUCT_COPY_FILES"][-1]))
rblf.mkinfo("product.mk", rblf.dir((_foobar).split()[-1]))
rblf.mkinfo("product.mk", rblf.abspath("foo/bar"))
rblf.mkinfo("product.mk", rblf.notdir("foo/bar"))
rblf.soong_config_namespace(g, "snsconfig")
rblf.soong_config_set(g, "snsconfig", "imagetype", "odm_image")
rblf.soong_config_set(g, "snsconfig", "foo", "foo_value")
rblf.soong_config_append(g, "snsconfig", "bar", "bar_value")
cfg["PRODUCT_COPY_FILES"] = rblf.copy_files(rblf.expand_wildcard("foo*.mk"), "etc")
cfg["PRODUCT_COPY_FILES"] = rblf.product_copy_files_by_pattern("from/%", "to/%", "a b c")
`,
},
{
desc: "subst in list",
mkname: "product.mk",
in: `
files = $(call find-copy-subdir-files,*,from,to)
PRODUCT_COPY_FILES += $(subst foo,bar,$(files))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
_files = rblf.find_and_copy("*", "from", "to")
rblf.setdefault(handle, "PRODUCT_COPY_FILES")
cfg["PRODUCT_COPY_FILES"] += rblf.mksubst("foo", "bar", _files)
`,
},
{
desc: "assignment flavors",
mkname: "product.mk",
in: `
PRODUCT_LIST1 := a
PRODUCT_LIST2 += a
PRODUCT_LIST1 += b
PRODUCT_LIST2 += b
PRODUCT_LIST3 ?= a
PRODUCT_LIST1 = c
PLATFORM_LIST += x
PRODUCT_PACKAGES := $(PLATFORM_LIST)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_LIST1"] = ["a"]
rblf.setdefault(handle, "PRODUCT_LIST2")
cfg["PRODUCT_LIST2"] += ["a"]
cfg["PRODUCT_LIST1"] += ["b"]
cfg["PRODUCT_LIST2"] += ["b"]
if cfg.get("PRODUCT_LIST3") == None:
cfg["PRODUCT_LIST3"] = ["a"]
cfg["PRODUCT_LIST1"] = ["c"]
g.setdefault("PLATFORM_LIST", [])
g["PLATFORM_LIST"] += ["x"]
cfg["PRODUCT_PACKAGES"] = g["PLATFORM_LIST"][:]
`,
},
{
desc: "assigment flavors2",
mkname: "product.mk",
in: `
PRODUCT_LIST1 = a
ifeq (0,1)
PRODUCT_LIST1 += b
PRODUCT_LIST2 += b
endif
PRODUCT_LIST1 += c
PRODUCT_LIST2 += c
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_LIST1"] = ["a"]
if "0" == "1":
cfg["PRODUCT_LIST1"] += ["b"]
rblf.setdefault(handle, "PRODUCT_LIST2")
cfg["PRODUCT_LIST2"] += ["b"]
cfg["PRODUCT_LIST1"] += ["c"]
rblf.setdefault(handle, "PRODUCT_LIST2")
cfg["PRODUCT_LIST2"] += ["c"]
`,
},
{
desc: "soong namespace assignments",
mkname: "product.mk",
in: `
SOONG_CONFIG_NAMESPACES += cvd
SOONG_CONFIG_cvd += launch_configs
SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json
SOONG_CONFIG_cvd += grub_config
SOONG_CONFIG_cvd_grub_config += grub.cfg
x := $(SOONG_CONFIG_cvd_grub_config)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.soong_config_namespace(g, "cvd")
rblf.soong_config_set(g, "cvd", "launch_configs", "cvd_config_auto.json")
rblf.soong_config_append(g, "cvd", "grub_config", "grub.cfg")
rblf.mk2rbc_error("product.mk:7", "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: SOONG_CONFIG_cvd_grub_config")
`,
}, {
desc: "soong namespace accesses",
mkname: "product.mk",
in: `
SOONG_CONFIG_NAMESPACES += cvd
SOONG_CONFIG_cvd += launch_configs
SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json
SOONG_CONFIG_cvd += grub_config
SOONG_CONFIG_cvd_grub_config += grub.cfg
x := $(call soong_config_get,cvd,grub_config)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.soong_config_namespace(g, "cvd")
rblf.soong_config_set(g, "cvd", "launch_configs", "cvd_config_auto.json")
rblf.soong_config_append(g, "cvd", "grub_config", "grub.cfg")
_x = rblf.soong_config_get(g, "cvd", "grub_config")
`,
},
{
desc: "string split",
mkname: "product.mk",
in: `
PRODUCT_LIST1 = a
local = b
local += c
FOO = d
FOO += e
PRODUCT_LIST1 += $(local)
PRODUCT_LIST1 += $(FOO)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_LIST1"] = ["a"]
_local = "b"
_local += " " + "c"
g["FOO"] = "d"
g["FOO"] += " " + "e"
cfg["PRODUCT_LIST1"] += (_local).split()
cfg["PRODUCT_LIST1"] += (g["FOO"]).split()
`,
},
{
desc: "apex_jars",
mkname: "product.mk",
in: `
PRODUCT_BOOT_JARS := $(ART_APEX_JARS) framework-minus-apex
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
cfg["PRODUCT_BOOT_JARS"] = (g.get("ART_APEX_JARS", []) +
["framework-minus-apex"])
`,
},
{
desc: "strip function",
mkname: "product.mk",
in: `
ifeq ($(filter hwaddress,$(PRODUCT_PACKAGES)),)
PRODUCT_PACKAGES := $(strip $(PRODUCT_PACKAGES) hwaddress)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "hwaddress" not in cfg.get("PRODUCT_PACKAGES", []):
cfg["PRODUCT_PACKAGES"] = (rblf.mkstrip("%s hwaddress" % " ".join(cfg.get("PRODUCT_PACKAGES", [])))).split()
`,
},
{
desc: "strip func in condition",
mkname: "product.mk",
in: `
ifneq ($(strip $(TARGET_VENDOR)),)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if rblf.mkstrip(g.get("TARGET_VENDOR", "")):
pass
`,
},
{
desc: "ref after set",
mkname: "product.mk",
in: `
PRODUCT_ADB_KEYS:=value
FOO := $(PRODUCT_ADB_KEYS)
ifneq (,$(PRODUCT_ADB_KEYS))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["PRODUCT_ADB_KEYS"] = "value"
g["FOO"] = g["PRODUCT_ADB_KEYS"]
if g["PRODUCT_ADB_KEYS"]:
pass
`,
},
{
desc: "ref before set",
mkname: "product.mk",
in: `
V1 := $(PRODUCT_ADB_KEYS)
ifeq (,$(PRODUCT_ADB_KEYS))
V2 := $(PRODUCT_ADB_KEYS)
PRODUCT_ADB_KEYS:=foo
V3 := $(PRODUCT_ADB_KEYS)
endif`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["V1"] = g.get("PRODUCT_ADB_KEYS", "")
if not g.get("PRODUCT_ADB_KEYS", ""):
g["V2"] = g.get("PRODUCT_ADB_KEYS", "")
g["PRODUCT_ADB_KEYS"] = "foo"
g["V3"] = g["PRODUCT_ADB_KEYS"]
`,
},
{
desc: "Dynamic inherit path",
mkname: "product.mk",
in: `
MY_PATH:=foo
$(call inherit-product,vendor/$(MY_PATH)/cfg.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
load("//vendor/bar/baz:cfg.star|init", _cfg1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
_entry = {
"vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
"vendor/bar/baz/cfg.mk": ("vendor/bar/baz/cfg", _cfg1_init),
}.get("vendor/%s/cfg.mk" % g["MY_PATH"])
(_varmod, _varmod_init) = _entry if _entry else (None, None)
if not _varmod_init:
rblf.mkerror("product.mk", "Cannot find %s" % ("vendor/%s/cfg.mk" % g["MY_PATH"]))
rblf.inherit(handle, _varmod, _varmod_init)
`,
},
{
desc: "Dynamic inherit with hint",
mkname: "product.mk",
in: `
MY_PATH:=foo
#RBC# include_top vendor/foo1
$(call inherit-product,$(MY_PATH)/cfg.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
`,
},
{
desc: "Dynamic inherit with duplicated hint",
mkname: "product.mk",
in: `
MY_PATH:=foo
#RBC# include_top vendor/foo1
$(call inherit-product,$(MY_PATH)/cfg.mk)
#RBC# include_top vendor/foo1
#RBC# include_top vendor/foo1
$(call inherit-product,$(MY_PATH)/cfg.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
`,
},
{
desc: "Dynamic inherit path that lacks hint",
mkname: "product.mk",
in: `
#RBC# include_top foo
$(call inherit-product,$(MY_VAR)/font.mk)
#RBC# include_top foo
# There's some space and even this comment between the include_top and the inherit-product
$(call inherit-product,$(MY_VAR)/font.mk)
$(call inherit-product,$(MY_VAR)/font.mk)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
load("//foo:font.star|init", _font_init = "init")
load("//bar:font.star|init", _font1_init = "init")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.inherit(handle, "foo/font", _font_init)
# There's some space and even this comment between the include_top and the inherit-product
rblf.inherit(handle, "foo/font", _font_init)
rblf.mkwarning("product.mk:11", "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
_entry = {
"foo/font.mk": ("foo/font", _font_init),
"bar/font.mk": ("bar/font", _font1_init),
}.get("%s/font.mk" % g.get("MY_VAR", ""))
(_varmod, _varmod_init) = _entry if _entry else (None, None)
if not _varmod_init:
rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
rblf.inherit(handle, _varmod, _varmod_init)
`,
},
{
desc: "Ignore make rules",
mkname: "product.mk",
in: `
foo: foo.c
gcc -o $@ $*`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.mk2rbc_error("product.mk:2", "unsupported line rule: foo: foo.c\n#gcc -o $@ $*")
`,
},
{
desc: "Flag override",
mkname: "product.mk",
in: `
override FOO:=`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
rblf.mk2rbc_error("product.mk:2", "cannot handle override directive")
`,
},
{
desc: "Bad expression",
mkname: "build/product.mk",
in: `
ifeq (,$(call foobar))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if rblf.mk2rbc_error("build/product.mk:2", "cannot handle invoking foobar"):
pass
`,
},
{
desc: "if expression",
mkname: "product.mk",
in: `
TEST_VAR := foo
TEST_VAR_LIST := foo
TEST_VAR_LIST += bar
TEST_VAR_2 := $(if $(TEST_VAR),bar)
TEST_VAR_3 := $(if $(TEST_VAR),bar,baz)
TEST_VAR_3 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["TEST_VAR"] = "foo"
g["TEST_VAR_LIST"] = ["foo"]
g["TEST_VAR_LIST"] += ["bar"]
g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "")
g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz")
g["TEST_VAR_3"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
`,
},
{
desc: "substitution references",
mkname: "product.mk",
in: `
SOURCES := foo.c bar.c
OBJECTS := $(SOURCES:.c=.o)
OBJECTS2 := $(SOURCES:%.c=%.o)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["SOURCES"] = "foo.c bar.c"
g["OBJECTS"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"])
g["OBJECTS2"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"])
`,
},
{
desc: "foreach expressions",
mkname: "product.mk",
in: `
BOOT_KERNEL_MODULES := foo.ko bar.ko
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))
BOOT_KERNEL_MODULES_LIST := foo.ko
BOOT_KERNEL_MODULES_LIST += bar.ko
BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["BOOT_KERNEL_MODULES"] = "foo.ko bar.ko"
g["BOOT_KERNEL_MODULES_FILTER"] = ["%%/%s" % m for m in rblf.words(g["BOOT_KERNEL_MODULES"])]
g["BOOT_KERNEL_MODULES_LIST"] = ["foo.ko"]
g["BOOT_KERNEL_MODULES_LIST"] += ["bar.ko"]
g["BOOT_KERNEL_MODULES_FILTER_2"] = ["%%/%s" % m for m in g["BOOT_KERNEL_MODULES_LIST"]]
`,
},
{
desc: "List appended to string",
mkname: "product.mk",
in: `
NATIVE_BRIDGE_PRODUCT_PACKAGES := \
libnative_bridge_vdso.native_bridge \
native_bridge_guest_app_process.native_bridge \
native_bridge_guest_linker.native_bridge
NATIVE_BRIDGE_MODIFIED_GUEST_LIBS := \
libaaudio \
libamidi \
libandroid \
libandroid_runtime
NATIVE_BRIDGE_PRODUCT_PACKAGES += \
$(addsuffix .native_bridge,$(NATIVE_BRIDGE_ORIG_GUEST_LIBS))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] = "libnative_bridge_vdso.native_bridge native_bridge_guest_app_process.native_bridge native_bridge_guest_linker.native_bridge"
g["NATIVE_BRIDGE_MODIFIED_GUEST_LIBS"] = "libaaudio libamidi libandroid libandroid_runtime"
g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] += " " + " ".join(rblf.addsuffix(".native_bridge", g.get("NATIVE_BRIDGE_ORIG_GUEST_LIBS", "")))
`,
},
{
desc: "Math functions",
mkname: "product.mk",
in: `
# Test the math functions defined in build/make/common/math.mk
ifeq ($(call math_max,2,5),5)
endif
ifeq ($(call math_min,2,5),2)
endif
ifeq ($(call math_gt_or_eq,2,5),true)
endif
ifeq ($(call math_gt,2,5),true)
endif
ifeq ($(call math_lt,2,5),true)
endif
ifeq ($(call math_gt_or_eq,2,5),)
endif
ifeq ($(call math_gt,2,5),)
endif
ifeq ($(call math_lt,2,5),)
endif
ifeq ($(call math_gt_or_eq,$(MY_VAR), 5),true)
endif
ifeq ($(call math_gt_or_eq,$(MY_VAR),$(MY_OTHER_VAR)),true)
endif
ifeq ($(call math_gt_or_eq,100$(MY_VAR),10),true)
endif
`,
expected: `# Test the math functions defined in build/make/common/math.mk
load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if max(2, 5) == 5:
pass
if min(2, 5) == 2:
pass
if 2 >= 5:
pass
if 2 > 5:
pass
if 2 < 5:
pass
if 2 < 5:
pass
if 2 <= 5:
pass
if 2 >= 5:
pass
if int(g.get("MY_VAR", "")) >= 5:
pass
if int(g.get("MY_VAR", "")) >= int(g.get("MY_OTHER_VAR", "")):
pass
if int("100%s" % g.get("MY_VAR", "")) >= 10:
pass
`,
},
}
var known_variables = []struct {
name string
class varClass
starlarkType
}{
{"NATIVE_COVERAGE", VarClassSoong, starlarkTypeBool},
{"PRODUCT_NAME", VarClassConfig, starlarkTypeString},
{"PRODUCT_MODEL", VarClassConfig, starlarkTypeString},
{"PRODUCT_PACKAGES", VarClassConfig, starlarkTypeList},
{"PRODUCT_BOOT_JARS", VarClassConfig, starlarkTypeList},
{"PRODUCT_COPY_FILES", VarClassConfig, starlarkTypeList},
{"PRODUCT_IS_64BIT", VarClassConfig, starlarkTypeString},
{"PRODUCT_LIST1", VarClassConfig, starlarkTypeList},
{"PRODUCT_LIST2", VarClassConfig, starlarkTypeList},
{"PRODUCT_LIST3", VarClassConfig, starlarkTypeList},
{"TARGET_PRODUCT", VarClassSoong, starlarkTypeString},
{"TARGET_BUILD_VARIANT", VarClassSoong, starlarkTypeString},
{"TARGET_BOARD_PLATFORM", VarClassSoong, starlarkTypeString},
{"QCOM_BOARD_PLATFORMS", VarClassSoong, starlarkTypeString},
{"PLATFORM_LIST", VarClassSoong, starlarkTypeList}, // TODO(asmundak): make it local instead of soong
}
type testMakefileFinder struct {
fs fs.FS
root string
files []string
}
func (t *testMakefileFinder) Find(root string) []string {
if t.files != nil || root == t.root {
return t.files
}
t.files = make([]string, 0)
fs.WalkDir(t.fs, root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
base := filepath.Base(path)
if base[0] == '.' && len(base) > 1 {
return fs.SkipDir
}
return nil
}
if strings.HasSuffix(path, ".mk") {
t.files = append(t.files, path)
}
return nil
})
return t.files
}
func TestGood(t *testing.T) {
for _, v := range known_variables {
KnownVariables.NewVariable(v.name, v.class, v.starlarkType)
}
fs := NewFindMockFS([]string{
"vendor/foo1/cfg.mk",
"vendor/bar/baz/cfg.mk",
"part.mk",
"foo/font.mk",
"bar/font.mk",
})
for _, test := range testCases {
t.Run(test.desc,
func(t *testing.T) {
ss, err := Convert(Request{
MkFile: test.mkname,
Reader: bytes.NewBufferString(test.in),
RootDir: ".",
OutputSuffix: ".star",
SourceFS: fs,
MakefileFinder: &testMakefileFinder{fs: fs},
})
if err != nil {
t.Error(err)
return
}
got := ss.String()
if got != test.expected {
t.Errorf("%q failed\nExpected:\n%s\nActual:\n%s\n", test.desc,
strings.ReplaceAll(test.expected, "\n", "␤\n"),
strings.ReplaceAll(got, "\n", "␤\n"))
}
})
}
}