aboutsummaryrefslogtreecommitdiff
path: root/android/sdk.go
blob: 63e0bbeec2fa44a7fcaf79863da202b57128bcb9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
// Copyright (C) 2019 The Android Open Source Project
//
// 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 android

import (
	"fmt"
	"sort"
	"strings"

	"github.com/google/blueprint"
	"github.com/google/blueprint/proptools"
)

// minApiLevelForSdkSnapshot provides access to the min_sdk_version for MinApiLevelForSdkSnapshot
type minApiLevelForSdkSnapshot interface {
	MinSdkVersion(ctx EarlyModuleContext) ApiLevel
}

// MinApiLevelForSdkSnapshot returns the ApiLevel of the min_sdk_version of the supplied module.
//
// If the module does not provide a min_sdk_version then it defaults to 1.
func MinApiLevelForSdkSnapshot(ctx EarlyModuleContext, module Module) ApiLevel {
	minApiLevel := NoneApiLevel
	if m, ok := module.(minApiLevelForSdkSnapshot); ok {
		minApiLevel = m.MinSdkVersion(ctx)
	}
	if minApiLevel == NoneApiLevel {
		// The default min API level is 1.
		minApiLevel = uncheckedFinalApiLevel(1)
	}
	return minApiLevel
}

// SnapshotBuilder provides support for generating the build rules which will build the snapshot.
type SnapshotBuilder interface {
	// CopyToSnapshot generates a rule that will copy the src to the dest (which is a snapshot
	// relative path) and add the dest to the zip.
	CopyToSnapshot(src Path, dest string)

	// EmptyFile returns the path to an empty file.
	//
	// This can be used by sdk member types that need to create an empty file in the snapshot, simply
	// pass the value returned from this to the CopyToSnapshot() method.
	EmptyFile() Path

	// UnzipToSnapshot generates a rule that will unzip the supplied zip into the snapshot relative
	// directory destDir.
	UnzipToSnapshot(zipPath Path, destDir string)

	// AddPrebuiltModule adds a new prebuilt module to the snapshot.
	//
	// It is intended to be called from SdkMemberType.AddPrebuiltModule which can add module type
	// specific properties that are not variant specific. The following properties will be
	// automatically populated before returning.
	//
	// * name
	// * sdk_member_name
	// * prefer
	//
	// Properties that are variant specific will be handled by SdkMemberProperties structure.
	//
	// Each module created by this method can be output to the generated Android.bp file in two
	// different forms, depending on the setting of the SOONG_SDK_SNAPSHOT_VERSION build property.
	// The two forms are:
	// 1. A versioned Soong module that is referenced from a corresponding similarly versioned
	//    snapshot module.
	// 2. An unversioned Soong module that.
	//
	// See sdk/update.go for more information.
	AddPrebuiltModule(member SdkMember, moduleType string) BpModule

	// SdkMemberReferencePropertyTag returns a property tag to use when adding a property to a
	// BpModule that contains references to other sdk members.
	//
	// Using this will ensure that the reference is correctly output for both versioned and
	// unversioned prebuilts in the snapshot.
	//
	// "required: true" means that the property must only contain references to other members of the
	// sdk. Passing a reference to a module that is not a member of the sdk will result in a build
	// error.
	//
	// "required: false" means that the property can contain references to modules that are either
	// members or not members of the sdk. If a reference is to a module that is a non member then the
	// reference is left unchanged, i.e. it is not transformed as references to members are.
	//
	// The handling of the member names is dependent on whether it is an internal or exported member.
	// An exported member is one whose name is specified in one of the member type specific
	// properties. An internal member is one that is added due to being a part of an exported (or
	// other internal) member and is not itself an exported member.
	//
	// Member names are handled as follows:
	// * When creating the unversioned form of the module the name is left unchecked unless the member
	//   is internal in which case it is transformed into an sdk specific name, i.e. by prefixing with
	//   the sdk name.
	//
	// * When creating the versioned form of the module the name is transformed into a versioned sdk
	//   specific name, i.e. by prefixing with the sdk name and suffixing with the version.
	//
	// e.g.
	// bpPropertySet.AddPropertyWithTag("libs", []string{"member1", "member2"}, builder.SdkMemberReferencePropertyTag(true))
	SdkMemberReferencePropertyTag(required bool) BpPropertyTag
}

