Merge "Support multilib in apex." am: 539d41b686
am: 2cda5749f4
Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1932025 Change-Id: Ia251d14ae2344eff2caf409e32e4d9b3712d4ea3
This commit is contained in:
commit
e1fc6a20ca
3 changed files with 334 additions and 26 deletions
|
@ -1932,6 +1932,10 @@ func (m *ModuleBase) VintfFragments() Paths {
|
||||||
return append(Paths{}, m.vintfFragmentsPaths...)
|
return append(Paths{}, m.vintfFragmentsPaths...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *ModuleBase) CompileMultilib() *string {
|
||||||
|
return m.base().commonProperties.Compile_multilib
|
||||||
|
}
|
||||||
|
|
||||||
// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
|
// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
|
||||||
// apex container for use when generation the license metadata file.
|
// apex container for use when generation the license metadata file.
|
||||||
func (m *ModuleBase) SetLicenseInstallMap(installMap []string) {
|
func (m *ModuleBase) SetLicenseInstallMap(installMap []string) {
|
||||||
|
|
173
apex/apex.go
173
apex/apex.go
|
@ -3270,17 +3270,23 @@ func rModulesPackages() map[string][]string {
|
||||||
// For Bazel / bp2build
|
// For Bazel / bp2build
|
||||||
|
|
||||||
type bazelApexBundleAttributes struct {
|
type bazelApexBundleAttributes struct {
|
||||||
Manifest bazel.LabelAttribute
|
Manifest bazel.LabelAttribute
|
||||||
Android_manifest bazel.LabelAttribute
|
Android_manifest bazel.LabelAttribute
|
||||||
File_contexts bazel.LabelAttribute
|
File_contexts bazel.LabelAttribute
|
||||||
Key bazel.LabelAttribute
|
Key bazel.LabelAttribute
|
||||||
Certificate bazel.LabelAttribute
|
Certificate bazel.LabelAttribute
|
||||||
Min_sdk_version *string
|
Min_sdk_version *string
|
||||||
Updatable bazel.BoolAttribute
|
Updatable bazel.BoolAttribute
|
||||||
Installable bazel.BoolAttribute
|
Installable bazel.BoolAttribute
|
||||||
Native_shared_libs bazel.LabelListAttribute
|
Binaries bazel.LabelListAttribute
|
||||||
Binaries bazel.LabelListAttribute
|
Prebuilts bazel.LabelListAttribute
|
||||||
Prebuilts bazel.LabelListAttribute
|
Native_shared_libs_32 bazel.LabelListAttribute
|
||||||
|
Native_shared_libs_64 bazel.LabelListAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
type convertedNativeSharedLibs struct {
|
||||||
|
Native_shared_libs_32 bazel.LabelListAttribute
|
||||||
|
Native_shared_libs_64 bazel.LabelListAttribute
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertWithBp2build performs bp2build conversion of an apex
|
// ConvertWithBp2build performs bp2build conversion of an apex
|
||||||
|
@ -3320,9 +3326,21 @@ func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
|
||||||
certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
|
certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
|
||||||
}
|
}
|
||||||
|
|
||||||
nativeSharedLibs := a.properties.ApexNativeDependencies.Native_shared_libs
|
nativeSharedLibs := &convertedNativeSharedLibs{
|
||||||
nativeSharedLibsLabelList := android.BazelLabelForModuleDeps(ctx, nativeSharedLibs)
|
Native_shared_libs_32: bazel.LabelListAttribute{},
|
||||||
nativeSharedLibsLabelListAttribute := bazel.MakeLabelListAttribute(nativeSharedLibsLabelList)
|
Native_shared_libs_64: bazel.LabelListAttribute{},
|
||||||
|
}
|
||||||
|
compileMultilib := "both"
|
||||||
|
if a.CompileMultilib() != nil {
|
||||||
|
compileMultilib = *a.CompileMultilib()
|
||||||
|
}
|
||||||
|
|
||||||
|
// properties.Native_shared_libs is treated as "both"
|
||||||
|
convertBothLibs(ctx, compileMultilib, a.properties.Native_shared_libs, nativeSharedLibs)
|
||||||
|
convertBothLibs(ctx, compileMultilib, a.properties.Multilib.Both.Native_shared_libs, nativeSharedLibs)
|
||||||
|
convert32Libs(ctx, compileMultilib, a.properties.Multilib.Lib32.Native_shared_libs, nativeSharedLibs)
|
||||||
|
convert64Libs(ctx, compileMultilib, a.properties.Multilib.Lib64.Native_shared_libs, nativeSharedLibs)
|
||||||
|
convertFirstLibs(ctx, compileMultilib, a.properties.Multilib.First.Native_shared_libs, nativeSharedLibs)
|
||||||
|
|
||||||
prebuilts := a.overridableProperties.Prebuilts
|
prebuilts := a.overridableProperties.Prebuilts
|
||||||
prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts)
|
prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts)
|
||||||
|
@ -3342,17 +3360,18 @@ func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs := &bazelApexBundleAttributes{
|
attrs := &bazelApexBundleAttributes{
|
||||||
Manifest: manifestLabelAttribute,
|
Manifest: manifestLabelAttribute,
|
||||||
Android_manifest: androidManifestLabelAttribute,
|
Android_manifest: androidManifestLabelAttribute,
|
||||||
File_contexts: fileContextsLabelAttribute,
|
File_contexts: fileContextsLabelAttribute,
|
||||||
Min_sdk_version: minSdkVersion,
|
Min_sdk_version: minSdkVersion,
|
||||||
Key: keyLabelAttribute,
|
Key: keyLabelAttribute,
|
||||||
Certificate: certificateLabelAttribute,
|
Certificate: certificateLabelAttribute,
|
||||||
Updatable: updatableAttribute,
|
Updatable: updatableAttribute,
|
||||||
Installable: installableAttribute,
|
Installable: installableAttribute,
|
||||||
Native_shared_libs: nativeSharedLibsLabelListAttribute,
|
Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32,
|
||||||
Binaries: binariesLabelListAttribute,
|
Native_shared_libs_64: nativeSharedLibs.Native_shared_libs_64,
|
||||||
Prebuilts: prebuiltsLabelListAttribute,
|
Binaries: binariesLabelListAttribute,
|
||||||
|
Prebuilts: prebuiltsLabelListAttribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
props := bazel.BazelTargetModuleProperties{
|
props := bazel.BazelTargetModuleProperties{
|
||||||
|
@ -3362,3 +3381,107 @@ func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
|
||||||
|
|
||||||
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
|
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The following conversions are based on this table where the rows are the compile_multilib
|
||||||
|
// values and the columns are the properties.Multilib.*.Native_shared_libs. Each cell
|
||||||
|
// represents how the libs should be compiled for a 64-bit/32-bit device: 32 means it
|
||||||
|
// should be compiled as 32-bit, 64 means it should be compiled as 64-bit, none means it
|
||||||
|
// should not be compiled.
|
||||||
|
// multib/compile_multilib, 32, 64, both, first
|
||||||
|
// 32, 32/32, none/none, 32/32, none/32
|
||||||
|
// 64, none/none, 64/none, 64/none, 64/none
|
||||||
|
// both, 32/32, 64/none, 32&64/32, 64/32
|
||||||
|
// first, 32/32, 64/none, 64/32, 64/32
|
||||||
|
|
||||||
|
func convert32Libs(ctx android.TopDownMutatorContext, compileMultilb string,
|
||||||
|
libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
|
||||||
|
switch compileMultilb {
|
||||||
|
case "both", "32":
|
||||||
|
makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "first":
|
||||||
|
make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "64":
|
||||||
|
// Incompatible, ignore
|
||||||
|
default:
|
||||||
|
invalidCompileMultilib(ctx, compileMultilb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert64Libs(ctx android.TopDownMutatorContext, compileMultilb string,
|
||||||
|
libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
|
||||||
|
switch compileMultilb {
|
||||||
|
case "both", "64", "first":
|
||||||
|
make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "32":
|
||||||
|
// Incompatible, ignore
|
||||||
|
default:
|
||||||
|
invalidCompileMultilib(ctx, compileMultilb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertBothLibs(ctx android.TopDownMutatorContext, compileMultilb string,
|
||||||
|
libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
|
||||||
|
switch compileMultilb {
|
||||||
|
case "both":
|
||||||
|
makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "first":
|
||||||
|
makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "32":
|
||||||
|
makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "64":
|
||||||
|
make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
default:
|
||||||
|
invalidCompileMultilib(ctx, compileMultilb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFirstLibs(ctx android.TopDownMutatorContext, compileMultilb string,
|
||||||
|
libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
|
||||||
|
switch compileMultilb {
|
||||||
|
case "both", "first":
|
||||||
|
makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "32":
|
||||||
|
make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
case "64":
|
||||||
|
make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
default:
|
||||||
|
invalidCompileMultilib(ctx, compileMultilb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFirstSharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNoConfig32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
list := bazel.LabelListAttribute{}
|
||||||
|
list.SetSelectValue(bazel.NoConfigAxis, "", libsLabelList)
|
||||||
|
nativeSharedLibs.Native_shared_libs_32.Append(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func make32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
makeSharedLibsAttributes("x86", libsLabelList, &nativeSharedLibs.Native_shared_libs_32)
|
||||||
|
makeSharedLibsAttributes("arm", libsLabelList, &nativeSharedLibs.Native_shared_libs_32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func make64SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
|
||||||
|
makeSharedLibsAttributes("x86_64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64)
|
||||||
|
makeSharedLibsAttributes("arm64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSharedLibsAttributes(config string, libsLabelList bazel.LabelList,
|
||||||
|
labelListAttr *bazel.LabelListAttribute) {
|
||||||
|
list := bazel.LabelListAttribute{}
|
||||||
|
list.SetSelectValue(bazel.ArchConfigurationAxis, config, libsLabelList)
|
||||||
|
labelListAttr.Append(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) {
|
||||||
|
ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value)
|
||||||
|
}
|
||||||
|
|
|
@ -131,10 +131,21 @@ apex {
|
||||||
"key": `":com.android.apogee.key"`,
|
"key": `":com.android.apogee.key"`,
|
||||||
"manifest": `"apogee_manifest.json"`,
|
"manifest": `"apogee_manifest.json"`,
|
||||||
"min_sdk_version": `"29"`,
|
"min_sdk_version": `"29"`,
|
||||||
"native_shared_libs": `[
|
"native_shared_libs_32": `[
|
||||||
":native_shared_lib_1",
|
":native_shared_lib_1",
|
||||||
":native_shared_lib_2",
|
":native_shared_lib_2",
|
||||||
]`,
|
]`,
|
||||||
|
"native_shared_libs_64": `select({
|
||||||
|
"//build/bazel/platforms/arch:arm64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//build/bazel/platforms/arch:x86_64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
"prebuilts": `[
|
"prebuilts": `[
|
||||||
":pretend_prebuilt_1",
|
":pretend_prebuilt_1",
|
||||||
":pretend_prebuilt_2",
|
":pretend_prebuilt_2",
|
||||||
|
@ -144,6 +155,126 @@ apex {
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApexBundleCompileMultilibBoth(t *testing.T) {
|
||||||
|
runApexTestCase(t, bp2buildTestCase{
|
||||||
|
description: "apex - example with compile_multilib=both",
|
||||||
|
moduleTypeUnderTest: "apex",
|
||||||
|
moduleTypeUnderTestFactory: apex.BundleFactory,
|
||||||
|
filesystem: map[string]string{},
|
||||||
|
blueprint: createMultilibBlueprint("both"),
|
||||||
|
expectedBazelTargets: []string{
|
||||||
|
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
|
||||||
|
"native_shared_libs_32": `[
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_3",
|
||||||
|
] + select({
|
||||||
|
"//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
|
||||||
|
"//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
|
"native_shared_libs_64": `select({
|
||||||
|
"//build/bazel/platforms/arch:arm64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_4",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//build/bazel/platforms/arch:x86_64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_4",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
|
}),
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApexBundleCompileMultilibFirst(t *testing.T) {
|
||||||
|
runApexTestCase(t, bp2buildTestCase{
|
||||||
|
description: "apex - example with compile_multilib=first",
|
||||||
|
moduleTypeUnderTest: "apex",
|
||||||
|
moduleTypeUnderTestFactory: apex.BundleFactory,
|
||||||
|
filesystem: map[string]string{},
|
||||||
|
blueprint: createMultilibBlueprint("first"),
|
||||||
|
expectedBazelTargets: []string{
|
||||||
|
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
|
||||||
|
"native_shared_libs_32": `select({
|
||||||
|
"//build/bazel/platforms/arch:arm": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_3",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//build/bazel/platforms/arch:x86": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_3",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
|
"native_shared_libs_64": `select({
|
||||||
|
"//build/bazel/platforms/arch:arm64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_4",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//build/bazel/platforms/arch:x86_64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_4",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
|
}),
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApexBundleCompileMultilib32(t *testing.T) {
|
||||||
|
runApexTestCase(t, bp2buildTestCase{
|
||||||
|
description: "apex - example with compile_multilib=32",
|
||||||
|
moduleTypeUnderTest: "apex",
|
||||||
|
moduleTypeUnderTestFactory: apex.BundleFactory,
|
||||||
|
filesystem: map[string]string{},
|
||||||
|
blueprint: createMultilibBlueprint("32"),
|
||||||
|
expectedBazelTargets: []string{
|
||||||
|
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
|
||||||
|
"native_shared_libs_32": `[
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_3",
|
||||||
|
] + select({
|
||||||
|
"//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
|
||||||
|
"//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
|
}),
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApexBundleCompileMultilib64(t *testing.T) {
|
||||||
|
runApexTestCase(t, bp2buildTestCase{
|
||||||
|
description: "apex - example with compile_multilib=64",
|
||||||
|
moduleTypeUnderTest: "apex",
|
||||||
|
moduleTypeUnderTestFactory: apex.BundleFactory,
|
||||||
|
filesystem: map[string]string{},
|
||||||
|
blueprint: createMultilibBlueprint("64"),
|
||||||
|
expectedBazelTargets: []string{
|
||||||
|
makeBazelTarget("apex", "com.android.apogee", attrNameToString{
|
||||||
|
"native_shared_libs_64": `select({
|
||||||
|
"//build/bazel/platforms/arch:arm64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_4",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//build/bazel/platforms/arch:x86_64": [
|
||||||
|
":native_shared_lib_1",
|
||||||
|
":native_shared_lib_4",
|
||||||
|
":native_shared_lib_2",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
})`,
|
||||||
|
}),
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
func TestApexBundleDefaultPropertyValues(t *testing.T) {
|
func TestApexBundleDefaultPropertyValues(t *testing.T) {
|
||||||
runApexTestCase(t, bp2buildTestCase{
|
runApexTestCase(t, bp2buildTestCase{
|
||||||
description: "apex - default property values",
|
description: "apex - default property values",
|
||||||
|
@ -180,3 +311,53 @@ apex {
|
||||||
}),
|
}),
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createMultilibBlueprint(compile_multilib string) string {
|
||||||
|
return `
|
||||||
|
cc_library {
|
||||||
|
name: "native_shared_lib_1",
|
||||||
|
bazel_module: { bp2build_available: false },
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_library {
|
||||||
|
name: "native_shared_lib_2",
|
||||||
|
bazel_module: { bp2build_available: false },
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_library {
|
||||||
|
name: "native_shared_lib_3",
|
||||||
|
bazel_module: { bp2build_available: false },
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_library {
|
||||||
|
name: "native_shared_lib_4",
|
||||||
|
bazel_module: { bp2build_available: false },
|
||||||
|
}
|
||||||
|
|
||||||
|
apex {
|
||||||
|
name: "com.android.apogee",
|
||||||
|
compile_multilib: "` + compile_multilib + `",
|
||||||
|
multilib: {
|
||||||
|
both: {
|
||||||
|
native_shared_libs: [
|
||||||
|
"native_shared_lib_1",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
first: {
|
||||||
|
native_shared_libs: [
|
||||||
|
"native_shared_lib_2",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
lib32: {
|
||||||
|
native_shared_libs: [
|
||||||
|
"native_shared_lib_3",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
lib64: {
|
||||||
|
native_shared_libs: [
|
||||||
|
"native_shared_lib_4",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue