Read ApexMkInfo for modules to be installed.
This piggybacks onto the ApexInfo cquery handler, so we're issuing a single bazel query call that reads two providers in the starlark expr. Also rename requiredDeps to makeModulesToInstall to differentiate it from APEX's required/provided libs in the apex manifest. Test: unit test Test: mkdiff Fixes: 263123189 Change-Id: Ib7e43f1586f29864eee8627dba3631bfaff27afa
This commit is contained in:
@@ -461,11 +461,6 @@ func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) st
|
|||||||
return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
|
return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModuleFromBazelLabel reverses the logic in bp2buildModuleLabel
|
|
||||||
func ModuleFromBazelLabel(label string) string {
|
|
||||||
return strings.Split(label, ":")[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
|
// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
|
||||||
type BazelOutPath struct {
|
type BazelOutPath struct {
|
||||||
OutputPath
|
OutputPath
|
||||||
|
@@ -309,7 +309,7 @@ func (a *apexBundle) writeRequiredModules(w io.Writer, moduleNames []string) {
|
|||||||
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
|
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
|
||||||
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
|
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
|
||||||
}
|
}
|
||||||
android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.requiredDeps, required)
|
android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
|
||||||
android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
|
android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
|
||||||
android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
|
android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
|
||||||
}
|
}
|
||||||
|
20
apex/apex.go
20
apex/apex.go
@@ -439,8 +439,8 @@ type apexBundle struct {
|
|||||||
// GenerateAndroidBuildActions.
|
// GenerateAndroidBuildActions.
|
||||||
filesInfo []apexFile
|
filesInfo []apexFile
|
||||||
|
|
||||||
// List of other module names that should be installed when this APEX gets installed.
|
// List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
|
||||||
requiredDeps []string
|
makeModulesToInstall []string
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Outputs (final and intermediates)
|
// Outputs (final and intermediates)
|
||||||
@@ -1922,11 +1922,9 @@ func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) {
|
|||||||
a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
|
a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
|
||||||
a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
|
a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
|
||||||
|
|
||||||
// Ensure ApexInfo.RequiresLibs are installed as part of a bundle build
|
// Ensure ApexMkInfo.install_to_system make module names are installed as
|
||||||
for _, bazelLabel := range outputs.RequiresLibs {
|
// part of a bundled build.
|
||||||
// convert Bazel label back to Soong module name
|
a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...)
|
||||||
a.requiredDeps = append(a.requiredDeps, android.ModuleFromBazelLabel(bazelLabel))
|
|
||||||
}
|
|
||||||
|
|
||||||
apexType := a.properties.ApexType
|
apexType := a.properties.ApexType
|
||||||
switch apexType {
|
switch apexType {
|
||||||
@@ -2025,7 +2023,7 @@ func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) {
|
|||||||
a.primaryApexType = true
|
a.primaryApexType = true
|
||||||
|
|
||||||
if ctx.Config().InstallExtraFlattenedApexes() {
|
if ctx.Config().InstallExtraFlattenedApexes() {
|
||||||
a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
|
a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case zipApex:
|
case zipApex:
|
||||||
@@ -2177,7 +2175,7 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
|
|||||||
|
|
||||||
vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
|
vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
|
||||||
for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
|
for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
|
||||||
a.requiredDeps = append(a.requiredDeps, makeModuleName)
|
a.makeModulesToInstall = append(a.makeModulesToInstall, makeModuleName)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
case sscpfTag:
|
case sscpfTag:
|
||||||
@@ -2343,8 +2341,8 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
|
|||||||
if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
|
if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
|
||||||
// we need a module name for Make
|
// we need a module name for Make
|
||||||
name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
|
name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
|
||||||
if !android.InList(name, a.requiredDeps) {
|
if !android.InList(name, a.makeModulesToInstall) {
|
||||||
a.requiredDeps = append(a.requiredDeps, name)
|
a.makeModulesToInstall = append(a.makeModulesToInstall, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
|
vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
|
||||||
|
@@ -5713,7 +5713,7 @@ func TestInstallExtraFlattenedApexes(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
|
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
|
||||||
ensureListContains(t, ab.requiredDeps, "myapex.flattened")
|
ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened")
|
||||||
mk := android.AndroidMkDataForTest(t, ctx, ab)
|
mk := android.AndroidMkDataForTest(t, ctx, ab)
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
|
mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
|
||||||
@@ -9290,7 +9290,7 @@ func TestAndroidMk_RequiredDeps(t *testing.T) {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
|
bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
|
||||||
bundle.requiredDeps = append(bundle.requiredDeps, "foo")
|
bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo")
|
||||||
data := android.AndroidMkDataForTest(t, ctx, bundle)
|
data := android.AndroidMkDataForTest(t, ctx, bundle)
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
|
data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
|
||||||
@@ -9298,7 +9298,7 @@ func TestAndroidMk_RequiredDeps(t *testing.T) {
|
|||||||
ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo\n")
|
ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo\n")
|
||||||
|
|
||||||
flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
|
flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
|
||||||
flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
|
flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo")
|
||||||
flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
|
flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
|
||||||
var flattenedBuilder strings.Builder
|
var flattenedBuilder strings.Builder
|
||||||
flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
|
flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
|
||||||
@@ -9542,7 +9542,7 @@ func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
|
|||||||
func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
|
func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
|
||||||
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
|
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
|
||||||
for _, dep := range deps {
|
for _, dep := range deps {
|
||||||
android.AssertStringListContains(t, "", a.requiredDeps, dep)
|
android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9550,7 +9550,7 @@ func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleNa
|
|||||||
func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
|
func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
|
||||||
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
|
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
|
||||||
for _, dep := range deps {
|
for _, dep := range deps {
|
||||||
android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
|
android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,6 +42,7 @@ apex {
|
|||||||
OutputBaseDir: outputBaseDir,
|
OutputBaseDir: outputBaseDir,
|
||||||
LabelToApexInfo: map[string]cquery.ApexInfo{
|
LabelToApexInfo: map[string]cquery.ApexInfo{
|
||||||
"//:foo": cquery.ApexInfo{
|
"//:foo": cquery.ApexInfo{
|
||||||
|
// ApexInfo Starlark provider.
|
||||||
SignedOutput: "signed_out.apex",
|
SignedOutput: "signed_out.apex",
|
||||||
SignedCompressedOutput: "signed_out.capex",
|
SignedCompressedOutput: "signed_out.capex",
|
||||||
UnsignedOutput: "unsigned_out.apex",
|
UnsignedOutput: "unsigned_out.apex",
|
||||||
@@ -56,6 +57,9 @@ apex {
|
|||||||
// unused
|
// unused
|
||||||
PackageName: "pkg_name",
|
PackageName: "pkg_name",
|
||||||
ProvidesLibs: []string{"a", "b"},
|
ProvidesLibs: []string{"a", "b"},
|
||||||
|
|
||||||
|
// ApexMkInfo Starlark provider
|
||||||
|
MakeModulesToInstall: []string{"c"}, // d deliberately omitted
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -111,7 +115,12 @@ apex {
|
|||||||
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
|
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
|
||||||
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
|
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
|
||||||
}
|
}
|
||||||
if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
|
|
||||||
|
// make modules to be installed to system
|
||||||
|
if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
|
||||||
|
t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
|
||||||
|
}
|
||||||
|
if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
|
||||||
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
|
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,6 +221,7 @@ override_apex {
|
|||||||
OutputBaseDir: outputBaseDir,
|
OutputBaseDir: outputBaseDir,
|
||||||
LabelToApexInfo: map[string]cquery.ApexInfo{
|
LabelToApexInfo: map[string]cquery.ApexInfo{
|
||||||
"//:foo": cquery.ApexInfo{
|
"//:foo": cquery.ApexInfo{
|
||||||
|
// ApexInfo Starlark provider
|
||||||
SignedOutput: "signed_out.apex",
|
SignedOutput: "signed_out.apex",
|
||||||
UnsignedOutput: "unsigned_out.apex",
|
UnsignedOutput: "unsigned_out.apex",
|
||||||
BundleKeyInfo: []string{"public_key", "private_key"},
|
BundleKeyInfo: []string{"public_key", "private_key"},
|
||||||
@@ -225,8 +235,12 @@ override_apex {
|
|||||||
// unused
|
// unused
|
||||||
PackageName: "pkg_name",
|
PackageName: "pkg_name",
|
||||||
ProvidesLibs: []string{"a", "b"},
|
ProvidesLibs: []string{"a", "b"},
|
||||||
|
|
||||||
|
// ApexMkInfo Starlark provider
|
||||||
|
MakeModulesToInstall: []string{"c"}, // d deliberately omitted
|
||||||
},
|
},
|
||||||
"//:override_foo": cquery.ApexInfo{
|
"//:override_foo": cquery.ApexInfo{
|
||||||
|
// ApexInfo Starlark provider
|
||||||
SignedOutput: "override_signed_out.apex",
|
SignedOutput: "override_signed_out.apex",
|
||||||
UnsignedOutput: "override_unsigned_out.apex",
|
UnsignedOutput: "override_unsigned_out.apex",
|
||||||
BundleKeyInfo: []string{"override_public_key", "override_private_key"},
|
BundleKeyInfo: []string{"override_public_key", "override_private_key"},
|
||||||
@@ -240,6 +254,9 @@ override_apex {
|
|||||||
// unused
|
// unused
|
||||||
PackageName: "override_pkg_name",
|
PackageName: "override_pkg_name",
|
||||||
ProvidesLibs: []string{"a", "b"},
|
ProvidesLibs: []string{"a", "b"},
|
||||||
|
|
||||||
|
// ApexMkInfo Starlark provider
|
||||||
|
MakeModulesToInstall: []string{"c"}, // d deliberately omitted
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -295,7 +312,12 @@ override_apex {
|
|||||||
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
|
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
|
||||||
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
|
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
|
||||||
}
|
}
|
||||||
if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
|
|
||||||
|
// make modules to be installed to system
|
||||||
|
if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
|
||||||
|
t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
|
||||||
|
}
|
||||||
|
if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
|
||||||
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
|
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -237,6 +237,10 @@ signed_compressed_output = "" # no .capex if the apex is not compressible, canno
|
|||||||
if info.signed_compressed_output:
|
if info.signed_compressed_output:
|
||||||
signed_compressed_output = info.signed_compressed_output.path
|
signed_compressed_output = info.signed_compressed_output.path
|
||||||
|
|
||||||
|
mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
|
||||||
|
if not mk_info:
|
||||||
|
fail("%s did not provide ApexMkInfo" % id_string)
|
||||||
|
|
||||||
return json_encode({
|
return json_encode({
|
||||||
"signed_output": info.signed_output.path,
|
"signed_output": info.signed_output.path,
|
||||||
"signed_compressed_output": signed_compressed_output,
|
"signed_compressed_output": signed_compressed_output,
|
||||||
@@ -251,10 +255,12 @@ return json_encode({
|
|||||||
"backing_libs": info.backing_libs.path,
|
"backing_libs": info.backing_libs.path,
|
||||||
"bundle_file": info.base_with_config_zip.path,
|
"bundle_file": info.base_with_config_zip.path,
|
||||||
"installed_files": info.installed_files.path,
|
"installed_files": info.installed_files.path,
|
||||||
|
"make_modules_to_install": mk_info.make_modules_to_install,
|
||||||
})`
|
})`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApexInfo struct {
|
type ApexInfo struct {
|
||||||
|
// From the ApexInfo provider
|
||||||
SignedOutput string `json:"signed_output"`
|
SignedOutput string `json:"signed_output"`
|
||||||
SignedCompressedOutput string `json:"signed_compressed_output"`
|
SignedCompressedOutput string `json:"signed_compressed_output"`
|
||||||
UnsignedOutput string `json:"unsigned_output"`
|
UnsignedOutput string `json:"unsigned_output"`
|
||||||
@@ -268,6 +274,9 @@ type ApexInfo struct {
|
|||||||
BackingLibs string `json:"backing_libs"`
|
BackingLibs string `json:"backing_libs"`
|
||||||
BundleFile string `json:"bundle_file"`
|
BundleFile string `json:"bundle_file"`
|
||||||
InstalledFiles string `json:"installed_files"`
|
InstalledFiles string `json:"installed_files"`
|
||||||
|
|
||||||
|
// From the ApexMkInfo provider
|
||||||
|
MakeModulesToInstall []string `json:"make_modules_to_install"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
|
// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
|
||||||
|
@@ -177,9 +177,11 @@ func TestGetApexInfoParseResults(t *testing.T) {
|
|||||||
"backing_libs":"path/to/backing.txt",
|
"backing_libs":"path/to/backing.txt",
|
||||||
"bundle_file": "dir/bundlefile.zip",
|
"bundle_file": "dir/bundlefile.zip",
|
||||||
"installed_files":"path/to/installed-files.txt",
|
"installed_files":"path/to/installed-files.txt",
|
||||||
"provides_native_libs":[]
|
"provides_native_libs":[],
|
||||||
|
"make_modules_to_install": ["foo","bar"]
|
||||||
}`,
|
}`,
|
||||||
expectedOutput: ApexInfo{
|
expectedOutput: ApexInfo{
|
||||||
|
// ApexInfo
|
||||||
SignedOutput: "my.apex",
|
SignedOutput: "my.apex",
|
||||||
UnsignedOutput: "my.apex.unsigned",
|
UnsignedOutput: "my.apex.unsigned",
|
||||||
RequiresLibs: []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
|
RequiresLibs: []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
|
||||||
@@ -191,6 +193,9 @@ func TestGetApexInfoParseResults(t *testing.T) {
|
|||||||
BackingLibs: "path/to/backing.txt",
|
BackingLibs: "path/to/backing.txt",
|
||||||
BundleFile: "dir/bundlefile.zip",
|
BundleFile: "dir/bundlefile.zip",
|
||||||
InstalledFiles: "path/to/installed-files.txt",
|
InstalledFiles: "path/to/installed-files.txt",
|
||||||
|
|
||||||
|
// ApexMkInfo
|
||||||
|
MakeModulesToInstall: []string{"foo", "bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
2
cc/cc.go
2
cc/cc.go
@@ -1445,6 +1445,8 @@ func isBionic(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InstallToBootstrap(name string, config android.Config) bool {
|
func InstallToBootstrap(name string, config android.Config) bool {
|
||||||
|
// NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
|
||||||
|
// if this list is updated.
|
||||||
if name == "libclang_rt.hwasan" {
|
if name == "libclang_rt.hwasan" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user