// BpPropertyTag is a marker interface that can be associated with properties in a BpPropertySet to
// provide additional information which can be used to customize their behavior.
type BpPropertyTag interface{}

// BpPropertySet is a set of properties for use in a .bp file.
type BpPropertySet interface {
	// AddProperty adds a property.
	//
	// The value can be one of the following types:
	// * string
	// * array of the above
	// * bool
	// For these types it is an error if multiple properties with the same name
	// are added.
	//
	// * pointer to a struct
	// * BpPropertySet
	//
	// A pointer to a Blueprint-style property struct is first converted into a
	// BpPropertySet by traversing the fields and adding their values as
	// properties in a BpPropertySet. A field with a struct value is itself
	// converted into a BpPropertySet before adding.
	//
	// Adding a BpPropertySet is done as follows:
	// * If no property with the name exists then the BpPropertySet is added
	//   directly to this property. Care must be taken to ensure that it does not
	//   introduce a cycle.
	// * If a property exists with the name and the current value is a
	//   BpPropertySet then every property of the new BpPropertySet is added to
	//   the existing BpPropertySet.
	// * Otherwise, if a property exists with the name then it is an error.
	AddProperty(name string, value interface{})

	// AddPropertyWithTag adds a property with an associated property tag.
	AddPropertyWithTag(name string, value interface{}, tag BpPropertyTag)

	// AddPropertySet adds a property set with the specified name and returns it so that additional
	// properties can be added to it.
	AddPropertySet(name string) BpPropertySet

	// AddCommentForProperty adds a comment for the named property (or property set).
	AddCommentForProperty(name, text string)
}

// BpModule represents a module definition in a .bp file.
type BpModule interface {
	BpPropertySet

	// ModuleType returns the module type of the module
	ModuleType() string

	// Name returns the name of the module or "" if no name has been specified.
	Name() string
}

// BpPrintable is a marker interface that must be implemented by any struct that is added as a
// property value.
type BpPrintable interface {
	bpPrintable()
}

// BpPrintableBase must be embedded within any struct that is added as a
// property value.
type BpPrintableBase struct {
}

func (b BpPrintableBase) bpPrintable() {
}

var _ BpPrintable = BpPrintableBase{}

// sdkRegisterable defines the interface that must be implemented by objects that can be registered
// in an sdkRegistry.
type sdkRegisterable interface {
	// SdkPropertyName returns the name of the corresponding property on an sdk module.
	SdkPropertyName() string
}

// sdkRegistry provides support for registering and retrieving objects that define properties for
// use by sdk and module_exports module types.
type sdkRegistry struct {
	// The list of registered objects sorted by property name.
	list []sdkRegisterable
}

// copyAndAppend creates a new sdkRegistry that includes all the traits registered in
// this registry plus the supplied trait.
func (r *sdkRegistry) copyAndAppend(registerable sdkRegisterable) *sdkRegistry {
	oldList := r.list

	// Make sure that list does not already contain the property. Uses a simple linear search instead
	// of a binary search even though the list is sorted. That is because the number of items in the
	// list is small and so not worth the overhead of a binary search.
	found := false
	newPropertyName := registerable.SdkPropertyName()
	for _, r := range oldList {
		if r.SdkPropertyName() == newPropertyName {
			found = true
			break
		}
	}
	if found {
		names := []string{}
		for _, r := range oldList {
			names = append(names, r.SdkPropertyName())
		}
		panic(fmt.Errorf("duplicate properties found, %q already exists in %q", newPropertyName, names))
	}

	// Copy the slice just in case this is being read while being modified, e.g. when testing.
	list := make([]sdkRegisterable, 0, len(oldList)+1)
	list = append(list, oldList...)
	list = append(list, registerable)

	// Sort the registered objects by their property name to ensure that registry order has no effect
	// on behavior.
	sort.Slice(list, func(i1, i2 int) bool {
		t1 := list[i1]
		t2 := list[i2]

		return t1.SdkPropertyName() < t2.SdkPropertyName()
	})

	// Create a new registry so the pointer uniquely identifies the set of registered types.
	return &sdkRegistry{
		list: list,
	}
}

