diff --git a/android/license.go b/android/license.go index 587cb36de..ebee05576 100644 --- a/android/license.go +++ b/android/license.go @@ -63,7 +63,7 @@ func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) { func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) { // license modules have no licenses, but license_kinds must refer to license_kind modules mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName()) - mergePathProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, m.properties.License_text)...) + namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...) for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) { if lk, ok := module.(*licenseKindModule); ok { mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...) diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go index 2ce921bb0..b17defea5 100644 --- a/android/license_sdk_member.go +++ b/android/license_sdk_member.go @@ -90,7 +90,10 @@ func (p *licenseSdkMemberProperties) PopulateFromVariant(_ SdkMemberContext, var // Populate the properties from the variant. l := variant.(*licenseModule) p.License_kinds = l.properties.License_kinds - p.License_text = l.base().commonProperties.Effective_license_text + p.License_text = make(Paths, 0, len(l.base().commonProperties.Effective_license_text)) + for _, np := range l.base().commonProperties.Effective_license_text { + p.License_text = append(p.License_text, np.Path) + } } func (p *licenseSdkMemberProperties) AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet) { diff --git a/android/licenses.go b/android/licenses.go index b82d8d7d4..b51a06b24 100644 --- a/android/licenses.go +++ b/android/licenses.go @@ -213,7 +213,7 @@ func licensesPropertyFlattener(ctx ModuleContext) { m.base().commonProperties.Effective_package_name = l.properties.Package_name } mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...) - mergePathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...) + mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...) mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...) mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...) } else { @@ -239,10 +239,24 @@ func mergeStringProps(prop *[]string, values ...string) { *prop = SortedUniqueStrings(*prop) } -// Update a property Path array with a distinct union of its values and a list of new values. -func mergePathProps(prop *Paths, values ...Path) { +// Update a property NamedPath array with a distinct union of its values and a list of new values. +func namePathProps(prop *NamedPaths, name *string, values ...Path) { + if name == nil { + for _, value := range values { + *prop = append(*prop, NamedPath{value, ""}) + } + } else { + for _, value := range values { + *prop = append(*prop, NamedPath{value, *name}) + } + } + *prop = SortedUniqueNamedPaths(*prop) +} + +// Update a property NamedPath array with a distinct union of its values and a list of new values. +func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) { *prop = append(*prop, values...) - *prop = SortedUniquePaths(*prop) + *prop = SortedUniqueNamedPaths(*prop) } // Get the licenses property falling back to the package default. diff --git a/android/licenses_test.go b/android/licenses_test.go index 70160faf2..8a81e1294 100644 --- a/android/licenses_test.go +++ b/android/licenses_test.go @@ -90,9 +90,9 @@ var licensesTests = []struct { "libother": []string{"shownotice"}, }, effectiveNotices: map[string][]string{ - "libexample1": []string{"top/LICENSE", "top/NOTICE"}, - "libnested": []string{"top/LICENSE", "top/NOTICE"}, - "libother": []string{"top/LICENSE", "top/NOTICE"}, + "libexample1": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"}, + "libnested": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"}, + "libother": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"}, }, }, diff --git a/android/module.go b/android/module.go index d0807c3f6..03d3f80bc 100644 --- a/android/module.go +++ b/android/module.go @@ -16,11 +16,13 @@ package android import ( "fmt" + "net/url" "os" "path" "path/filepath" "reflect" "regexp" + "sort" "strings" "text/scanner" @@ -616,6 +618,53 @@ type Dist struct { Tag *string `android:"arch_variant"` } +// NamedPath associates a path with a name. e.g. a license text path with a package name +type NamedPath struct { + Path Path + Name string +} + +// String returns an escaped string representing the `NamedPath`. +func (p NamedPath) String() string { + if len(p.Name) > 0 { + return p.Path.String() + ":" + url.QueryEscape(p.Name) + } + return p.Path.String() +} + +// NamedPaths describes a list of paths each associated with a name. +type NamedPaths []NamedPath + +// Strings returns a list of escaped strings representing each `NamedPath` in the list. +func (l NamedPaths) Strings() []string { + result := make([]string, 0, len(l)) + for _, p := range l { + result = append(result, p.String()) + } + return result +} + +// SortedUniqueNamedPaths modifies `l` in place to return the sorted unique subset. +func SortedUniqueNamedPaths(l NamedPaths) NamedPaths { + if len(l) == 0 { + return l + } + sort.Slice(l, func(i, j int) bool { + return l[i].String() < l[j].String() + }) + k := 0 + for i := 1; i < len(l); i++ { + if l[i].String() == l[k].String() { + continue + } + k++ + if k < i { + l[k] = l[i] + } + } + return l[:k+1] +} + type nameProperties struct { // The name of the module. Must be unique across all modules. Name *string @@ -684,7 +733,7 @@ type commonProperties struct { // Override of module name when reporting licenses Effective_package_name *string `blueprint:"mutated"` // Notice files - Effective_license_text Paths `blueprint:"mutated"` + Effective_license_text NamedPaths `blueprint:"mutated"` // License names Effective_license_kinds []string `blueprint:"mutated"` // License conditions @@ -1801,7 +1850,11 @@ func (m *ModuleBase) ExportedToMake() bool { } func (m *ModuleBase) EffectiveLicenseFiles() Paths { - return m.commonProperties.Effective_license_text + result := make(Paths, 0, len(m.commonProperties.Effective_license_text)) + for _, p := range m.commonProperties.Effective_license_text { + result = append(result, p.Path) + } + return result } // computeInstallDeps finds the installed paths of all dependencies that have a dependency diff --git a/android/module_test.go b/android/module_test.go index c35e66ed6..a1bab6d01 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -816,3 +816,120 @@ test { }) } } + +func TestSortedUniqueNamedPaths(t *testing.T) { + type np struct { + path, name string + } + makePaths := func(l []np) NamedPaths { + result := make(NamedPaths, 0, len(l)) + for _, p := range l { + result = append(result, NamedPath{PathForTesting(p.path), p.name}) + } + return result + } + + tests := []struct { + name string + in []np + expectedOut []np + }{ + { + name: "empty", + in: []np{}, + expectedOut: []np{}, + }, + { + name: "all_same", + in: []np{ + {"a.txt", "A"}, + {"a.txt", "A"}, + {"a.txt", "A"}, + {"a.txt", "A"}, + {"a.txt", "A"}, + }, + expectedOut: []np{ + {"a.txt", "A"}, + }, + }, + { + name: "same_path_different_names", + in: []np{ + {"a.txt", "C"}, + {"a.txt", "A"}, + {"a.txt", "D"}, + {"a.txt", "B"}, + {"a.txt", "E"}, + }, + expectedOut: []np{ + {"a.txt", "A"}, + {"a.txt", "B"}, + {"a.txt", "C"}, + {"a.txt", "D"}, + {"a.txt", "E"}, + }, + }, + { + name: "different_paths_same_name", + in: []np{ + {"b/b.txt", "A"}, + {"a/a.txt", "A"}, + {"a/txt", "A"}, + {"b", "A"}, + {"a/b/d", "A"}, + }, + expectedOut: []np{ + {"a/a.txt", "A"}, + {"a/b/d", "A"}, + {"a/txt", "A"}, + {"b/b.txt", "A"}, + {"b", "A"}, + }, + }, + { + name: "all_different", + in: []np{ + {"b/b.txt", "A"}, + {"a/a.txt", "B"}, + {"a/txt", "D"}, + {"b", "C"}, + {"a/b/d", "E"}, + }, + expectedOut: []np{ + {"a/a.txt", "B"}, + {"a/b/d", "E"}, + {"a/txt", "D"}, + {"b/b.txt", "A"}, + {"b", "C"}, + }, + }, + { + name: "some_different", + in: []np{ + {"b/b.txt", "A"}, + {"a/a.txt", "B"}, + {"a/txt", "D"}, + {"a/b/d", "E"}, + {"b", "C"}, + {"a/a.txt", "B"}, + {"a/b/d", "E"}, + }, + expectedOut: []np{ + {"a/a.txt", "B"}, + {"a/b/d", "E"}, + {"a/txt", "D"}, + {"b/b.txt", "A"}, + {"b", "C"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := SortedUniqueNamedPaths(makePaths(tt.in)) + expected := makePaths(tt.expectedOut) + t.Logf("actual: %v", actual) + t.Logf("expected: %v", expected) + AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual) + }) + } +}