diff --git a/android/sdk.go b/android/sdk.go index d13ad7d51..f28c39215 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -302,7 +302,46 @@ type SdkMemberType interface { // // The SdkMember is guaranteed to contain variants for which the // IsInstance(Module) method returned true. + // + // deprecated Use AddPrebuiltModule() instead. BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) + + // Add a prebuilt module that the sdk will populate. + // + // Returning nil from this will cause the sdk module type to use the deprecated BuildSnapshot + // method to build the snapshot. That method is deprecated because it requires the SdkMemberType + // implementation to do all the word. + // + // Otherwise, returning a non-nil value from this will cause the sdk module type to do the + // majority of the work to generate the snapshot. 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(SdkAware) + // 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. + // + // * 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. + // + // * Finally, the FinalizeModule(...) method is called to add any additional properties. + // This was created to allow the property ordering in existing tests to be maintained so + // as to avoid having to change tests while refactoring. + // + AddPrebuiltModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) BpModule + + // Add any additional properties to the end of the module. + FinalizeModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember, bpModule BpModule) + + // Create a structure into which variant specific properties can be added. + CreateVariantPropertiesStruct() SdkMemberProperties } // Base type for SdkMemberType implementations. @@ -324,6 +363,23 @@ func (b *SdkMemberTypeBase) HasTransitiveSdkMembers() bool { return b.TransitiveSdkMembers } +func (b *SdkMemberTypeBase) BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) { + panic("override AddPrebuiltModule") +} + +func (b *SdkMemberTypeBase) AddPrebuiltModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) BpModule { + // Returning nil causes the legacy BuildSnapshot method to be used. + return nil +} + +func (b *SdkMemberTypeBase) FinalizeModule(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember, module BpModule) { + // Do nothing by default +} + +func (b *SdkMemberTypeBase) CreateVariantPropertiesStruct() SdkMemberProperties { + panic("override me") +} + // Encapsulates the information about registered SdkMemberTypes. type SdkMemberTypesRegistry struct { // The list of types sorted by property name. @@ -389,3 +445,31 @@ func RegisterSdkMemberType(memberType SdkMemberType) { SdkMemberTypes = SdkMemberTypes.copyAndAppend(memberType) } } + +// Base structure for all implementations of SdkMemberProperties. +// +// Contains common properties that apply across many different member types. +type SdkMemberPropertiesBase struct { + // The setting to use for the compile_multilib property. + Compile_multilib string +} + +func (b *SdkMemberPropertiesBase) Base() *SdkMemberPropertiesBase { + return b +} + +// 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 { + // Access the base structure. + Base() *SdkMemberPropertiesBase + + // Populate the structure with information from the variant. + PopulateFromVariant(variant SdkAware) + + // Add the information from the structure to the property set. + AddToPropertySet(sdkModuleContext ModuleContext, builder SnapshotBuilder, propertySet BpPropertySet) +} diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go index 58d6ad0ad..eddf5b805 100644 --- a/cc/binary_sdk_member.go +++ b/cc/binary_sdk_member.go @@ -16,7 +16,6 @@ package cc import ( "path/filepath" - "strings" "android/soong/android" "github.com/google/blueprint" @@ -64,65 +63,13 @@ func (mt *binarySdkMemberType) IsInstance(module android.Module) bool { return false } -func (mt *binarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) { - info := mt.organizeVariants(member) - buildSharedNativeBinarySnapshot(info, builder, member) -} - -// Organize the variants by architecture. -func (mt *binarySdkMemberType) organizeVariants(member android.SdkMember) *nativeBinaryInfo { - memberName := member.Name() - info := &nativeBinaryInfo{ - name: memberName, - memberType: mt, - } - - for _, variant := range member.Variants() { - ccModule := variant.(*Module) - - info.archVariantProperties = append(info.archVariantProperties, nativeBinaryInfoProperties{ - name: memberName, - archType: ccModule.Target().Arch.ArchType.String(), - outputFile: ccModule.OutputFile().Path(), - }) - } - - // Initialize the unexported properties that will not be set during the - // extraction process. - info.commonProperties.name = memberName - - // Extract common properties from the arch specific properties. - extractCommonProperties(&info.commonProperties, info.archVariantProperties) - - return info -} - -func buildSharedNativeBinarySnapshot(info *nativeBinaryInfo, builder android.SnapshotBuilder, member android.SdkMember) { +func (mt *binarySdkMemberType) AddPrebuiltModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) android.BpModule { pbm := builder.AddPrebuiltModule(member, "cc_prebuilt_binary") - archVariantCount := len(info.archVariantProperties) + return pbm +} - // Choose setting for compile_multilib that is appropriate for the arch variants supplied. - var multilib string - if archVariantCount == 2 { - multilib = "both" - } else if archVariantCount == 1 { - if strings.HasSuffix(info.archVariantProperties[0].archType, "64") { - multilib = "64" - } else { - multilib = "32" - } - } - if multilib != "" { - pbm.AddProperty("compile_multilib", multilib) - } - - archProperties := pbm.AddPropertySet("arch") - for _, av := range info.archVariantProperties { - archTypeProperties := archProperties.AddPropertySet(av.archType) - archTypeProperties.AddProperty("srcs", []string{nativeBinaryPathFor(av)}) - - builder.CopyToSnapshot(av.outputFile, nativeBinaryPathFor(av)) - } +func (mt *binarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { + return &nativeBinaryInfoProperties{} } const ( @@ -140,8 +87,7 @@ func nativeBinaryPathFor(lib nativeBinaryInfoProperties) string { // The exported (capitalized) fields will be examined and may be changed during common value extraction. // The unexported fields will be left untouched. type nativeBinaryInfoProperties struct { - // The name of the library, is not exported as this must not be changed during optimization. - name string + android.SdkMemberPropertiesBase // archType is not exported as if set (to a non default value) it is always arch specific. // This is "" for common properties. @@ -151,10 +97,21 @@ type nativeBinaryInfoProperties struct { outputFile android.Path } -// nativeBinaryInfo represents a collection of arch-specific modules having the same name -type nativeBinaryInfo struct { - name string - memberType *binarySdkMemberType - archVariantProperties []nativeBinaryInfoProperties - commonProperties nativeBinaryInfoProperties +func (p *nativeBinaryInfoProperties) PopulateFromVariant(variant android.SdkAware) { + ccModule := variant.(*Module) + + p.archType = ccModule.Target().Arch.ArchType.String() + p.outputFile = ccModule.OutputFile().Path() +} + +func (p *nativeBinaryInfoProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) { + if p.Compile_multilib != "" { + propertySet.AddProperty("compile_multilib", p.Compile_multilib) + } + + if p.outputFile != nil { + propertySet.AddProperty("srcs", []string{nativeBinaryPathFor(*p)}) + + builder.CopyToSnapshot(p.outputFile, nativeBinaryPathFor(*p)) + } } diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index a36917eda..16b9e6f83 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -16,7 +16,6 @@ package cc import ( "path/filepath" - "reflect" "android/soong/android" "github.com/google/blueprint" @@ -96,48 +95,18 @@ func (mt *librarySdkMemberType) IsInstance(module android.Module) bool { return false } -// copy exported header files and stub *.so files -func (mt *librarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) { - info := mt.organizeVariants(member) - info.generatePrebuiltLibrary(sdkModuleContext, builder, member) +func (mt *librarySdkMemberType) AddPrebuiltModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) android.BpModule { + pbm := builder.AddPrebuiltModule(member, mt.prebuiltModuleType) + return pbm } -// Organize the variants by architecture. -func (mt *librarySdkMemberType) organizeVariants(member android.SdkMember) *nativeLibInfo { - memberName := member.Name() - info := &nativeLibInfo{ - name: memberName, - memberType: mt, - } +func (mt *librarySdkMemberType) FinalizeModule(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember, bpModule android.BpModule) { + bpModule.AddProperty("stl", "none") + bpModule.AddProperty("system_shared_libs", []string{}) +} - for _, variant := range member.Variants() { - ccModule := variant.(*Module) - - // Separate out the generated include dirs (which are arch specific) from the - // include dirs (which may not be). - exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate( - ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory) - - info.archVariantProperties = append(info.archVariantProperties, nativeLibInfoProperties{ - name: memberName, - archType: ccModule.Target().Arch.ArchType.String(), - ExportedIncludeDirs: exportedIncludeDirs, - exportedGeneratedIncludeDirs: exportedGeneratedIncludeDirs, - ExportedSystemIncludeDirs: ccModule.ExportedSystemIncludeDirs(), - ExportedFlags: ccModule.ExportedFlags(), - exportedGeneratedHeaders: ccModule.ExportedGeneratedHeaders(), - outputFile: ccModule.OutputFile().Path(), - }) - } - - // Initialize the unexported properties that will not be set during the - // extraction process. - info.commonProperties.name = memberName - - // Extract common properties from the arch specific properties. - extractCommonProperties(&info.commonProperties, info.archVariantProperties) - - return info +func (mt *librarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { + return &nativeLibInfoProperties{memberType: mt} } func isGeneratedHeaderDirectory(p android.Path) bool { @@ -145,94 +114,9 @@ func isGeneratedHeaderDirectory(p android.Path) bool { return gen } -// Extract common properties from a slice of property structures of the same type. -// -// All the property structures must be of the same type. -// commonProperties - must be a pointer to the structure into which common properties will be added. -// inputPropertiesSlice - must be a slice of input properties structures. -// -// Iterates over each exported field (capitalized name) and checks to see whether they -// have the same value (using DeepEquals) across all the input properties. If it does not then no -// change is made. Otherwise, the common value is stored in the field in the commonProperties -// and the field in each of the input properties structure is set to its default value. -func extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) { - commonStructValue := reflect.ValueOf(commonProperties).Elem() - propertiesStructType := commonStructValue.Type() - - // Create an empty structure from which default values for the field can be copied. - emptyStructValue := reflect.New(propertiesStructType).Elem() - - for f := 0; f < propertiesStructType.NumField(); f++ { - // Check to see if all the structures have the same value for the field. The commonValue - // is nil on entry to the loop and if it is nil on exit then there is no common value, - // otherwise it points to the common value. - var commonValue *reflect.Value - sliceValue := reflect.ValueOf(inputPropertiesSlice) - - for i := 0; i < sliceValue.Len(); i++ { - structValue := sliceValue.Index(i) - fieldValue := structValue.Field(f) - if !fieldValue.CanInterface() { - // The field is not exported so ignore it. - continue - } - - if commonValue == nil { - // Use the first value as the commonProperties value. - commonValue = &fieldValue - } else { - // If the value does not match the current common value then there is - // no value in common so break out. - if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) { - commonValue = nil - break - } - } - } - - // If the fields all have a common value then store it in the common struct field - // and set the input struct's field to the empty value. - if commonValue != nil { - emptyValue := emptyStructValue.Field(f) - commonStructValue.Field(f).Set(*commonValue) - for i := 0; i < sliceValue.Len(); i++ { - structValue := sliceValue.Index(i) - fieldValue := structValue.Field(f) - fieldValue.Set(emptyValue) - } - } - } -} - -func (info *nativeLibInfo) generatePrebuiltLibrary(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) { - - pbm := builder.AddPrebuiltModule(member, info.memberType.prebuiltModuleType) - - addPossiblyArchSpecificProperties(sdkModuleContext, builder, info.commonProperties, pbm) - - archProperties := pbm.AddPropertySet("arch") - for _, av := range info.archVariantProperties { - archTypeProperties := archProperties.AddPropertySet(av.archType) - - // If the library has some link types then it produces an output binary file, otherwise it - // is header only. - if info.memberType.linkTypes != nil { - // Copy the generated library to the snapshot and add a reference to it in the .bp module. - nativeLibraryPath := nativeLibraryPathFor(av) - builder.CopyToSnapshot(av.outputFile, nativeLibraryPath) - archTypeProperties.AddProperty("srcs", []string{nativeLibraryPath}) - } - - // Add any arch specific properties inside the appropriate arch: {: {...}} block - addPossiblyArchSpecificProperties(sdkModuleContext, builder, av, archTypeProperties) - } - pbm.AddProperty("stl", "none") - pbm.AddProperty("system_shared_libs", []string{}) -} - type includeDirsProperty struct { // Accessor to retrieve the paths - pathsGetter func(libInfo nativeLibInfoProperties) android.Paths + pathsGetter func(libInfo *nativeLibInfoProperties) android.Paths // The name of the property in the prebuilt library, "" means there is no property. propertyName string @@ -252,7 +136,7 @@ var includeDirProperties = []includeDirsProperty{ // ExportedIncludeDirs lists directories that contains some header files to be // copied into a directory in the snapshot. The snapshot directories must be added to // the export_include_dirs property in the prebuilt module in the snapshot. - pathsGetter: func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.ExportedIncludeDirs }, + pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedIncludeDirs }, propertyName: "export_include_dirs", snapshotDir: nativeIncludeDir, copy: true, @@ -262,7 +146,7 @@ var includeDirProperties = []includeDirsProperty{ // ExportedSystemIncludeDirs lists directories that contains some system header files to // be copied into a directory in the snapshot. The snapshot directories must be added to // the export_system_include_dirs property in the prebuilt module in the snapshot. - pathsGetter: func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.ExportedSystemIncludeDirs }, + pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedSystemIncludeDirs }, propertyName: "export_system_include_dirs", snapshotDir: nativeIncludeDir, copy: true, @@ -273,7 +157,7 @@ var includeDirProperties = []includeDirsProperty{ // that are explicitly listed in the exportedGeneratedHeaders property. So, the contents // of these directories do not need to be copied, but these directories do need adding to // the export_include_dirs property in the prebuilt module in the snapshot. - pathsGetter: func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs }, + pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs }, propertyName: "export_include_dirs", snapshotDir: nativeGeneratedIncludeDir, copy: false, @@ -284,7 +168,7 @@ var includeDirProperties = []includeDirsProperty{ // specified in exportedGeneratedIncludeDirs must be copied into the snapshot. // As they are in a directory in exportedGeneratedIncludeDirs they do not need adding to a // property in the prebuilt module in the snapshot. - pathsGetter: func(libInfo nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders }, + pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders }, propertyName: "", snapshotDir: nativeGeneratedIncludeDir, copy: true, @@ -293,7 +177,14 @@ var includeDirProperties = []includeDirsProperty{ } // Add properties that may, or may not, be arch specific. -func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo nativeLibInfoProperties, outputProperties android.BpPropertySet) { +func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, libInfo *nativeLibInfoProperties, outputProperties android.BpPropertySet) { + + // Copy the generated library to the snapshot and add a reference to it in the .bp module. + if libInfo.outputFile != nil { + nativeLibraryPath := nativeLibraryPathFor(libInfo) + builder.CopyToSnapshot(libInfo.outputFile, nativeLibraryPath) + outputProperties.AddProperty("srcs", []string{nativeLibraryPath}) + } // Map from property name to the include dirs to add to the prebuilt module in the snapshot. includeDirs := make(map[string][]string) @@ -355,7 +246,7 @@ const ( ) // path to the native library. Relative to / -func nativeLibraryPathFor(lib nativeLibInfoProperties) string { +func nativeLibraryPathFor(lib *nativeLibInfoProperties) string { return filepath.Join(lib.archType, nativeStubDir, lib.outputFile.Base()) } @@ -365,6 +256,10 @@ func nativeLibraryPathFor(lib nativeLibInfoProperties) string { // The exported (capitalized) fields will be examined and may be changed during common value extraction. // The unexported fields will be left untouched. type nativeLibInfoProperties struct { + android.SdkMemberPropertiesBase + + memberType *librarySdkMemberType + // The name of the library, is not exported as this must not be changed during optimization. name string @@ -401,10 +296,29 @@ type nativeLibInfoProperties struct { outputFile android.Path } -// nativeLibInfo represents a collection of arch-specific modules having the same name -type nativeLibInfo struct { - name string - memberType *librarySdkMemberType - archVariantProperties []nativeLibInfoProperties - commonProperties nativeLibInfoProperties +func (p *nativeLibInfoProperties) PopulateFromVariant(variant android.SdkAware) { + ccModule := variant.(*Module) + + // If the library has some link types then it produces an output binary file, otherwise it + // is header only. + if p.memberType.linkTypes != nil { + p.outputFile = ccModule.OutputFile().Path() + } + + // Separate out the generated include dirs (which are arch specific) from the + // include dirs (which may not be). + exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate( + ccModule.ExportedIncludeDirs(), isGeneratedHeaderDirectory) + + p.name = variant.Name() + p.archType = ccModule.Target().Arch.ArchType.String() + p.ExportedIncludeDirs = exportedIncludeDirs + p.exportedGeneratedIncludeDirs = exportedGeneratedIncludeDirs + p.ExportedSystemIncludeDirs = ccModule.ExportedSystemIncludeDirs() + p.ExportedFlags = ccModule.ExportedFlags() + p.exportedGeneratedHeaders = ccModule.ExportedGeneratedHeaders() +} + +func (p *nativeLibInfoProperties) AddToPropertySet(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, propertySet android.BpPropertySet) { + addPossiblyArchSpecificProperties(sdkModuleContext, builder, p, propertySet) } diff --git a/sdk/update.go b/sdk/update.go index b3357778e..352cc32e4 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -251,7 +251,14 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) andro members, multilib := s.organizeMembers(ctx, memberRefs) for _, member := range members { - member.memberType.BuildSnapshot(ctx, builder, member) + memberType := member.memberType + prebuiltModule := memberType.AddPrebuiltModule(ctx, builder, member) + if prebuiltModule == nil { + // Fall back to legacy method of building a snapshot + memberType.BuildSnapshot(ctx, builder, member) + } else { + s.createMemberSnapshot(ctx, builder, member, prebuiltModule) + } } // Create a transformer that will transform an unversioned module into a versioned module. @@ -643,3 +650,137 @@ func (m *sdkMember) Name() string { func (m *sdkMember) Variants() []android.SdkAware { return m.variants } + +type baseInfo struct { + Properties android.SdkMemberProperties +} + +type osTypeSpecificInfo struct { + baseInfo + + // The list of arch type specific info for this os type. + archTypes []*archTypeSpecificInfo +} + +type archTypeSpecificInfo struct { + baseInfo + + archType android.ArchType +} + +func (s *sdk) createMemberSnapshot(sdkModuleContext android.ModuleContext, builder *snapshotBuilder, member *sdkMember, bpModule android.BpModule) { + + memberType := member.memberType + + // Group the properties for each variant by arch type. + osInfo := &osTypeSpecificInfo{} + osInfo.Properties = memberType.CreateVariantPropertiesStruct() + variants := member.Variants() + for _, variant := range variants { + var properties android.SdkMemberProperties + + // Get the info associated with the arch type inside the os info. + archType := variant.Target().Arch.ArchType + + archInfo := &archTypeSpecificInfo{archType: archType} + properties = memberType.CreateVariantPropertiesStruct() + archInfo.Properties = properties + + osInfo.archTypes = append(osInfo.archTypes, archInfo) + + properties.PopulateFromVariant(variant) + } + + var archProperties []android.SdkMemberProperties + for _, archInfo := range osInfo.archTypes { + archProperties = append(archProperties, archInfo.Properties) + } + + extractCommonProperties(osInfo.Properties, archProperties) + + // Choose setting for compile_multilib that is appropriate for the arch variants supplied. + var multilib string + archVariantCount := len(osInfo.archTypes) + if archVariantCount == 2 { + multilib = "both" + } else if archVariantCount == 1 { + if strings.HasSuffix(osInfo.archTypes[0].archType.Name, "64") { + multilib = "64" + } else { + multilib = "32" + } + } + + osInfo.Properties.Base().Compile_multilib = multilib + + osInfo.Properties.AddToPropertySet(sdkModuleContext, builder, bpModule) + + archPropertySet := bpModule.AddPropertySet("arch") + for _, av := range osInfo.archTypes { + archTypePropertySet := archPropertySet.AddPropertySet(av.archType.Name) + + av.Properties.AddToPropertySet(sdkModuleContext, builder, archTypePropertySet) + } + + memberType.FinalizeModule(sdkModuleContext, builder, member, bpModule) +} + +// Extract common properties from a slice of property structures of the same type. +// +// All the property structures must be of the same type. +// commonProperties - must be a pointer to the structure into which common properties will be added. +// inputPropertiesSlice - must be a slice of input properties structures. +// +// Iterates over each exported field (capitalized name) and checks to see whether they +// have the same value (using DeepEquals) across all the input properties. If it does not then no +// change is made. Otherwise, the common value is stored in the field in the commonProperties +// and the field in each of the input properties structure is set to its default value. +func extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) { + commonPropertiesValue := reflect.ValueOf(commonProperties) + commonStructValue := commonPropertiesValue.Elem() + propertiesStructType := commonStructValue.Type() + + // Create an empty structure from which default values for the field can be copied. + emptyStructValue := reflect.New(propertiesStructType).Elem() + + for f := 0; f < propertiesStructType.NumField(); f++ { + // Check to see if all the structures have the same value for the field. The commonValue + // is nil on entry to the loop and if it is nil on exit then there is no common value, + // otherwise it points to the common value. + var commonValue *reflect.Value + sliceValue := reflect.ValueOf(inputPropertiesSlice) + + for i := 0; i < sliceValue.Len(); i++ { + structValue := sliceValue.Index(i).Elem().Elem() + fieldValue := structValue.Field(f) + if !fieldValue.CanInterface() { + // The field is not exported so ignore it. + continue + } + + if commonValue == nil { + // Use the first value as the commonProperties value. + commonValue = &fieldValue + } else { + // If the value does not match the current common value then there is + // no value in common so break out. + if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) { + commonValue = nil + break + } + } + } + + // If the fields all have a common value then store it in the common struct field + // and set the input struct's field to the empty value. + if commonValue != nil { + emptyValue := emptyStructValue.Field(f) + commonStructValue.Field(f).Set(*commonValue) + for i := 0; i < sliceValue.Len(); i++ { + structValue := sliceValue.Index(i).Elem().Elem() + fieldValue := structValue.Field(f) + fieldValue.Set(emptyValue) + } + } + } +}