// registeredObjects returns the list of registered instances.
func (r *sdkRegistry) registeredObjects() []sdkRegisterable {
	return r.list
}

// uniqueOnceKey returns a key that uniquely identifies this instance and can be used with
// OncePer.Once
func (r *sdkRegistry) uniqueOnceKey() OnceKey {
	// Use the pointer to the registry as the unique key. The pointer is used because it is guaranteed
	// to uniquely identify the contained list. The list itself cannot be used as slices are not
	// comparable. Using the pointer does mean that two separate registries with identical lists would
	// have different keys and so cause whatever information is cached to be created multiple times.
	// However, that is not an issue in practice as it should not occur outside tests. Constructing a
	// string representation of the list to use instead would avoid that but is an unnecessary
	// complication that provides no significant benefit.
	return NewCustomOnceKey(r)
}

// SdkMemberTrait represents a trait that members of an sdk module can contribute to the sdk
// snapshot.
//
// A trait is simply a characteristic of sdk member that is not required by default which may be
// required for some members but not others. Traits can cause additional information to be output
// to the sdk snapshot or replace the default information exported for a member with something else.
// e.g.
//   - By default cc libraries only export the default image variants to the SDK. However, for some
//     members it may be necessary to export specific image variants, e.g. vendor, or recovery.
//   - By default cc libraries export all the configured architecture variants except for the native
//     bridge architecture variants. However, for some members it may be necessary to export the
//     native bridge architecture variants as well.
//   - By default cc libraries export the platform variant (i.e. sdk:). However, for some members it
//     may be necessary to export the sdk variant (i.e. sdk:sdk).
//
// A sdk can request a module to provide no traits, one trait or a collection of traits. The exact
// behavior of a trait is determined by how SdkMemberType implementations handle the traits. A trait
// could be specific to one SdkMemberType or many. Some trait combinations could be incompatible.
//
// The sdk module type will create a special traits structure that contains a property for each
// trait registered with RegisterSdkMemberTrait(). The property names are those returned from
// SdkPropertyName(). Each property contains a list of modules that are required to have that trait.
// e.g. something like this:
//
//	sdk {
//	  name: "sdk",
//	  ...
//	  traits: {
//	    recovery_image: ["module1", "module4", "module5"],
//	    native_bridge: ["module1", "module2"],
//	    native_sdk: ["module1", "module3"],
//	    ...
//	  },
//	  ...
//	}
type SdkMemberTrait interface {
	// SdkPropertyName returns the name of the traits property on an sdk module.
	SdkPropertyName() string
}

var _ sdkRegisterable = (SdkMemberTrait)(nil)

// SdkMemberTraitBase is the base struct that must be embedded within any type that implements
// SdkMemberTrait.
type SdkMemberTraitBase struct {
	// PropertyName is the name of the property
	PropertyName string
}

func (b *SdkMemberTraitBase) SdkPropertyName() string {
	return b.PropertyName
}

// SdkMemberTraitSet is a set of SdkMemberTrait instances.
type SdkMemberTraitSet interface {
	// Empty returns true if this set is empty.
	Empty() bool

	// Contains returns true if this set contains the specified trait.
	Contains(trait SdkMemberTrait) bool

	// Subtract returns a new set containing all elements of this set except for those in the
	// other set.
	Subtract(other SdkMemberTraitSet) SdkMemberTraitSet

	// String returns a string representation of the set and its contents.
	String() string
}

