• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2021 The Android Open Source Project
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 api
16
17import (
18	"slices"
19
20	"github.com/google/blueprint/proptools"
21
22	"android/soong/android"
23	"android/soong/java"
24)
25
26const art = "art.module.public.api"
27const conscrypt = "conscrypt.module.public.api"
28const i18n = "i18n.module.public.api"
29const virtualization = "framework-virtualization"
30const location = "framework-location"
31const platformCrashrecovery = "framework-platformcrashrecovery"
32const ondeviceintelligence = "framework-ondeviceintelligence-platform"
33
34var core_libraries_modules = []string{art, conscrypt, i18n}
35
36// List of modules that are not yet updatable, and hence they can still compile
37// against hidden APIs. These modules are filtered out when building the
38// updatable-framework-module-impl (because updatable-framework-module-impl is
39// built against module_current SDK). Instead they are directly statically
40// linked into the all-framework-module-lib, which is building against hidden
41// APIs.
42// In addition, the modules in this list are allowed to contribute to test APIs
43// stubs.
44var non_updatable_modules = []string{virtualization, location, platformCrashrecovery, ondeviceintelligence}
45
46// The intention behind this soong plugin is to generate a number of "merged"
47// API-related modules that would otherwise require a large amount of very
48// similar Android.bp boilerplate to define. For example, the merged current.txt
49// API definitions (created by merging the non-updatable current.txt with all
50// the module current.txts). This simplifies the addition of new android
51// modules, by reducing the number of genrules etc a new module must be added to.
52
53// The properties of the combined_apis module type.
54type CombinedApisProperties struct {
55	// Module libraries in the bootclasspath
56	Bootclasspath proptools.Configurable[[]string]
57	// Module libraries on the bootclasspath if include_nonpublic_framework_api is true.
58	Conditional_bootclasspath []string
59	// Module libraries in system server
60	System_server_classpath proptools.Configurable[[]string]
61}
62
63type CombinedApis struct {
64	android.ModuleBase
65
66	properties CombinedApisProperties
67}
68
69func init() {
70	registerBuildComponents(android.InitRegistrationContext)
71}
72
73func registerBuildComponents(ctx android.RegistrationContext) {
74	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
75}
76
77var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
78
79func (a *CombinedApis) apiFingerprintStubDeps(ctx android.BottomUpMutatorContext) []string {
80	bootClasspath := a.properties.Bootclasspath.GetOrDefault(ctx, nil)
81	systemServerClasspath := a.properties.System_server_classpath.GetOrDefault(ctx, nil)
82	var ret []string
83	ret = append(
84		ret,
85		transformArray(bootClasspath, "", ".stubs")...,
86	)
87	ret = append(
88		ret,
89		transformArray(bootClasspath, "", ".stubs.system")...,
90	)
91	ret = append(
92		ret,
93		transformArray(bootClasspath, "", ".stubs.module_lib")...,
94	)
95	ret = append(
96		ret,
97		transformArray(systemServerClasspath, "", ".stubs.system_server")...,
98	)
99	return ret
100}
101
102func (a *CombinedApis) DepsMutator(ctx android.BottomUpMutatorContext) {
103	ctx.AddDependency(ctx.Module(), nil, a.apiFingerprintStubDeps(ctx)...)
104}
105
106func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
107	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
108		javaInfo, ok := android.OtherModuleProvider(ctx, child, java.JavaInfoProvider)
109		if ok && javaInfo.AndroidLibraryDependencyInfo != nil && child.Name() != "framework-res" {
110			// Stubs of BCP and SSCP libraries should not have any dependencies on apps
111			// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
112			ctx.ModuleErrorf(
113				"Module %s is not a valid dependency of the stub library %s\n."+
114					"If this dependency has been added via `libs` of java_sdk_library, please move it to `impl_only_libs`\n",
115				child.Name(), parent.Name())
116			return false // error detected
117		}
118		return true
119	})
120
121}
122
123type genruleProps struct {
124	Name       *string
125	Cmd        *string
126	Dists      []android.Dist
127	Out        []string
128	Srcs       proptools.Configurable[[]string]
129	Tools      []string
130	Visibility []string
131}
132
133type libraryProps struct {
134	Name            *string
135	Sdk_version     *string
136	Static_libs     proptools.Configurable[[]string]
137	Visibility      []string
138	Defaults        []string
139	Is_stubs_module *bool
140}
141
142type fgProps struct {
143	Name               *string
144	Srcs               proptools.Configurable[[]string]
145	Device_common_srcs proptools.Configurable[[]string]
146	Visibility         []string
147}
148
149type defaultsProps struct {
150	Name                *string
151	Api_surface         *string
152	Api_contributions   []string
153	Defaults_visibility []string
154	Previous_api        *string
155}
156
157// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
158type MergedTxtDefinition struct {
159	// "current.txt" or "removed.txt"
160	TxtFilename string
161	// Filename in the new dist dir. "android.txt" or "android-removed.txt"
162	DistFilename string
163	// The module for the non-updatable / non-module part of the api.
164	BaseTxt string
165	// The list of modules that are relevant for this merged txt.
166	Modules proptools.Configurable[[]string]
167	// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
168	ModuleTag string
169	// public, system, module-lib or system-server
170	Scope string
171}
172
173func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubsTypeSuffix string, doDist bool) {
174	metalavaCmd := "$(location metalava)"
175	// Silence reflection warnings. See b/168689341
176	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
177	metalavaCmd += " --quiet merge-signatures --format=v2 "
178
179	filename := txt.TxtFilename
180	if txt.Scope != "public" {
181		filename = txt.Scope + "-" + filename
182	}
183	moduleName := ctx.ModuleName() + stubsTypeSuffix + filename
184
185	props := genruleProps{}
186	props.Name = proptools.StringPtr(moduleName)
187	props.Tools = []string{"metalava"}
188	props.Out = []string{filename}
189	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
190	props.Srcs = proptools.NewSimpleConfigurable([]string{txt.BaseTxt})
191	props.Srcs.Append(createSrcs(txt.Modules, txt.ModuleTag))
192	if doDist {
193		props.Dists = []android.Dist{
194			{
195				Targets: []string{"droidcore"},
196				Dir:     proptools.StringPtr("api"),
197				Dest:    proptools.StringPtr(filename),
198			},
199			{
200				Targets: []string{"api_txt", "sdk"},
201				Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
202				Dest:    proptools.StringPtr(txt.DistFilename),
203			},
204		}
205	}
206	props.Visibility = []string{"//visibility:public"}
207	ctx.CreateModule(java.GenRuleFactory, &props)
208}
209
210func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) {
211	for _, i := range []struct {
212		name    string
213		tag     string
214		modules proptools.Configurable[[]string]
215	}{
216		{
217			name:    "all-modules-public-annotations",
218			tag:     "{.public.annotations.zip}",
219			modules: modules,
220		}, {
221			name:    "all-modules-system-annotations",
222			tag:     "{.system.annotations.zip}",
223			modules: modules,
224		}, {
225			name:    "all-modules-module-lib-annotations",
226			tag:     "{.module-lib.annotations.zip}",
227			modules: modules,
228		}, {
229			name:    "all-modules-system-server-annotations",
230			tag:     "{.system-server.annotations.zip}",
231			modules: system_server_modules,
232		},
233	} {
234		props := fgProps{}
235		props.Name = proptools.StringPtr(i.name)
236		props.Device_common_srcs = createSrcs(i.modules, i.tag)
237		ctx.CreateModule(android.FileGroupFactory, &props)
238	}
239}
240
241func createMergedPublicStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
242	modules = modules.Clone()
243	transformConfigurableArray(modules, "", ".stubs")
244	props := libraryProps{}
245	props.Name = proptools.StringPtr("all-modules-public-stubs")
246	props.Static_libs = modules
247	props.Sdk_version = proptools.StringPtr("module_current")
248	props.Visibility = []string{"//frameworks/base"}
249	props.Is_stubs_module = proptools.BoolPtr(true)
250	ctx.CreateModule(java.LibraryFactory, &props)
251}
252
253func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
254	modules = modules.Clone()
255	transformConfigurableArray(modules, "", ".stubs.exportable")
256	props := libraryProps{}
257	props.Name = proptools.StringPtr("all-modules-public-stubs-exportable")
258	props.Static_libs = modules
259	props.Sdk_version = proptools.StringPtr("module_current")
260	props.Visibility = []string{"//frameworks/base"}
261	props.Is_stubs_module = proptools.BoolPtr(true)
262	ctx.CreateModule(java.LibraryFactory, &props)
263}
264
265func createMergedSystemStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
266	// First create the all-updatable-modules-system-stubs
267	{
268		updatable_modules := modules.Clone()
269		removeAll(updatable_modules, non_updatable_modules)
270		transformConfigurableArray(updatable_modules, "", ".stubs.system")
271		props := libraryProps{}
272		props.Name = proptools.StringPtr("all-updatable-modules-system-stubs")
273		props.Static_libs = updatable_modules
274		props.Sdk_version = proptools.StringPtr("module_current")
275		props.Visibility = []string{"//frameworks/base"}
276		props.Is_stubs_module = proptools.BoolPtr(true)
277		ctx.CreateModule(java.LibraryFactory, &props)
278	}
279	// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
280	// into all-modules-system-stubs.
281	{
282		static_libs := transformArray(non_updatable_modules, "", ".stubs.system")
283		static_libs = append(static_libs, "all-updatable-modules-system-stubs")
284		props := libraryProps{}
285		props.Name = proptools.StringPtr("all-modules-system-stubs")
286		props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
287		props.Sdk_version = proptools.StringPtr("module_current")
288		props.Visibility = []string{"//frameworks/base"}
289		props.Is_stubs_module = proptools.BoolPtr(true)
290		ctx.CreateModule(java.LibraryFactory, &props)
291	}
292}
293
294func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
295	// First create the all-updatable-modules-system-stubs
296	{
297		updatable_modules := modules.Clone()
298		removeAll(updatable_modules, non_updatable_modules)
299		transformConfigurableArray(updatable_modules, "", ".stubs.exportable.system")
300		props := libraryProps{}
301		props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable")
302		props.Static_libs = updatable_modules
303		props.Sdk_version = proptools.StringPtr("module_current")
304		props.Visibility = []string{"//frameworks/base"}
305		props.Is_stubs_module = proptools.BoolPtr(true)
306		ctx.CreateModule(java.LibraryFactory, &props)
307	}
308	// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
309	// into all-modules-system-stubs.
310	{
311		static_libs := transformArray(non_updatable_modules, "", ".stubs.exportable.system")
312		static_libs = append(static_libs, "all-updatable-modules-system-stubs-exportable")
313		props := libraryProps{}
314		props.Name = proptools.StringPtr("all-modules-system-stubs-exportable")
315		props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
316		props.Sdk_version = proptools.StringPtr("module_current")
317		props.Visibility = []string{"//frameworks/base"}
318		props.Is_stubs_module = proptools.BoolPtr(true)
319		ctx.CreateModule(java.LibraryFactory, &props)
320	}
321}
322
323func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
324	props := libraryProps{}
325	props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
326	props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.test"))
327	props.Sdk_version = proptools.StringPtr("module_current")
328	props.Visibility = []string{"//frameworks/base"}
329	props.Is_stubs_module = proptools.BoolPtr(true)
330	ctx.CreateModule(java.LibraryFactory, &props)
331}
332
333func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) {
334	props := libraryProps{}
335	props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable")
336	props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.exportable.test"))
337	props.Sdk_version = proptools.StringPtr("module_current")
338	props.Visibility = []string{"//frameworks/base"}
339	props.Is_stubs_module = proptools.BoolPtr(true)
340	ctx.CreateModule(java.LibraryFactory, &props)
341}
342
343func createMergedFrameworkImpl(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
344	modules = modules.Clone()
345	// This module is for the "framework-all" module, which should not include the core libraries.
346	removeAll(modules, core_libraries_modules)
347	// Remove the modules that belong to non-updatable APEXes since those are allowed to compile
348	// against unstable APIs.
349	removeAll(modules, non_updatable_modules)
350	// First create updatable-framework-module-impl, which contains all updatable modules.
351	// This module compiles against module_lib SDK.
352	{
353		transformConfigurableArray(modules, "", ".impl")
354		props := libraryProps{}
355		props.Name = proptools.StringPtr("updatable-framework-module-impl")
356		props.Static_libs = modules
357		props.Sdk_version = proptools.StringPtr("module_current")
358		props.Visibility = []string{"//frameworks/base"}
359		ctx.CreateModule(java.LibraryFactory, &props)
360	}
361
362	// Now create all-framework-module-impl, which contains updatable-framework-module-impl
363	// and all non-updatable modules. This module compiles against hidden APIs.
364	{
365		static_libs := transformArray(non_updatable_modules, "", ".impl")
366		static_libs = append(static_libs, "updatable-framework-module-impl")
367		props := libraryProps{}
368		props.Name = proptools.StringPtr("all-framework-module-impl")
369		props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
370		props.Sdk_version = proptools.StringPtr("core_platform")
371		props.Visibility = []string{"//frameworks/base"}
372		ctx.CreateModule(java.LibraryFactory, &props)
373	}
374}
375
376func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
377	modules = modules.Clone()
378	// The user of this module compiles against the "core" SDK and against non-updatable modules,
379	// so remove to avoid dupes.
380	removeAll(modules, core_libraries_modules)
381	removeAll(modules, non_updatable_modules)
382	transformConfigurableArray(modules, "", ".stubs.exportable.module_lib")
383	props := libraryProps{}
384	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable")
385	props.Static_libs = modules
386	props.Sdk_version = proptools.StringPtr("module_current")
387	props.Visibility = []string{"//frameworks/base"}
388	props.Is_stubs_module = proptools.BoolPtr(true)
389	ctx.CreateModule(java.LibraryFactory, &props)
390}
391
392func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
393	modules = modules.Clone()
394	// The user of this module compiles against the "core" SDK and against non-updatable modules,
395	// so remove to avoid dupes.
396	removeAll(modules, core_libraries_modules)
397	removeAll(modules, non_updatable_modules)
398	transformConfigurableArray(modules, "", ".stubs.module_lib")
399	props := libraryProps{}
400	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
401	props.Static_libs = modules
402	props.Sdk_version = proptools.StringPtr("module_current")
403	props.Visibility = []string{"//frameworks/base"}
404	props.Is_stubs_module = proptools.BoolPtr(true)
405	ctx.CreateModule(java.LibraryFactory, &props)
406}
407
408func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath proptools.Configurable[[]string]) {
409	// The user of this module compiles against the "core" SDK and against non-updatable bootclasspathModules,
410	// so remove to avoid dupes.
411	bootclasspathModules := bootclasspath.Clone()
412	removeAll(bootclasspathModules, core_libraries_modules)
413	removeAll(bootclasspathModules, non_updatable_modules)
414	transformConfigurableArray(bootclasspathModules, "", ".stubs.exportable.module_lib")
415
416	system_server_classpath = system_server_classpath.Clone()
417	transformConfigurableArray(system_server_classpath, "", ".stubs.exportable.system_server")
418
419	// Include all the module-lib APIs from the bootclasspath libraries.
420	// Then add all the system-server APIs from the service-* libraries.
421	bootclasspathModules.Append(system_server_classpath)
422
423	props := libraryProps{}
424	props.Name = proptools.StringPtr("framework-updatable-stubs-system_server_api-exportable")
425	props.Static_libs = bootclasspathModules
426	props.Sdk_version = proptools.StringPtr("system_server_current")
427	props.Visibility = []string{"//frameworks/base"}
428	props.Is_stubs_module = proptools.BoolPtr(true)
429	ctx.CreateModule(java.LibraryFactory, &props)
430}
431
432func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
433	props := fgProps{}
434	props.Name = proptools.StringPtr("all-modules-public-stubs-source-exportable")
435	transformConfigurableArray(modules, "", ".stubs.source")
436	props.Device_common_srcs = createSrcs(modules, "{.exportable}")
437	props.Visibility = []string{"//frameworks/base"}
438	ctx.CreateModule(android.FileGroupFactory, &props)
439}
440
441func createMergedTxts(
442	ctx android.LoadHookContext,
443	bootclasspath proptools.Configurable[[]string],
444	system_server_classpath proptools.Configurable[[]string],
445	baseTxtModulePrefix string,
446	stubsTypeSuffix string,
447	doDist bool,
448) {
449	var textFiles []MergedTxtDefinition
450
451	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
452	distFilename := []string{"android.txt", "android-removed.txt"}
453	for i, f := range []string{"current.txt", "removed.txt"} {
454		textFiles = append(textFiles, MergedTxtDefinition{
455			TxtFilename:  f,
456			DistFilename: distFilename[i],
457			BaseTxt:      ":" + baseTxtModulePrefix + f,
458			Modules:      bootclasspath,
459			ModuleTag:    "{.public" + tagSuffix[i],
460			Scope:        "public",
461		})
462		textFiles = append(textFiles, MergedTxtDefinition{
463			TxtFilename:  f,
464			DistFilename: distFilename[i],
465			BaseTxt:      ":" + baseTxtModulePrefix + "system-" + f,
466			Modules:      bootclasspath,
467			ModuleTag:    "{.system" + tagSuffix[i],
468			Scope:        "system",
469		})
470		textFiles = append(textFiles, MergedTxtDefinition{
471			TxtFilename:  f,
472			DistFilename: distFilename[i],
473			BaseTxt:      ":" + baseTxtModulePrefix + "module-lib-" + f,
474			Modules:      bootclasspath,
475			ModuleTag:    "{.module-lib" + tagSuffix[i],
476			Scope:        "module-lib",
477		})
478		textFiles = append(textFiles, MergedTxtDefinition{
479			TxtFilename:  f,
480			DistFilename: distFilename[i],
481			BaseTxt:      ":" + baseTxtModulePrefix + "system-server-" + f,
482			Modules:      system_server_classpath,
483			ModuleTag:    "{.system-server" + tagSuffix[i],
484			Scope:        "system-server",
485		})
486	}
487	for _, txt := range textFiles {
488		createMergedTxt(ctx, txt, stubsTypeSuffix, doDist)
489	}
490}
491
492func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
493	bootclasspath := a.properties.Bootclasspath.Clone()
494	system_server_classpath := a.properties.System_server_classpath.Clone()
495	if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
496		bootclasspath.AppendSimpleValue(a.properties.Conditional_bootclasspath)
497	}
498	createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false)
499	createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true)
500
501	createMergedPublicStubs(ctx, bootclasspath)
502	createMergedSystemStubs(ctx, bootclasspath)
503	createMergedTestStubsForNonUpdatableModules(ctx)
504	createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
505	createMergedFrameworkImpl(ctx, bootclasspath)
506
507	createMergedPublicExportableStubs(ctx, bootclasspath)
508	createMergedSystemExportableStubs(ctx, bootclasspath)
509	createMergedTestExportableStubsForNonUpdatableModules(ctx)
510	createMergedFrameworkModuleLibExportableStubs(ctx, bootclasspath)
511	createMergedFrameworkSystemServerExportableStubs(ctx, bootclasspath, system_server_classpath)
512
513	createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
514
515	createPublicStubsSourceFilegroup(ctx, bootclasspath)
516}
517
518func combinedApisModuleFactory() android.Module {
519	module := &CombinedApis{}
520	module.AddProperties(&module.properties)
521	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
522	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
523	return module
524}
525
526// Various utility methods below.
527
528// Creates an array of ":<m><tag>" for each m in <modules>.
529func createSrcs(modules proptools.Configurable[[]string], tag string) proptools.Configurable[[]string] {
530	result := modules.Clone()
531	transformConfigurableArray(result, ":", tag)
532	return result
533}
534
535// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
536func transformArray(modules []string, prefix, suffix string) []string {
537	a := make([]string, 0, len(modules))
538	for _, module := range modules {
539		a = append(a, prefix+module+suffix)
540	}
541	return a
542}
543
544// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
545func transformConfigurableArray(modules proptools.Configurable[[]string], prefix, suffix string) {
546	modules.AddPostProcessor(func(s []string) []string {
547		return transformArray(s, prefix, suffix)
548	})
549}
550
551func removeAll(s proptools.Configurable[[]string], vs []string) {
552	s.AddPostProcessor(func(s []string) []string {
553		a := make([]string, 0, len(s))
554		for _, module := range s {
555			if !slices.Contains(vs, module) {
556				a = append(a, module)
557			}
558		}
559		return a
560	})
561}
562
563func remove(s []string, v string) []string {
564	s2 := make([]string, 0, len(s))
565	for _, sv := range s {
566		if sv != v {
567			s2 = append(s2, sv)
568		}
569	}
570	return s2
571}
572