aboutsummaryrefslogtreecommitdiff
path: root/sdk/member_type.go
blob: 98f59820b730ad239edfa67a9ac3599fdc30695f (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
// Copyright (C) 2021 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 sdk

import (
	"reflect"

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

// Contains information about the sdk properties that list sdk members by type, e.g.
// Java_header_libs.
type sdkMemberTypeListProperty struct {
	// getter for the list of member names
	getter func(properties interface{}) []string

	// setter for the list of member names
	setter func(properties interface{}, list []string)

	// the type of member referenced in the list
	memberType android.SdkMemberType

	// the dependency tag used for items in this list that can be used to determine the memberType
	// for a resolved dependency.
	dependencyTag android.SdkMemberDependencyTag
}

func (p *sdkMemberTypeListProperty) propertyName() string {
	return p.memberType.SdkPropertyName()
}

// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
var dynamicSdkMemberTypesMap android.OncePer

// A dynamically generated set of member list properties and associated structure type.
type dynamicSdkMemberTypes struct {
	// The dynamically generated structure type.
	//
	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
	propertiesStructType reflect.Type

	// Information about each of the member type specific list properties.
	memberTypeListProperties []*sdkMemberTypeListProperty

	memberTypeToProperty map[android.SdkMemberType]*sdkMemberTypeListProperty
}

func (d *dynamicSdkMemberTypes) createMemberTypeListProperties() interface{} {
	return reflect.New(d.propertiesStructType).Interface()
}

func getDynamicSdkMemberTypes(key android.OnceKey, registeredTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
	// Get the cached value, creating new instance if necessary.
	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
		return createDynamicSdkMemberTypes(registeredTypes)
	}).(*dynamicSdkMemberTypes)
}

// Create the dynamicSdkMemberTypes from the list of registered member types.
//
// A struct is created which contains one exported field per member type corresponding to
// the SdkMemberType.SdkPropertyName() value.
//
// A list of sdkMemberTypeListProperty instances is created, one per member type that provides:
// * a reference to the member type.
// * a getter for the corresponding field in the properties struct.
// * a dependency tag that identifies the member type of a resolved dependency.
func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {

	var listProperties []*sdkMemberTypeListProperty
	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberTypeListProperty{}
	var fields []reflect.StructField

	// Iterate over the member types creating StructField and sdkMemberTypeListProperty objects.
	nextFieldIndex := 0
	for _, memberType := range sdkMemberTypes {

		p := memberType.SdkPropertyName()

		var getter func(properties interface{}) []string
		var setter func(properties interface{}, list []string)
		if memberType.RequiresBpProperty() {
			// Create a dynamic exported field for the member type's property.
			fields = append(fields, reflect.StructField{
				Name: proptools.FieldNameForProperty(p),
				Type: reflect.TypeOf([]string{}),
				Tag:  `android:"arch_variant"`,
			})

			// Copy the field index for use in the getter func as using the loop variable directly will
			// cause all funcs to use the last value.
			fieldIndex := nextFieldIndex
			nextFieldIndex += 1

			getter = func(properties interface{}) []string {
				// The properties is expected to be of the following form (where
				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
				//     properties *struct {<Module_types> []string, ....}
				//
				// Although it accesses the field by index the following reflection code is equivalent to:
				//    *properties.<Module_types>
				//
				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
				return list
			}

			setter = func(properties interface{}, list []string) {
				// The properties is expected to be of the following form (where
				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
				//     properties *struct {<Module_types> []string, ....}
				//
				// Although it accesses the field by index the following reflection code is equivalent to:
				//    *properties.<Module_types> = list
				//
				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
			}
		}

		// Create an sdkMemberTypeListProperty for the member type.
		memberListProperty := &sdkMemberTypeListProperty{
			getter:     getter,
			setter:     setter,
			memberType: memberType,

			// Dependencies added directly from member properties are always exported.
			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
		}

		memberTypeToProperty[memberType] = memberListProperty
		listProperties = append(listProperties, memberListProperty)
	}

	// Create a dynamic struct from the collated fields.
	propertiesStructType := reflect.StructOf(fields)

	return &dynamicSdkMemberTypes{
		memberTypeListProperties: listProperties,
		memberTypeToProperty:     memberTypeToProperty,
		propertiesStructType:     propertiesStructType,
	}
}