func NewSdkMemberTraitSet(traits []SdkMemberTrait) SdkMemberTraitSet {
	if len(traits) == 0 {
		return EmptySdkMemberTraitSet()
	}

	m := sdkMemberTraitSet{}
	for _, trait := range traits {
		m[trait] = true
	}
	return m
}

func EmptySdkMemberTraitSet() SdkMemberTraitSet {
	return (sdkMemberTraitSet)(nil)
}

type sdkMemberTraitSet map[SdkMemberTrait]bool

var _ SdkMemberTraitSet = (sdkMemberTraitSet{})

func (s sdkMemberTraitSet) Empty() bool {
	return len(s) == 0
}

func (s sdkMemberTraitSet) Contains(trait SdkMemberTrait) bool {
	return s[trait]
}

func (s sdkMemberTraitSet) Subtract(other SdkMemberTraitSet) SdkMemberTraitSet {
	if other.Empty() {
		return s
	}

	var remainder []SdkMemberTrait
	for trait, _ := range s {
		if !other.Contains(trait) {
			remainder = append(remainder, trait)
		}
	}

	return NewSdkMemberTraitSet(remainder)
}

func (s sdkMemberTraitSet) String() string {
	list := []string{}
	for trait, _ := range s {
		list = append(list, trait.SdkPropertyName())
	}
	sort.Strings(list)
	return fmt.Sprintf("[%s]", strings.Join(list, ","))
}

var registeredSdkMemberTraits = &sdkRegistry{}

// RegisteredSdkMemberTraits returns a OnceKey and a sorted list of registered traits.
//
// The key uniquely identifies the array of traits and can be used with OncePer.Once() to cache
// information derived from the array of traits.
func RegisteredSdkMemberTraits() (OnceKey, []SdkMemberTrait) {
	registerables := registeredSdkMemberTraits.registeredObjects()
	traits := make([]SdkMemberTrait, len(registerables))
	for i, registerable := range registerables {
		traits[i] = registerable.(SdkMemberTrait)
	}
	return registeredSdkMemberTraits.uniqueOnceKey(), traits
}

// RegisterSdkMemberTrait registers an SdkMemberTrait object to allow them to be used in the
// module_exports, module_exports_snapshot, sdk and sdk_snapshot module types.
func RegisterSdkMemberTrait(trait SdkMemberTrait) {
	registeredSdkMemberTraits = registeredSdkMemberTraits.copyAndAppend(trait)
}

// SdkMember is an individual member of the SDK.
//
// It includes all of the variants that the SDK depends upon.
type SdkMember interface {
	// Name returns the name of the member.
	Name() string

	// Variants returns all the variants of this module depended upon by the SDK.
	Variants() []Module
}

// SdkMemberDependencyTag is the interface that a tag must implement in order to allow the
// dependent module to be automatically added to the sdk.
type SdkMemberDependencyTag interface {
	blueprint.DependencyTag

	// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
	// to the sdk.
	//
	// Returning nil will prevent the module being added to the sdk.
	SdkMemberType(child Module) SdkMemberType

	// ExportMember determines whether a module added to the sdk through this tag will be exported
	// from the sdk or not.
	//
	// An exported member is added to the sdk using its own name, e.g. if "foo" was exported from sdk
	// "bar" then its prebuilt would be simply called "foo". A member can be added to the sdk via
	// multiple tags and if any of those tags returns true from this method then the membe will be
	// exported. Every module added directly to the sdk via one of the member type specific
	// properties, e.g. java_libs, will automatically be exported.
	//
	// If a member is not exported then it is treated as an internal implementation detail of the
	// sdk and so will be added with an sdk specific name. e.g. if "foo" was an internal member of sdk
	// "bar" then its prebuilt would be called "bar_foo". Additionally its visibility will be set to
	// "//visibility:private" so it will not be accessible from outside its Android.bp file.
	ExportMember() bool
}

var _ SdkMemberDependencyTag = (*sdkMemberDependencyTag)(nil)
var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)

type sdkMemberDependencyTag struct {
	blueprint.BaseDependencyTag
	memberType SdkMemberType
	export     bool
}

