xref: /aosp_15_r20/frameworks/base/api/api.go (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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.WalkDeps(func(child, parent android.Module) bool {
108		if _, ok := child.(java.AndroidLibraryDependency); ok && child.Name() != "framework-res" {
109			// Stubs of BCP and SSCP libraries should not have any dependencies on apps
110			// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
111			ctx.ModuleErrorf(
112				"Module %s is not a valid dependency of the stub library %s\n."+
113					"If this dependency has been added via `libs` of java_sdk_library, please move it to `impl_only_libs`\n",
114				child.Name(), parent.Name())
115			return false // error detected
116		}
117		return true
118	})
119
120}
121
122type genruleProps struct {
123	Name       *string
124	Cmd        *string
125	Dists      []android.Dist
126	Out        []string
127	Srcs       proptools.Configurable[[]string]
128	Tools      []string
129	Visibility []string
130}
131
132type libraryProps struct {
133	Name            *string
134	Sdk_version     *string
135	Static_libs     proptools.Configurable[[]string]
136	Visibility      []string
137	Defaults        []string
138	Is_stubs_module *bool
139}
140
141type fgProps struct {
142	Name               *string
143	Srcs               proptools.Configurable[[]string]
144	Device_common_srcs proptools.Configurable[[]string]
145	Visibility         []string
146}
147
148type defaultsProps struct {
149	Name                *string
150	Api_surface         *string
151	Api_contributions   []string
152	Defaults_visibility []string
153	Previous_api        *string
154}
155
156// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
157type MergedTxtDefinition struct {
158	// "current.txt" or "removed.txt"
159	TxtFilename string
160	// Filename in the new dist dir. "android.txt" or "android-removed.txt"
161	DistFilename string
162	// The module for the non-updatable / non-module part of the api.
163	BaseTxt string
164	// The list of modules that are relevant for this merged txt.
165	Modules proptools.Configurable[[]string]
166	// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
167	ModuleTag string
168	// public, system, module-lib or system-server
169	Scope string
170}
171
172func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubsTypeSuffix string, doDist bool) {
173	metalavaCmd := "$(location metalava)"
174	// Silence reflection warnings. See b/168689341
175	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
176	metalavaCmd += " --quiet merge-signatures --format=v2 "
177
178	filename := txt.TxtFilename
179	if txt.Scope != "public" {
180		filename = txt.Scope + "-" + filename
181	}
182	moduleName := ctx.ModuleName() + stubsTypeSuffix + filename
183
184	props := genruleProps{}
185	props.Name = proptools.StringPtr(moduleName)
186	props.Tools = []string{"metalava"}
187	props.Out = []string{filename}
188	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
189	props.Srcs = proptools.NewSimpleConfigurable([]string{txt.BaseTxt})
190	props.Srcs.Append(createSrcs(txt.Modules, txt.ModuleTag))
191	if doDist {
192		props.Dists = []android.Dist{
193			{
194				Targets: []string{"droidcore"},
195				Dir:     proptools.StringPtr("api"),
196				Dest:    proptools.StringPtr(filename),
197			},
198			{
199				Targets: []string{"api_txt", "sdk"},
200				Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
201				Dest:    proptools.StringPtr(txt.DistFilename),
202			},
203		}
204	}
205	props.Visibility = []string{"//visibility:public"}
206	ctx.CreateModule(java.GenRuleFactory, &props)
207}
208
209func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) {
210	for _, i := range []struct {
211		name    string
212		tag     string
213		modules proptools.Configurable[[]string]
214	}{
215		{
216			name:    "all-modules-public-annotations",
217			tag:     "{.public.annotations.zip}",
218			modules: modules,
219		}, {
220			name:    "all-modules-system-annotations",
221			tag:     "{.system.annotations.zip}",
222			modules: modules,
223		}, {
224			name:    "all-modules-module-lib-annotations",
225			tag:     "{.module-lib.annotations.zip}",
226			modules: modules,
227		}, {
228			name:    "all-modules-system-server-annotations",
229			tag:     "{.system-server.annotations.zip}",
230			modules: system_server_modules,
231		},
232	} {
233		props := fgProps{}
234		props.Name = proptools.StringPtr(i.name)
235		props.Device_common_srcs = createSrcs(i.modules, i.tag)
236		ctx.CreateModule(android.FileGroupFactory, &props)
237	}
238}
239
240func createMergedPublicStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
241	modules = modules.Clone()
242	transformConfigurableArray(modules, "", ".stubs")
243	props := libraryProps{}
244	props.Name = proptools.StringPtr("all-modules-public-stubs")
245	props.Static_libs = modules
246	props.Sdk_version = proptools.StringPtr("module_current")
247	props.Visibility = []string{"//frameworks/base"}
248	props.Is_stubs_module = proptools.BoolPtr(true)
249	ctx.CreateModule(java.LibraryFactory, &props)
250}
251
252func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
253	modules = modules.Clone()
254	transformConfigurableArray(modules, "", ".stubs.exportable")
255	props := libraryProps{}
256	props.Name = proptools.StringPtr("all-modules-public-stubs-exportable")
257	props.Static_libs = modules
258	props.Sdk_version = proptools.StringPtr("module_current")
259	props.Visibility = []string{"//frameworks/base"}
260	props.Is_stubs_module = proptools.BoolPtr(true)
261	ctx.CreateModule(java.LibraryFactory, &props)
262}
263
264func createMergedSystemStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
265	// First create the all-updatable-modules-system-stubs
266	{
267		updatable_modules := modules.Clone()
268		removeAll(updatable_modules, non_updatable_modules)
269		transformConfigurableArray(updatable_modules, "", ".stubs.system")
270		props := libraryProps{}
271		props.Name = proptools.StringPtr("all-updatable-modules-system-stubs")
272		props.Static_libs = updatable_modules
273		props.Sdk_version = proptools.StringPtr("module_current")
274		props.Visibility = []string{"//frameworks/base"}
275		props.Is_stubs_module = proptools.BoolPtr(true)
276		ctx.CreateModule(java.LibraryFactory, &props)
277	}
278	// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
279	// into all-modules-system-stubs.
280	{
281		static_libs := transformArray(non_updatable_modules, "", ".stubs.system")
282		static_libs = append(static_libs, "all-updatable-modules-system-stubs")
283		props := libraryProps{}
284		props.Name = proptools.StringPtr("all-modules-system-stubs")
285		props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
286		props.Sdk_version = proptools.StringPtr("module_current")
287		props.Visibility = []string{"//frameworks/base"}
288		props.Is_stubs_module = proptools.BoolPtr(true)
289		ctx.CreateModule(java.LibraryFactory, &props)
290	}
291}
292
293func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
294	// First create the all-updatable-modules-system-stubs
295	{
296		updatable_modules := modules.Clone()
297		removeAll(updatable_modules, non_updatable_modules)
298		transformConfigurableArray(updatable_modules, "", ".stubs.exportable.system")
299		props := libraryProps{}
300		props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable")
301		props.Static_libs = updatable_modules
302		props.Sdk_version = proptools.StringPtr("module_current")
303		props.Visibility = []string{"//frameworks/base"}
304		props.Is_stubs_module = proptools.BoolPtr(true)
305		ctx.CreateModule(java.LibraryFactory, &props)
306	}
307	// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
308	// into all-modules-system-stubs.
309	{
310		static_libs := transformArray(non_updatable_modules, "", ".stubs.exportable.system")
311		static_libs = append(static_libs, "all-updatable-modules-system-stubs-exportable")
312		props := libraryProps{}
313		props.Name = proptools.StringPtr("all-modules-system-stubs-exportable")
314		props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
315		props.Sdk_version = proptools.StringPtr("module_current")
316		props.Visibility = []string{"//frameworks/base"}
317		props.Is_stubs_module = proptools.BoolPtr(true)
318		ctx.CreateModule(java.LibraryFactory, &props)
319	}
320}
321
322func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
323	props := libraryProps{}
324	props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
325	props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.test"))
326	props.Sdk_version = proptools.StringPtr("module_current")
327	props.Visibility = []string{"//frameworks/base"}
328	props.Is_stubs_module = proptools.BoolPtr(true)
329	ctx.CreateModule(java.LibraryFactory, &props)
330}
331
332func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) {
333	props := libraryProps{}
334	props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable")
335	props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.exportable.test"))
336	props.Sdk_version = proptools.StringPtr("module_current")
337	props.Visibility = []string{"//frameworks/base"}
338	props.Is_stubs_module = proptools.BoolPtr(true)
339	ctx.CreateModule(java.LibraryFactory, &props)
340}
341
342func createMergedFrameworkImpl(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
343	modules = modules.Clone()
344	// This module is for the "framework-all" module, which should not include the core libraries.
345	removeAll(modules, core_libraries_modules)
346	// Remove the modules that belong to non-updatable APEXes since those are allowed to compile
347	// against unstable APIs.
348	removeAll(modules, non_updatable_modules)
349	// First create updatable-framework-module-impl, which contains all updatable modules.
350	// This module compiles against module_lib SDK.
351	{
352		transformConfigurableArray(modules, "", ".impl")
353		props := libraryProps{}
354		props.Name = proptools.StringPtr("updatable-framework-module-impl")
355		props.Static_libs = modules
356		props.Sdk_version = proptools.StringPtr("module_current")
357		props.Visibility = []string{"//frameworks/base"}
358		ctx.CreateModule(java.LibraryFactory, &props)
359	}
360
361	// Now create all-framework-module-impl, which contains updatable-framework-module-impl
362	// and all non-updatable modules. This module compiles against hidden APIs.
363	{
364		static_libs := transformArray(non_updatable_modules, "", ".impl")
365		static_libs = append(static_libs, "updatable-framework-module-impl")
366		props := libraryProps{}
367		props.Name = proptools.StringPtr("all-framework-module-impl")
368		props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
369		props.Sdk_version = proptools.StringPtr("core_platform")
370		props.Visibility = []string{"//frameworks/base"}
371		ctx.CreateModule(java.LibraryFactory, &props)
372	}
373}
374
375func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
376	modules = modules.Clone()
377	// The user of this module compiles against the "core" SDK and against non-updatable modules,
378	// so remove to avoid dupes.
379	removeAll(modules, core_libraries_modules)
380	removeAll(modules, non_updatable_modules)
381	transformConfigurableArray(modules, "", ".stubs.exportable.module_lib")
382	props := libraryProps{}
383	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable")
384	props.Static_libs = modules
385	props.Sdk_version = proptools.StringPtr("module_current")
386	props.Visibility = []string{"//frameworks/base"}
387	props.Is_stubs_module = proptools.BoolPtr(true)
388	ctx.CreateModule(java.LibraryFactory, &props)
389}
390
391func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
392	modules = modules.Clone()
393	// The user of this module compiles against the "core" SDK and against non-updatable modules,
394	// so remove to avoid dupes.
395	removeAll(modules, core_libraries_modules)
396	removeAll(modules, non_updatable_modules)
397	transformConfigurableArray(modules, "", ".stubs.module_lib")
398	props := libraryProps{}
399	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
400	props.Static_libs = modules
401	props.Sdk_version = proptools.StringPtr("module_current")
402	props.Visibility = []string{"//frameworks/base"}
403	props.Is_stubs_module = proptools.BoolPtr(true)
404	ctx.CreateModule(java.LibraryFactory, &props)
405}
406
407func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath proptools.Configurable[[]string]) {
408	// The user of this module compiles against the "core" SDK and against non-updatable bootclasspathModules,
409	// so remove to avoid dupes.
410	bootclasspathModules := bootclasspath.Clone()
411	removeAll(bootclasspathModules, core_libraries_modules)
412	removeAll(bootclasspathModules, non_updatable_modules)
413	transformConfigurableArray(bootclasspathModules, "", ".stubs.exportable.module_lib")
414
415	system_server_classpath = system_server_classpath.Clone()
416	transformConfigurableArray(system_server_classpath, "", ".stubs.exportable.system_server")
417
418	// Include all the module-lib APIs from the bootclasspath libraries.
419	// Then add all the system-server APIs from the service-* libraries.
420	bootclasspathModules.Append(system_server_classpath)
421
422	props := libraryProps{}
423	props.Name = proptools.StringPtr("framework-updatable-stubs-system_server_api-exportable")
424	props.Static_libs = bootclasspathModules
425	props.Sdk_version = proptools.StringPtr("system_server_current")
426	props.Visibility = []string{"//frameworks/base"}
427	props.Is_stubs_module = proptools.BoolPtr(true)
428	ctx.CreateModule(java.LibraryFactory, &props)
429}
430
431func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
432	props := fgProps{}
433	props.Name = proptools.StringPtr("all-modules-public-stubs-source-exportable")
434	transformConfigurableArray(modules, "", ".stubs.source")
435	props.Device_common_srcs = createSrcs(modules, "{.exportable}")
436	props.Visibility = []string{"//frameworks/base"}
437	ctx.CreateModule(android.FileGroupFactory, &props)
438}
439
440func createMergedTxts(
441	ctx android.LoadHookContext,
442	bootclasspath proptools.Configurable[[]string],
443	system_server_classpath proptools.Configurable[[]string],
444	baseTxtModulePrefix string,
445	stubsTypeSuffix string,
446	doDist bool,
447) {
448	var textFiles []MergedTxtDefinition
449
450	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
451	distFilename := []string{"android.txt", "android-removed.txt"}
452	for i, f := range []string{"current.txt", "removed.txt"} {
453		textFiles = append(textFiles, MergedTxtDefinition{
454			TxtFilename:  f,
455			DistFilename: distFilename[i],
456			BaseTxt:      ":" + baseTxtModulePrefix + f,
457			Modules:      bootclasspath,
458			ModuleTag:    "{.public" + tagSuffix[i],
459			Scope:        "public",
460		})
461		textFiles = append(textFiles, MergedTxtDefinition{
462			TxtFilename:  f,
463			DistFilename: distFilename[i],
464			BaseTxt:      ":" + baseTxtModulePrefix + "system-" + f,
465			Modules:      bootclasspath,
466			ModuleTag:    "{.system" + tagSuffix[i],
467			Scope:        "system",
468		})
469		textFiles = append(textFiles, MergedTxtDefinition{
470			TxtFilename:  f,
471			DistFilename: distFilename[i],
472			BaseTxt:      ":" + baseTxtModulePrefix + "module-lib-" + f,
473			Modules:      bootclasspath,
474			ModuleTag:    "{.module-lib" + tagSuffix[i],
475			Scope:        "module-lib",
476		})
477		textFiles = append(textFiles, MergedTxtDefinition{
478			TxtFilename:  f,
479			DistFilename: distFilename[i],
480			BaseTxt:      ":" + baseTxtModulePrefix + "system-server-" + f,
481			Modules:      system_server_classpath,
482			ModuleTag:    "{.system-server" + tagSuffix[i],
483			Scope:        "system-server",
484		})
485	}
486	for _, txt := range textFiles {
487		createMergedTxt(ctx, txt, stubsTypeSuffix, doDist)
488	}
489}
490
491func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
492	bootclasspath := a.properties.Bootclasspath.Clone()
493	system_server_classpath := a.properties.System_server_classpath.Clone()
494	if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
495		bootclasspath.AppendSimpleValue(a.properties.Conditional_bootclasspath)
496	}
497	createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false)
498	createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true)
499
500	createMergedPublicStubs(ctx, bootclasspath)
501	createMergedSystemStubs(ctx, bootclasspath)
502	createMergedTestStubsForNonUpdatableModules(ctx)
503	createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
504	createMergedFrameworkImpl(ctx, bootclasspath)
505
506	createMergedPublicExportableStubs(ctx, bootclasspath)
507	createMergedSystemExportableStubs(ctx, bootclasspath)
508	createMergedTestExportableStubsForNonUpdatableModules(ctx)
509	createMergedFrameworkModuleLibExportableStubs(ctx, bootclasspath)
510	createMergedFrameworkSystemServerExportableStubs(ctx, bootclasspath, system_server_classpath)
511
512	createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
513
514	createPublicStubsSourceFilegroup(ctx, bootclasspath)
515}
516
517func combinedApisModuleFactory() android.Module {
518	module := &CombinedApis{}
519	module.AddProperties(&module.properties)
520	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
521	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
522	return module
523}
524
525// Various utility methods below.
526
527// Creates an array of ":<m><tag>" for each m in <modules>.
528func createSrcs(modules proptools.Configurable[[]string], tag string) proptools.Configurable[[]string] {
529	result := modules.Clone()
530	transformConfigurableArray(result, ":", tag)
531	return result
532}
533
534// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
535func transformArray(modules []string, prefix, suffix string) []string {
536	a := make([]string, 0, len(modules))
537	for _, module := range modules {
538		a = append(a, prefix+module+suffix)
539	}
540	return a
541}
542
543// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
544func transformConfigurableArray(modules proptools.Configurable[[]string], prefix, suffix string) {
545	modules.AddPostProcessor(func(s []string) []string {
546		return transformArray(s, prefix, suffix)
547	})
548}
549
550func removeAll(s proptools.Configurable[[]string], vs []string) {
551	s.AddPostProcessor(func(s []string) []string {
552		a := make([]string, 0, len(s))
553		for _, module := range s {
554			if !slices.Contains(vs, module) {
555				a = append(a, module)
556			}
557		}
558		return a
559	})
560}
561
562func remove(s []string, v string) []string {
563	s2 := make([]string, 0, len(s))
564	for _, sv := range s {
565		if sv != v {
566			s2 = append(s2, sv)
567		}
568	}
569	return s2
570}
571