func (t *sdkMemberDependencyTag) SdkMemberType(_ Module) SdkMemberType {
	return t.memberType
}

func (t *sdkMemberDependencyTag) ExportMember() bool {
	return t.export
}

// ReplaceSourceWithPrebuilt prevents dependencies from the sdk/module_exports onto their members
// from being replaced with a preferred prebuilt.
func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
	return false
}

// DependencyTagForSdkMemberType creates an SdkMemberDependencyTag that will cause any
// dependencies added by the tag to be added to the sdk as the specified SdkMemberType and exported
// (or not) as specified by the export parameter.
func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberDependencyTag {
	return &sdkMemberDependencyTag{memberType: memberType, export: export}
}

// SdkMemberType is the interface that must be implemented for every type that can be a member of an
// sdk.
//
// The basic implementation should look something like this, where ModuleType is
// the name of the module type being supported.
//
//	type moduleTypeSdkMemberType struct {
//	    android.SdkMemberTypeBase
//	}
//
//	func init() {
//	    android.RegisterSdkMemberType(&moduleTypeSdkMemberType{
//	        SdkMemberTypeBase: android.SdkMemberTypeBase{
//	            PropertyName: "module_types",
//	        },
//	    }
//	}
//
//	...methods...
type SdkMemberType interface {
	// SdkPropertyName returns the name of the member type property on an sdk module.
	SdkPropertyName() string

	// RequiresBpProperty returns true if this member type requires its property to be usable within
	// an Android.bp file.
	RequiresBpProperty() bool

	// SupportedBuildReleases returns the string representation of a set of target build releases that
	// support this member type.
	SupportedBuildReleases() string

	// UsableWithSdkAndSdkSnapshot returns true if the member type supports the sdk/sdk_snapshot,
	// false otherwise.
	UsableWithSdkAndSdkSnapshot() bool

	// IsHostOsDependent returns true if prebuilt host artifacts may be specific to the host OS. Only
	// applicable to modules where HostSupported() is true. If this is true, snapshots will list each
	// host OS variant explicitly and disable all other host OS'es.
	IsHostOsDependent() bool

	// SupportedLinkages returns the names of the linkage variants supported by this module.
	SupportedLinkages() []string

	// ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false
	// otherwise.
	ArePrebuiltsRequired() bool

	// AddDependencies adds dependencies from the SDK module to all the module variants the member
	// type contributes to the SDK. `names` is the list of module names given in the member type
	// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
	// required is determined by the SDK and its properties. The dependencies must be added with the
	// supplied tag.
	//
	// The BottomUpMutatorContext provided is for the SDK module.
	AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string)

	// IsInstance returns true if the supplied module is an instance of this member type.
	//
	// This is used to check the type of each variant before added to the SdkMember. Returning false
	// will cause an error to be logged explaining that the module is not allowed in whichever sdk
	// property it was added.
	IsInstance(module Module) bool

	// UsesSourceModuleTypeInSnapshot returns true when the AddPrebuiltModule() method returns a
	// source module type.
	UsesSourceModuleTypeInSnapshot() bool

	// AddPrebuiltModule is called to add a prebuilt module that the sdk will populate.
	//
	// The sdk module code generates the snapshot as follows:
	//
	// * A properties struct of type SdkMemberProperties is created for each variant and
	//   populated with information from the variant by calling PopulateFromVariant(Module)
	//   on the struct.
	//
	// * An additional properties struct is created into which the common properties will be
	//   added.
	//
	// * The variant property structs are analysed to find exported (capitalized) fields which
	//   have common values. Those fields are cleared and the common value added to the common
	//   properties.
	//
	//   A field annotated with a tag of `sdk:"ignore"` will be treated as if it
	//   was not capitalized, i.e. ignored and not optimized for common values.
	//
	//   A field annotated with a tag of `sdk:"keep"` will not be cleared even if the value is common
	//   across multiple structs. Common values will still be copied into the common property struct.
	//   So, if the same value is placed in all structs populated from variants that value would be
	//   copied into all common property structs and so be available in every instance.
	//
	//   A field annotated with a tag of `android:"arch_variant"` will be allowed to have
	//   values that differ by arch, fields not tagged as such must have common values across
	//   all variants.
	//
	// * Additional field tags can be specified on a field that will ignore certain values
	//   for the purpose of common value optimization. A value that is ignored must have the
	//   default value for the property type. This is to ensure that significant value are not
	//   ignored by accident. The purpose of this is to allow the snapshot generation to reflect
	//   the behavior of the runtime. e.g. if a property is ignored on the host then a property
	//   that is common for android can be treated as if it was common for android and host as
	//   the setting for host is ignored anyway.
	//   * `sdk:"ignored-on-host" - this indicates the property is ignored on the host variant.
	//
	// * The sdk module type populates the BpModule structure, creating the arch specific
	//   structure and calls AddToPropertySet(...) on the properties struct to add the member
	//   specific properties in the correct place in the structure.
	//
	AddPrebuiltModule(ctx SdkMemberContext, member SdkMember) BpModule

	// CreateVariantPropertiesStruct creates a structure into which variant specific properties can be
	// added.
	CreateVariantPropertiesStruct() SdkMemberProperties

	// SupportedTraits returns the set of traits supported by this member type.
	SupportedTraits() SdkMemberTraitSet

	// Overrides returns whether type overrides other SdkMemberType
	Overrides(SdkMemberType) bool
}

var _ sdkRegisterable = (SdkMemberType)(nil)

// SdkDependencyContext provides access to information needed by the SdkMemberType.AddDependencies()
// implementations.
type SdkDependencyContext interface {
	BottomUpMutatorContext

	// RequiredTraits returns the set of SdkMemberTrait instances that the sdk requires the named
	// member to provide.
	RequiredTraits(name string) SdkMemberTraitSet

	// RequiresTrait returns true if the sdk requires the member with the supplied name to provide the
	// supplied trait.
	RequiresTrait(name string, trait SdkMemberTrait) bool
}

// SdkMemberTypeBase is the base type for SdkMemberType implementations and must be embedded in any
// struct that implements SdkMemberType.
type SdkMemberTypeBase struct {
	PropertyName string

	// Property names that this SdkMemberTypeBase can override, this is useful when a module type is a
	// superset of another module type.
	OverridesPropertyNames map[string]bool

	// The names of linkage variants supported by this module.
	SupportedLinkageNames []string

	// When set to true BpPropertyNotRequired indicates that the member type does not require the
	// property to be specifiable in an Android.bp file.
	BpPropertyNotRequired bool

	// The name of the first targeted build release.
	//
	// If not specified then it is assumed to be available on all targeted build releases.
	SupportedBuildReleaseSpecification string

	// Set to true if this must be usable with the sdk/sdk_snapshot module types. Otherwise, it will
	// only be usable with module_exports/module_exports_snapshots module types.
	SupportsSdk bool

	// Set to true if prebuilt host artifacts of this member may be specific to the host OS. Only
	// applicable to modules where HostSupported() is true.
	HostOsDependent bool

	// When set to true UseSourceModuleTypeInSnapshot indicates that the member type creates a source
	// module type in its SdkMemberType.AddPrebuiltModule() method. That prevents the sdk snapshot
	// code from automatically adding a prefer: true flag.
	UseSourceModuleTypeInSnapshot bool

	// Set to proptools.BoolPtr(false) if this member does not generate prebuilts but is only provided
	// to allow the sdk to gather members from this member's dependencies. If not specified then
	// defaults to true.
	PrebuiltsRequired *bool

	// The list of supported traits.
	Traits []SdkMemberTrait
}

func (b *SdkMemberTypeBase) SdkPropertyName() string {
	return b.PropertyName
}

func (b *SdkMemberTypeBase) RequiresBpProperty() bool {
	return !b.BpPropertyNotRequired
}

func (b *SdkMemberTypeBase) SupportedBuildReleases() string {
	return b.SupportedBuildReleaseSpecification
}

func (b *SdkMemberTypeBase) UsableWithSdkAndSdkSnapshot() bool {
	return b.SupportsSdk
}

func (b *SdkMemberTypeBase) IsHostOsDependent() bool {
	return b.HostOsDependent
}

func (b *SdkMemberTypeBase) ArePrebuiltsRequired() bool {
	return proptools.BoolDefault(b.PrebuiltsRequired, true)
}

func (b *SdkMemberTypeBase) UsesSourceModuleTypeInSnapshot() bool {
	return b.UseSourceModuleTypeInSnapshot
}

func (b *SdkMemberTypeBase) SupportedTraits() SdkMemberTraitSet {
	return NewSdkMemberTraitSet(b.Traits)
}

func (b *SdkMemberTypeBase) Overrides(other SdkMemberType) bool {
	return b.OverridesPropertyNames[other.SdkPropertyName()]
}

func (b *SdkMemberTypeBase) SupportedLinkages() []string {
	return b.SupportedLinkageNames
}

// registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
// modules.
var registeredModuleExportsMemberTypes = &sdkRegistry{}

// registeredSdkMemberTypes is the set of registered registeredSdkMemberTypes for sdk modules.
var registeredSdkMemberTypes = &sdkRegistry{}

// RegisteredSdkMemberTypes returns a OnceKey and a sorted list of registered types.
//
// If moduleExports is true then the slice of types includes all registered types that can be used
// with the module_exports and module_exports_snapshot module types. Otherwise, the slice of types
// only includes those registered types that can be used with the sdk and sdk_snapshot module
// types.
//
// The key uniquely identifies the array of types and can be used with OncePer.Once() to cache
// information derived from the array of types.
func RegisteredSdkMemberTypes(moduleExports bool) (OnceKey, []SdkMemberType) {
	var registry *sdkRegistry
	if moduleExports {
		registry = registeredModuleExportsMemberTypes
	} else {
		registry = registeredSdkMemberTypes
	}

	registerables := registry.registeredObjects()
	types := make([]SdkMemberType, len(registerables))
	for i, registerable := range registerables {
		types[i] = registerable.(SdkMemberType)
	}
	return registry.uniqueOnceKey(), types
}

// RegisterSdkMemberType registers an SdkMemberType object to allow them to be used in the
// module_exports, module_exports_snapshot and (depending on the value returned from
// SdkMemberType.UsableWithSdkAndSdkSnapshot) the sdk and sdk_snapshot module types.
func RegisterSdkMemberType(memberType SdkMemberType) {
	// All member types are usable with module_exports.
	registeredModuleExportsMemberTypes = registeredModuleExportsMemberTypes.copyAndAppend(memberType)

	// Only those that explicitly indicate it are usable with sdk.
	if memberType.UsableWithSdkAndSdkSnapshot() {
		registeredSdkMemberTypes = registeredSdkMemberTypes.copyAndAppend(memberType)
	}
}

// SdkMemberPropertiesBase is the base structure for all implementations of SdkMemberProperties and
// must be embedded in any struct that implements SdkMemberProperties.
//
// Contains common properties that apply across many different member types.
type SdkMemberPropertiesBase struct {
	// The number of unique os types supported by the member variants.
	//
	// If a member has a variant with more than one os type then it will need to differentiate
	// the locations of any of their prebuilt files in the snapshot by os type to prevent them
	// from colliding. See OsPrefix().
	//
	// Ignore this property during optimization. This is needed because this property is the same for
	// all variants of a member and so would be optimized away if it was not ignored.
	Os_count int `sdk:"ignore"`

	// The os type for which these properties refer.
	//
	// Provided to allow a member to differentiate between os types in the locations of their
	// prebuilt files when it supports more than one os type.
	//
	// Ignore this property during optimization. This is needed because this property is the same for
	// all variants of a member and so would be optimized away if it was not ignored.
	Os OsType `sdk:"ignore"`

	// The setting to use for the compile_multilib property.
	Compile_multilib string `android:"arch_variant"`
}

// OsPrefix returns the os prefix to use for any file paths in the sdk.
//
// Is an empty string if the member only provides variants for a single os type, otherwise
// is the OsType.Name.
func (b *SdkMemberPropertiesBase) OsPrefix() string {
	if b.Os_count == 1 {
		return ""
	} else {
		return b.Os.Name
	}
}

func (b *SdkMemberPropertiesBase) Base() *SdkMemberPropertiesBase {
	return b
}

// SdkMemberProperties is the interface to be implemented on top of a structure that contains
// variant specific information.
//
// Struct fields that are capitalized are examined for common values to extract. Fields that are not
// capitalized are assumed to be arch specific.
type SdkMemberProperties interface {
	// Base returns the base structure.
	Base() *SdkMemberPropertiesBase

	// PopulateFromVariant populates this structure with information from a module variant.
	//
	// It will typically be called once for each variant of a member module that the SDK depends upon.
	PopulateFromVariant(ctx SdkMemberContext, variant Module)

	// AddToPropertySet adds the information from this structure to the property set.
	//
	// This will be called for each instance of this structure on which the PopulateFromVariant method
	// was called and also on a number of different instances of this structure into which properties
	// common to one or more variants have been copied. Therefore, implementations of this must handle
	// the case when this structure is only partially populated.
	AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet)
}

// SdkMemberContext provides access to information common to a specific member.
type SdkMemberContext interface {

	// SdkModuleContext returns the module context of the sdk common os variant which is creating the
	// snapshot.
	//
	// This is common to all members of the sdk and is not specific to the member being processed.
	// If information about the member being processed needs to be obtained from this ModuleContext it
	// must be obtained using one of the OtherModule... methods not the Module... methods.
	SdkModuleContext() ModuleContext

	// SnapshotBuilder the builder of the snapshot.
	SnapshotBuilder() SnapshotBuilder

	// MemberType returns the type of the member currently being processed.
	MemberType() SdkMemberType

	// Name returns the name of the member currently being processed.
	//
	// Provided for use by sdk members to create a member specific location within the snapshot
	// into which to copy the prebuilt files.
	Name() string

	// RequiresTrait returns true if this member is expected to provide the specified trait.
	RequiresTrait(trait SdkMemberTrait) bool

	// IsTargetBuildBeforeTiramisu return true if the target build release for which this snapshot is
	// being generated is before Tiramisu, i.e. S.
	IsTargetBuildBeforeTiramisu() bool
}

// ExportedComponentsInfo contains information about the components that this module exports to an
// sdk snapshot.
//
// A component of a module is a child module that the module creates and which forms an integral
// part of the functionality that the creating module provides. A component module is essentially
// owned by its creator and is tightly coupled to the creator and other components.
//
// e.g. the child modules created by prebuilt_apis are not components because they are not tightly
// coupled to the prebuilt_apis module. Once they are created the prebuilt_apis ignores them. The
// child impl and stub library created by java_sdk_library (and corresponding import) are components
// because the creating module depends upon them in order to provide some of its own functionality.
//
// A component is exported if it is part of an sdk snapshot. e.g. The xml and impl child modules are
// components but they are not exported as they are not part of an sdk snapshot.
//
// This information is used by the sdk snapshot generation code to ensure that it does not create
// an sdk snapshot that contains a declaration of the component module and the module that creates
// it as that would result in duplicate modules when attempting to use the snapshot. e.g. a snapshot
// that included the java_sdk_library_import "foo" and also a java_import "foo.stubs" would fail
// as there would be two modules called "foo.stubs".
type ExportedComponentsInfo struct {
	// The names of the exported components.
	Components []string
}

var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})

// AdditionalSdkInfo contains additional properties to add to the generated SDK info file.
type AdditionalSdkInfo struct {
	Properties map[string]interface{}
}

var AdditionalSdkInfoProvider = blueprint.NewProvider(AdditionalSdkInfo{})