Merge "Package robolectric tests for tradefed"
diff --git a/OWNERS b/OWNERS
index dbb491d..8355d10 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,9 +6,8 @@
per-file * = patricearruda@google.com
per-file * = paulduffin@google.com
-per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
+per-file ndk_*.go = danalbert@google.com
per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
per-file tidy.go = srhines@google.com, chh@google.com
per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
per-file docs/map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com
-per-file *ndk_api_coverage_parser.py = sophiez@google.com
\ No newline at end of file
diff --git a/android/Android.bp b/android/Android.bp
index 2682279..6ddcc14 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -19,6 +19,7 @@
"csuite_config.go",
"defaults.go",
"defs.go",
+ "depset.go",
"expand.go",
"filegroup.go",
"hooks.go",
@@ -62,6 +63,7 @@
"arch_test.go",
"config_test.go",
"csuite_config_test.go",
+ "depset_test.go",
"expand_test.go",
"module_test.go",
"mutator_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 045cb59..dfc68c4 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -107,6 +107,25 @@
a.EntryMap[name] = []string{path.String()}
}
+func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) {
+ if path.Valid() {
+ a.SetPath(name, path.Path())
+ }
+}
+
+func (a *AndroidMkEntries) AddPath(name string, path Path) {
+ if _, ok := a.EntryMap[name]; !ok {
+ a.entryOrder = append(a.entryOrder, name)
+ }
+ a.EntryMap[name] = append(a.EntryMap[name], path.String())
+}
+
+func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) {
+ if path.Valid() {
+ a.AddPath(name, path.Path())
+ }
+}
+
func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
if flag {
if _, ok := a.EntryMap[name]; !ok {
diff --git a/android/config.go b/android/config.go
index 7073a49..d680b65 100644
--- a/android/config.go
+++ b/android/config.go
@@ -875,12 +875,12 @@
enforceList := c.productVariables.EnforceRROTargets
// TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
exemptedList := c.productVariables.EnforceRROExemptedTargets
- if exemptedList != nil {
+ if len(exemptedList) > 0 {
if InList(name, exemptedList) {
return false
}
}
- if enforceList != nil {
+ if len(enforceList) > 0 {
if InList("*", enforceList) {
return true
}
@@ -891,7 +891,7 @@
func (c *config) EnforceRROExcludedOverlay(path string) bool {
excluded := c.productVariables.EnforceRROExcludedOverlays
- if excluded != nil {
+ if len(excluded) > 0 {
return HasAnyPrefix(path, excluded)
}
return false
@@ -1059,7 +1059,7 @@
HasAnyPrefix(path, c.config.productVariables.JavaCoveragePaths) {
coverage = true
}
- if coverage && c.config.productVariables.JavaCoverageExcludePaths != nil {
+ if coverage && len(c.config.productVariables.JavaCoverageExcludePaths) > 0 {
if HasAnyPrefix(path, c.config.productVariables.JavaCoverageExcludePaths) {
coverage = false
}
@@ -1088,12 +1088,12 @@
// NativeCoveragePaths represents any path.
func (c *deviceConfig) NativeCoverageEnabledForPath(path string) bool {
coverage := false
- if c.config.productVariables.NativeCoveragePaths != nil {
+ if len(c.config.productVariables.NativeCoveragePaths) > 0 {
if InList("*", c.config.productVariables.NativeCoveragePaths) || HasAnyPrefix(path, c.config.productVariables.NativeCoveragePaths) {
coverage = true
}
}
- if coverage && c.config.productVariables.NativeCoverageExcludePaths != nil {
+ if coverage && len(c.config.productVariables.NativeCoverageExcludePaths) > 0 {
if HasAnyPrefix(path, c.config.productVariables.NativeCoverageExcludePaths) {
coverage = false
}
@@ -1164,21 +1164,21 @@
}
func (c *config) IntegerOverflowDisabledForPath(path string) bool {
- if c.productVariables.IntegerOverflowExcludePaths == nil {
+ if len(c.productVariables.IntegerOverflowExcludePaths) == 0 {
return false
}
return HasAnyPrefix(path, c.productVariables.IntegerOverflowExcludePaths)
}
func (c *config) CFIDisabledForPath(path string) bool {
- if c.productVariables.CFIExcludePaths == nil {
+ if len(c.productVariables.CFIExcludePaths) == 0 {
return false
}
return HasAnyPrefix(path, c.productVariables.CFIExcludePaths)
}
func (c *config) CFIEnabledForPath(path string) bool {
- if c.productVariables.CFIIncludePaths == nil {
+ if len(c.productVariables.CFIIncludePaths) == 0 {
return false
}
return HasAnyPrefix(path, c.productVariables.CFIIncludePaths)
diff --git a/android/depset.go b/android/depset.go
new file mode 100644
index 0000000..f707094
--- /dev/null
+++ b/android/depset.go
@@ -0,0 +1,190 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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"
+
+// DepSet is designed to be conceptually compatible with Bazel's depsets:
+// https://docs.bazel.build/versions/master/skylark/depsets.html
+
+// A DepSet efficiently stores Paths from transitive dependencies without copying. It is stored
+// as a DAG of DepSet nodes, each of which has some direct contents and a list of dependency
+// DepSet nodes.
+//
+// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order
+// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered
+// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that
+// elements of children are listed after all of their parents (unless there are duplicate direct
+// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
+// duplicated element is not guaranteed).
+//
+// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the Paths for direct contents
+// and the *DepSets of dependencies. A DepSet is immutable once created.
+type DepSet struct {
+ preorder bool
+ reverse bool
+ order DepSetOrder
+ direct Paths
+ transitive []*DepSet
+}
+
+// DepSetBuilder is used to create an immutable DepSet.
+type DepSetBuilder struct {
+ order DepSetOrder
+ direct Paths
+ transitive []*DepSet
+}
+
+type DepSetOrder int
+
+const (
+ PREORDER DepSetOrder = iota
+ POSTORDER
+ TOPOLOGICAL
+)
+
+func (o DepSetOrder) String() string {
+ switch o {
+ case PREORDER:
+ return "PREORDER"
+ case POSTORDER:
+ return "POSTORDER"
+ case TOPOLOGICAL:
+ return "TOPOLOGICAL"
+ default:
+ panic(fmt.Errorf("Invalid DepSetOrder %d", o))
+ }
+}
+
+// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
+func NewDepSet(order DepSetOrder, direct Paths, transitive []*DepSet) *DepSet {
+ var directCopy Paths
+ var transitiveCopy []*DepSet
+ if order == TOPOLOGICAL {
+ directCopy = ReversePaths(direct)
+ transitiveCopy = reverseDepSets(transitive)
+ } else {
+ // Use copy instead of append(nil, ...) to make a slice that is exactly the size of the input
+ // slice. The DepSet is immutable, there is no need for additional capacity.
+ directCopy = make(Paths, len(direct))
+ copy(directCopy, direct)
+ transitiveCopy = make([]*DepSet, len(transitive))
+ copy(transitiveCopy, transitive)
+ }
+
+ for _, dep := range transitive {
+ if dep.order != order {
+ panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
+ order, dep.order))
+ }
+ }
+
+ return &DepSet{
+ preorder: order == PREORDER,
+ reverse: order == TOPOLOGICAL,
+ order: order,
+ direct: directCopy,
+ transitive: transitiveCopy,
+ }
+}
+
+// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order.
+func NewDepSetBuilder(order DepSetOrder) *DepSetBuilder {
+ return &DepSetBuilder{order: order}
+}
+
+// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents.
+func (b *DepSetBuilder) Direct(direct ...Path) *DepSetBuilder {
+ b.direct = append(b.direct, direct...)
+ return b
+}
+
+// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
+// transitive contents are to the right of any existing transitive contents.
+func (b *DepSetBuilder) Transitive(transitive ...*DepSet) *DepSetBuilder {
+ b.transitive = append(b.transitive, transitive...)
+ return b
+}
+
+// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents
+// for creating more DepSets.
+func (b *DepSetBuilder) Build() *DepSet {
+ return NewDepSet(b.order, b.direct, b.transitive)
+}
+
+// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
+// otherwise postordered.
+func (d *DepSet) walk(visit func(Paths)) {
+ visited := make(map[*DepSet]bool)
+
+ var dfs func(d *DepSet)
+ dfs = func(d *DepSet) {
+ visited[d] = true
+ if d.preorder {
+ visit(d.direct)
+ }
+ for _, dep := range d.transitive {
+ if !visited[dep] {
+ dfs(dep)
+ }
+ }
+
+ if !d.preorder {
+ visit(d.direct)
+ }
+ }
+
+ dfs(d)
+}
+
+// ToList returns the DepSet flattened to a list. The order in the list is based on the order
+// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
+// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
+// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
+// its transitive dependencies, in which case the ordering of the duplicated element is not
+// guaranteed).
+func (d *DepSet) ToList() Paths {
+ var list Paths
+ d.walk(func(paths Paths) {
+ list = append(list, paths...)
+ })
+ list = FirstUniquePaths(list)
+ if d.reverse {
+ reversePathsInPlace(list)
+ }
+ return list
+}
+
+// ToSortedList returns the direct and transitive contents of a DepSet in lexically sorted order
+// with duplicates removed.
+func (d *DepSet) ToSortedList() Paths {
+ list := d.ToList()
+ return SortedUniquePaths(list)
+}
+
+func reversePathsInPlace(list Paths) {
+ for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
+ list[i], list[j] = list[j], list[i]
+ }
+}
+
+func reverseDepSets(list []*DepSet) []*DepSet {
+ ret := make([]*DepSet, len(list))
+ for i := range list {
+ ret[i] = list[len(list)-1-i]
+ }
+ return ret
+}
diff --git a/android/depset_test.go b/android/depset_test.go
new file mode 100644
index 0000000..c328127
--- /dev/null
+++ b/android/depset_test.go
@@ -0,0 +1,304 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func ExampleDepSet_ToList_postordered() {
+ a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build()
+ b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+ c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+ d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+ fmt.Println(d.ToList().Strings())
+ // Output: [a b c d]
+}
+
+func ExampleDepSet_ToList_preordered() {
+ a := NewDepSetBuilder(PREORDER).Direct(PathForTesting("a")).Build()
+ b := NewDepSetBuilder(PREORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+ c := NewDepSetBuilder(PREORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+ d := NewDepSetBuilder(PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+ fmt.Println(d.ToList().Strings())
+ // Output: [d b a c]
+}
+
+func ExampleDepSet_ToList_topological() {
+ a := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("a")).Build()
+ b := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build()
+ c := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build()
+ d := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+ fmt.Println(d.ToList().Strings())
+ // Output: [d b c a]
+}
+
+func ExampleDepSet_ToSortedList() {
+ a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build()
+ b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+ c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+ d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+ fmt.Println(d.ToSortedList().Strings())
+ // Output: [a b c d]
+}
+
+// Tests based on Bazel's ExpanderTestBase.java to ensure compatibility
+// https://github.com/bazelbuild/bazel/blob/master/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
+func TestDepSet(t *testing.T) {
+ a := PathForTesting("a")
+ b := PathForTesting("b")
+ c := PathForTesting("c")
+ c2 := PathForTesting("c2")
+ d := PathForTesting("d")
+ e := PathForTesting("e")
+
+ tests := []struct {
+ name string
+ depSet func(t *testing.T, order DepSetOrder) *DepSet
+ postorder, preorder, topological []string
+ }{
+ {
+ name: "simple",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ return NewDepSet(order, Paths{c, a, b}, nil)
+ },
+ postorder: []string{"c", "a", "b"},
+ preorder: []string{"c", "a", "b"},
+ topological: []string{"c", "a", "b"},
+ },
+ {
+ name: "simpleNoDuplicates",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ return NewDepSet(order, Paths{c, a, a, a, b}, nil)
+ },
+ postorder: []string{"c", "a", "b"},
+ preorder: []string{"c", "a", "b"},
+ topological: []string{"c", "a", "b"},
+ },
+ {
+ name: "nesting",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ subset := NewDepSet(order, Paths{c, a, e}, nil)
+ return NewDepSet(order, Paths{b, d}, []*DepSet{subset})
+ },
+ postorder: []string{"c", "a", "e", "b", "d"},
+ preorder: []string{"b", "d", "c", "a", "e"},
+ topological: []string{"b", "d", "c", "a", "e"},
+ },
+ {
+ name: "builderReuse",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ assertEquals := func(t *testing.T, w, g Paths) {
+ if !reflect.DeepEqual(w, g) {
+ t.Errorf("want %q, got %q", w, g)
+ }
+ }
+ builder := NewDepSetBuilder(order)
+ assertEquals(t, nil, builder.Build().ToList())
+
+ builder.Direct(b)
+ assertEquals(t, Paths{b}, builder.Build().ToList())
+
+ builder.Direct(d)
+ assertEquals(t, Paths{b, d}, builder.Build().ToList())
+
+ child := NewDepSetBuilder(order).Direct(c, a, e).Build()
+ builder.Transitive(child)
+ return builder.Build()
+ },
+ postorder: []string{"c", "a", "e", "b", "d"},
+ preorder: []string{"b", "d", "c", "a", "e"},
+ topological: []string{"b", "d", "c", "a", "e"},
+ },
+ {
+ name: "builderChaining",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ return NewDepSetBuilder(order).Direct(b).Direct(d).
+ Transitive(NewDepSetBuilder(order).Direct(c, a, e).Build()).Build()
+ },
+ postorder: []string{"c", "a", "e", "b", "d"},
+ preorder: []string{"b", "d", "c", "a", "e"},
+ topological: []string{"b", "d", "c", "a", "e"},
+ },
+ {
+ name: "transitiveDepsHandledSeparately",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ subset := NewDepSetBuilder(order).Direct(c, a, e).Build()
+ builder := NewDepSetBuilder(order)
+ // The fact that we add the transitive subset between the Direct(b) and Direct(d)
+ // calls should not change the result.
+ builder.Direct(b)
+ builder.Transitive(subset)
+ builder.Direct(d)
+ return builder.Build()
+ },
+ postorder: []string{"c", "a", "e", "b", "d"},
+ preorder: []string{"b", "d", "c", "a", "e"},
+ topological: []string{"b", "d", "c", "a", "e"},
+ },
+ {
+ name: "nestingNoDuplicates",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ subset := NewDepSetBuilder(order).Direct(c, a, e).Build()
+ return NewDepSetBuilder(order).Direct(b, d, e).Transitive(subset).Build()
+ },
+ postorder: []string{"c", "a", "e", "b", "d"},
+ preorder: []string{"b", "d", "e", "c", "a"},
+ topological: []string{"b", "d", "c", "a", "e"},
+ },
+ {
+ name: "chain",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ c := NewDepSetBuilder(order).Direct(c).Build()
+ b := NewDepSetBuilder(order).Direct(b).Transitive(c).Build()
+ a := NewDepSetBuilder(order).Direct(a).Transitive(b).Build()
+
+ return a
+ },
+ postorder: []string{"c", "b", "a"},
+ preorder: []string{"a", "b", "c"},
+ topological: []string{"a", "b", "c"},
+ },
+ {
+ name: "diamond",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ d := NewDepSetBuilder(order).Direct(d).Build()
+ c := NewDepSetBuilder(order).Direct(c).Transitive(d).Build()
+ b := NewDepSetBuilder(order).Direct(b).Transitive(d).Build()
+ a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+
+ return a
+ },
+ postorder: []string{"d", "b", "c", "a"},
+ preorder: []string{"a", "b", "d", "c"},
+ topological: []string{"a", "b", "c", "d"},
+ },
+ {
+ name: "extendedDiamond",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ d := NewDepSetBuilder(order).Direct(d).Build()
+ e := NewDepSetBuilder(order).Direct(e).Build()
+ b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build()
+ c := NewDepSetBuilder(order).Direct(c).Transitive(e).Transitive(d).Build()
+ a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+ return a
+ },
+ postorder: []string{"d", "e", "b", "c", "a"},
+ preorder: []string{"a", "b", "d", "e", "c"},
+ topological: []string{"a", "b", "c", "e", "d"},
+ },
+ {
+ name: "extendedDiamondRightArm",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ d := NewDepSetBuilder(order).Direct(d).Build()
+ e := NewDepSetBuilder(order).Direct(e).Build()
+ b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build()
+ c2 := NewDepSetBuilder(order).Direct(c2).Transitive(e).Transitive(d).Build()
+ c := NewDepSetBuilder(order).Direct(c).Transitive(c2).Build()
+ a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+ return a
+ },
+ postorder: []string{"d", "e", "b", "c2", "c", "a"},
+ preorder: []string{"a", "b", "d", "e", "c", "c2"},
+ topological: []string{"a", "b", "c", "c2", "e", "d"},
+ },
+ {
+ name: "orderConflict",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ child1 := NewDepSetBuilder(order).Direct(a, b).Build()
+ child2 := NewDepSetBuilder(order).Direct(b, a).Build()
+ parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build()
+ return parent
+ },
+ postorder: []string{"a", "b"},
+ preorder: []string{"a", "b"},
+ topological: []string{"b", "a"},
+ },
+ {
+ name: "orderConflictNested",
+ depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+ a := NewDepSetBuilder(order).Direct(a).Build()
+ b := NewDepSetBuilder(order).Direct(b).Build()
+ child1 := NewDepSetBuilder(order).Transitive(a).Transitive(b).Build()
+ child2 := NewDepSetBuilder(order).Transitive(b).Transitive(a).Build()
+ parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build()
+ return parent
+ },
+ postorder: []string{"a", "b"},
+ preorder: []string{"a", "b"},
+ topological: []string{"b", "a"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Run("postorder", func(t *testing.T) {
+ depSet := tt.depSet(t, POSTORDER)
+ if g, w := depSet.ToList().Strings(), tt.postorder; !reflect.DeepEqual(g, w) {
+ t.Errorf("expected ToList() = %q, got %q", w, g)
+ }
+ })
+ t.Run("preorder", func(t *testing.T) {
+ depSet := tt.depSet(t, PREORDER)
+ if g, w := depSet.ToList().Strings(), tt.preorder; !reflect.DeepEqual(g, w) {
+ t.Errorf("expected ToList() = %q, got %q", w, g)
+ }
+ })
+ t.Run("topological", func(t *testing.T) {
+ depSet := tt.depSet(t, TOPOLOGICAL)
+ if g, w := depSet.ToList().Strings(), tt.topological; !reflect.DeepEqual(g, w) {
+ t.Errorf("expected ToList() = %q, got %q", w, g)
+ }
+ })
+ })
+ }
+}
+
+func TestDepSetInvalidOrder(t *testing.T) {
+ orders := []DepSetOrder{POSTORDER, PREORDER, TOPOLOGICAL}
+
+ run := func(t *testing.T, order1, order2 DepSetOrder) {
+ defer func() {
+ if r := recover(); r != nil {
+ if err, ok := r.(error); !ok {
+ t.Fatalf("expected panic error, got %v", err)
+ } else if !strings.Contains(err.Error(), "incompatible order") {
+ t.Fatalf("expected incompatible order error, got %v", err)
+ }
+ }
+ }()
+ NewDepSet(order1, nil, []*DepSet{NewDepSet(order2, nil, nil)})
+ t.Fatal("expected panic")
+ }
+
+ for _, order1 := range orders {
+ t.Run(order1.String(), func(t *testing.T) {
+ for _, order2 := range orders {
+ t.Run(order2.String(), func(t *testing.T) {
+ if order1 != order2 {
+ run(t, order1, order2)
+ }
+ })
+ }
+ })
+ }
+}
diff --git a/android/module.go b/android/module.go
index 06079ca..2062a4d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -50,6 +50,8 @@
Implicit Path
Implicits Paths
OrderOnly Paths
+ Validation Path
+ Validations Paths
Default bool
Args map[string]string
}
@@ -228,6 +230,16 @@
// For more information, see Module.GenerateBuildActions within Blueprint's module_ctx.go
GenerateAndroidBuildActions(ModuleContext)
+ // Add dependencies to the components of a module, i.e. modules that are created
+ // by the module and which are considered to be part of the creating module.
+ //
+ // This is called before prebuilts are renamed so as to allow a dependency to be
+ // added directly to a prebuilt child module instead of depending on a source module
+ // and relying on prebuilt processing to switch to the prebuilt module if preferred.
+ //
+ // A dependency on a prebuilt must include the "prebuilt_" prefix.
+ ComponentDepsMutator(ctx BottomUpMutatorContext)
+
DepsMutator(BottomUpMutatorContext)
base() *ModuleBase
@@ -769,6 +781,8 @@
prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
}
+func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
+
func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
func (m *ModuleBase) AddProperties(props ...interface{}) {
@@ -1549,6 +1563,7 @@
Inputs: params.Inputs.Strings(),
Implicits: params.Implicits.Strings(),
OrderOnly: params.OrderOnly.Strings(),
+ Validations: params.Validations.Strings(),
Args: params.Args,
Optional: !params.Default,
}
@@ -1568,13 +1583,17 @@
if params.Implicit != nil {
bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
}
+ if params.Validation != nil {
+ bparams.Validations = append(bparams.Validations, params.Validation.String())
+ }
bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs)
bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs)
bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs)
bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits)
bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly)
- bparams.Depfile = proptools.NinjaEscapeList([]string{bparams.Depfile})[0]
+ bparams.Validations = proptools.NinjaEscapeList(bparams.Validations)
+ bparams.Depfile = proptools.NinjaEscape(bparams.Depfile)
return bparams
}
@@ -2099,15 +2118,6 @@
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
-func findStringInSlice(str string, slice []string) int {
- for i, s := range slice {
- if s == str {
- return i
- }
- }
- return -1
-}
-
// SrcIsModule decodes module references in the format ":name" into the module name, or empty string if the input
// was not a module reference.
func SrcIsModule(s string) (module string) {
diff --git a/android/mutator.go b/android/mutator.go
index 77d5f43..b70c4ff 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -115,6 +115,18 @@
// a DefaultableHook.
RegisterDefaultsPreArchMutators,
+ // Add dependencies on any components so that any component references can be
+ // resolved within the deps mutator.
+ //
+ // Must be run after defaults so it can be used to create dependencies on the
+ // component modules that are creating in a DefaultableHook.
+ //
+ // Must be run before RegisterPrebuiltsPreArchMutators, i.e. before prebuilts are
+ // renamed. That is so that if a module creates components using a prebuilt module
+ // type that any dependencies (which must use prebuilt_ prefixes) are resolved to
+ // the prebuilt module and not the source module.
+ RegisterComponentsMutator,
+
// Create an association between prebuilt modules and their corresponding source
// modules (if any).
//
@@ -202,6 +214,7 @@
AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string)
AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
ReplaceDependencies(string)
+ ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
AliasVariation(variationName string)
}
@@ -252,8 +265,21 @@
return mutator
}
+func RegisterComponentsMutator(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("component-deps", componentDepsMutator).Parallel()
+}
+
+// A special mutator that runs just prior to the deps mutator to allow the dependencies
+// on component modules to be added so that they can depend directly on a prebuilt
+// module.
+func componentDepsMutator(ctx BottomUpMutatorContext) {
+ if m := ctx.Module(); m.Enabled() {
+ m.ComponentDepsMutator(ctx)
+ }
+}
+
func depsMutator(ctx BottomUpMutatorContext) {
- if m, ok := ctx.Module().(Module); ok && m.Enabled() {
+ if m := ctx.Module(); m.Enabled() {
m.DepsMutator(ctx)
}
}
@@ -403,6 +429,10 @@
b.bp.ReplaceDependencies(name)
}
+func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate blueprint.ReplaceDependencyPredicate) {
+ b.bp.ReplaceDependenciesIf(name, predicate)
+}
+
func (b *bottomUpMutatorContext) AliasVariation(variationName string) {
b.bp.AliasVariation(variationName)
}
diff --git a/android/paths.go b/android/paths.go
index 066baf2..20ff55e 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -380,6 +380,18 @@
}
func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) {
+ excludePaths := func(paths Paths) Paths {
+ if len(expandedExcludes) == 0 {
+ return paths
+ }
+ remainder := make(Paths, 0, len(paths))
+ for _, p := range paths {
+ if !InList(p.String(), expandedExcludes) {
+ remainder = append(remainder, p)
+ }
+ }
+ return remainder
+ }
if m, t := SrcIsModuleWithTag(s); m != "" {
module := ctx.GetDirectDepWithTag(m, sourceOrOutputDepTag(t))
if module == nil {
@@ -390,20 +402,11 @@
if err != nil {
return nil, fmt.Errorf("path dependency %q: %s", s, err)
}
- return outputFiles, nil
+ return excludePaths(outputFiles), nil
} else if t != "" {
return nil, fmt.Errorf("path dependency %q is not an output file producing module", s)
} else if srcProducer, ok := module.(SourceFileProducer); ok {
- moduleSrcs := srcProducer.Srcs()
- for _, e := range expandedExcludes {
- for j := 0; j < len(moduleSrcs); j++ {
- if moduleSrcs[j].String() == e {
- moduleSrcs = append(moduleSrcs[:j], moduleSrcs[j+1:]...)
- j--
- }
- }
- }
- return moduleSrcs, nil
+ return excludePaths(srcProducer.Srcs()), nil
} else {
return nil, fmt.Errorf("path dependency %q is not a source file producing module", s)
}
@@ -418,8 +421,7 @@
reportPathErrorf(ctx, "module source path %q does not exist", p)
}
- j := findStringInSlice(p.String(), expandedExcludes)
- if j >= 0 {
+ if InList(p.String(), expandedExcludes) {
return nil, nil
}
return Paths{p}, nil
@@ -484,6 +486,10 @@
return ret
}
+func CopyOfPaths(paths Paths) Paths {
+ return append(Paths(nil), paths...)
+}
+
// FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It
// modifies the Paths slice contents in place, and returns a subslice of the original slice.
func FirstUniquePaths(list Paths) Paths {
@@ -494,7 +500,8 @@
return firstUniquePathsList(list)
}
-// SortedUniquePaths returns what its name says
+// SortedUniquePaths returns all unique elements of a Paths in sorted order. It modifies the
+// Paths slice contents in place, and returns a subslice of the original slice.
func SortedUniquePaths(list Paths) Paths {
unique := FirstUniquePaths(list)
sort.Slice(unique, func(i, j int) bool {
diff --git a/android/paths_test.go b/android/paths_test.go
index 9b45d3f..a9cd22b 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1091,6 +1091,21 @@
rels: []string{"gen/c"},
},
{
+ name: "output file provider with exclude",
+ bp: `
+ test {
+ name: "foo",
+ srcs: [":b", ":c"],
+ exclude_srcs: [":c"]
+ }
+ output_file_provider {
+ name: "c",
+ outs: ["gen/c"],
+ }`,
+ srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"},
+ rels: []string{"gen/b"},
+ },
+ {
name: "special characters glob",
bp: `
test {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index a29ec91..269ad5d 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -30,6 +30,16 @@
ctx.PostDepsMutators(RegisterPrebuiltsPostDepsMutators)
}
+// Marks a dependency tag as possibly preventing a reference to a source from being
+// replaced with the prebuilt.
+type ReplaceSourceWithPrebuilt interface {
+ blueprint.DependencyTag
+
+ // Return true if the dependency defined by this tag should be replaced with the
+ // prebuilt.
+ ReplaceSourceWithPrebuilt() bool
+}
+
type prebuiltDependencyTag struct {
blueprint.BaseDependencyTag
}
@@ -215,7 +225,7 @@
// PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
// corresponding source module, if one exists for the same variant.
func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
- if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+ if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Enabled() && m.Prebuilt() != nil {
p := m.Prebuilt()
if !p.properties.PrebuiltRenamedToSource {
name := m.base().BaseModuleName()
@@ -260,7 +270,13 @@
name := m.base().BaseModuleName()
if p.properties.UsePrebuilt {
if p.properties.SourceExists {
- ctx.ReplaceDependencies(name)
+ ctx.ReplaceDependenciesIf(name, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
+ if t, ok := tag.(ReplaceSourceWithPrebuilt); ok {
+ return t.ReplaceSourceWithPrebuilt()
+ }
+
+ return true
+ })
}
} else {
m.SkipInstall()
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8029b85..6c3cd9e 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -22,9 +22,10 @@
)
var prebuiltsTests = []struct {
- name string
- modules string
- prebuilt []OsClass
+ name string
+ replaceBp bool // modules is added to default bp boilerplate if false.
+ modules string
+ prebuilt []OsType
}{
{
name: "no prebuilt",
@@ -42,7 +43,7 @@
prefer: false,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "no source prebuilt preferred",
@@ -52,7 +53,7 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "prebuilt not preferred",
@@ -80,7 +81,7 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "prebuilt no file not preferred",
@@ -120,7 +121,7 @@
prefer: true,
srcs: [":fg"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "prebuilt module for device only",
@@ -135,7 +136,7 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device},
+ prebuilt: []OsType{Android},
},
{
name: "prebuilt file for host only",
@@ -153,7 +154,7 @@
},
},
}`,
- prebuilt: []OsClass{Host},
+ prebuilt: []OsType{BuildOs},
},
{
name: "prebuilt override not preferred",
@@ -191,7 +192,72 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
+ },
+ {
+ name: "prebuilt including default-disabled OS",
+ replaceBp: true,
+ modules: `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ source {
+ name: "bar",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt_file"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }`,
+ prebuilt: []OsType{Android, BuildOs, Windows},
+ },
+ {
+ name: "fall back to source for default-disabled OS",
+ replaceBp: true,
+ modules: `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ source {
+ name: "bar",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt_file"],
+ }`,
+ prebuilt: []OsType{Android, BuildOs},
},
}
@@ -203,14 +269,25 @@
for _, test := range prebuiltsTests {
t.Run(test.name, func(t *testing.T) {
- bp := `
- source {
- name: "foo",
- deps: [":bar"],
- }
- ` + test.modules
+ bp := test.modules
+ if !test.replaceBp {
+ bp = bp + `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ }`
+ }
config := TestArchConfig(buildDir, nil, bp, fs)
+ // Add windows to the target list to test the logic when a variant is
+ // disabled by default.
+ if !Windows.DefaultDisabled {
+ t.Errorf("windows is assumed to be disabled by default")
+ }
+ config.config.Targets[Windows] = []Target{
+ {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+ }
+
ctx := NewTestArchContext()
registerTestPrebuiltBuildComponents(ctx)
ctx.RegisterModuleType("filegroup", FileGroupFactory)
@@ -223,7 +300,7 @@
for _, variant := range ctx.ModuleVariantsForTests("foo") {
foo := ctx.ModuleForTests("foo", variant)
- t.Run(foo.Module().Target().Os.Class.String(), func(t *testing.T) {
+ t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
var dependsOnSourceModule, dependsOnPrebuiltModule bool
ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
if _, ok := m.(*sourceModule); ok {
@@ -237,26 +314,38 @@
}
})
+ moduleIsDisabled := !foo.Module().Enabled()
deps := foo.Module().(*sourceModule).deps
- if deps == nil || len(deps) != 1 {
- t.Errorf("deps does not have single path, but is %v", deps)
+ if moduleIsDisabled {
+ if len(deps) > 0 {
+ t.Errorf("disabled module got deps: %v", deps)
+ }
+ } else {
+ if len(deps) != 1 {
+ t.Errorf("deps does not have single path, but is %v", deps)
+ }
}
+
var usingSourceFile, usingPrebuiltFile bool
- if deps[0].String() == "source_file" {
+ if len(deps) > 0 && deps[0].String() == "source_file" {
usingSourceFile = true
}
- if deps[0].String() == "prebuilt_file" {
+ if len(deps) > 0 && deps[0].String() == "prebuilt_file" {
usingPrebuiltFile = true
}
prebuilt := false
for _, os := range test.prebuilt {
- if os == foo.Module().Target().Os.Class {
+ if os == foo.Module().Target().Os {
prebuilt = true
}
}
if prebuilt {
+ if moduleIsDisabled {
+ t.Errorf("dependent module for prebuilt is disabled")
+ }
+
if !dependsOnPrebuiltModule {
t.Errorf("doesn't depend on prebuilt module")
}
@@ -270,7 +359,7 @@
if usingSourceFile {
t.Errorf("using source_file")
}
- } else {
+ } else if !moduleIsDisabled {
if dependsOnPrebuiltModule {
t.Errorf("depends on prebuilt module")
}
diff --git a/android/sdk.go b/android/sdk.go
index e823106..2c38f56 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -266,6 +266,9 @@
SdkMemberType() SdkMemberType
}
+var _ SdkMemberTypeDependencyTag = (*sdkMemberDependencyTag)(nil)
+var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)
+
type sdkMemberDependencyTag struct {
blueprint.BaseDependencyTag
memberType SdkMemberType
@@ -275,6 +278,12 @@
return t.memberType
}
+// Prevent dependencies from the sdk/module_exports onto their members from being
+// replaced with a preferred prebuilt.
+func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
+ return false
+}
+
func DependencyTagForSdkMemberType(memberType SdkMemberType) SdkMemberTypeDependencyTag {
return &sdkMemberDependencyTag{memberType: memberType}
}
@@ -457,8 +466,7 @@
// Base structure for all implementations of SdkMemberProperties.
//
-// Contains common properties that apply across many different member types. These
-// are not affected by the optimization to extract common values.
+// Contains common properties that apply across many different member types.
type SdkMemberPropertiesBase struct {
// The number of unique os types supported by the member variants.
//
@@ -480,9 +488,7 @@
Os OsType `sdk:"keep"`
// The setting to use for the compile_multilib property.
- //
- // This property is set after optimization so there is no point in trying to optimize it.
- Compile_multilib string `sdk:"keep"`
+ Compile_multilib string `android:"arch_variant"`
}
// The os prefix to use for any file paths in the sdk.
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 7595238..b9bcc3a 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -105,9 +105,10 @@
fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
// /apex/<apex_name>/{lib|framework|...}
pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+ var modulePath string
if apexType == flattenedApex {
// /system/apex/<name>/{lib|framework|...}
- modulePath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+ modulePath = filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
if a.primaryApexType && !symbolFilesNotNeeded {
fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
@@ -131,6 +132,7 @@
fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
}
} else {
+ modulePath = pathWhenActivated
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
@@ -193,8 +195,13 @@
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.apk.apk
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
- if app, ok := fi.module.(*java.AndroidApp); ok && len(app.JniCoverageOutputs()) > 0 {
- fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(app.JniCoverageOutputs().Strings(), " "))
+ if app, ok := fi.module.(*java.AndroidApp); ok {
+ if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
+ }
+ if jniLibSymbols := app.JNISymbolsInstalls(modulePath); len(jniLibSymbols) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_SYMBOLS :=", jniLibSymbols.String())
+ }
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
case appSet:
@@ -203,6 +210,7 @@
panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
}
fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+ fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
case nativeSharedLib, nativeExecutable, nativeTest:
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
diff --git a/apex/apex.go b/apex/apex.go
index d0c1a09..045ae82 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1750,9 +1750,15 @@
return false
}
+ dt := ctx.OtherModuleDependencyTag(child)
+
+ if _, ok := dt.(android.ExcludeFromApexContentsTag); ok {
+ return false
+ }
+
// Check for the direct dependencies that contribute to the payload
- if dt, ok := ctx.OtherModuleDependencyTag(child).(dependencyTag); ok {
- if dt.payload {
+ if adt, ok := dt.(dependencyTag); ok {
+ if adt.payload {
return do(ctx, parent, am, false /* externalDep */)
}
// As soon as the dependency graph crosses the APEX boundary, don't go further.
@@ -2106,7 +2112,7 @@
case android.PrebuiltDepTag:
// If the prebuilt is force disabled, remember to delete the prebuilt file
// that might have been installed in the previous builds
- if prebuilt, ok := child.(*Prebuilt); ok && prebuilt.isForceDisabled() {
+ if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
a.prebuiltFileToDelete = prebuilt.InstallFilename()
}
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index befb814..f064338 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -233,6 +233,7 @@
ctx.RegisterModuleType("apex_set", apexSetFactory)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(android.RegisterComponentsMutator)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
cc.RegisterRequiredBuildComponentsForTest(ctx)
@@ -5790,6 +5791,41 @@
}
}
+func TestNonPreferredPrebuiltDependency(t *testing.T) {
+ _, _ = testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ stubs: {
+ versions: ["10000"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ cc_prebuilt_library_shared {
+ name: "mylib",
+ prefer: false,
+ srcs: ["prebuilt.so"],
+ stubs: {
+ versions: ["10000"],
+ },
+ apex_available: ["myapex"],
+ }
+ `)
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/apex/builder.go b/apex/builder.go
index 5fb9a5f..49e4642 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -390,7 +390,7 @@
} else {
if fi.class == appSet {
copyCommands = append(copyCommands,
- fmt.Sprintf("unzip -q -d %s %s", destPathDir, fi.builtFile.String()))
+ fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, fi.builtFile.String()))
} else {
copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index bf574dc..d459f87 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -21,6 +21,7 @@
"android/soong/android"
"android/soong/java"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -39,9 +40,55 @@
"abis", "allow-prereleased", "sdk-version")
)
+type prebuilt interface {
+ isForceDisabled() bool
+ InstallFilename() string
+}
+
+type prebuiltCommon struct {
+ prebuilt android.Prebuilt
+ properties prebuiltCommonProperties
+}
+
+type prebuiltCommonProperties struct {
+ ForceDisable bool `blueprint:"mutated"`
+}
+
+func (p *prebuiltCommon) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *prebuiltCommon) isForceDisabled() bool {
+ return p.properties.ForceDisable
+}
+
+func (p *prebuiltCommon) checkForceDisable(ctx android.ModuleContext) bool {
+ // If the device is configured to use flattened APEX, force disable the prebuilt because
+ // the prebuilt is a non-flattened one.
+ forceDisable := ctx.Config().FlattenApex()
+
+ // Force disable the prebuilts when we are doing unbundled build. We do unbundled build
+ // to build the prebuilts themselves.
+ forceDisable = forceDisable || ctx.Config().UnbundledBuild()
+
+ // Force disable the prebuilts when coverage is enabled.
+ forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
+ forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
+
+ // b/137216042 don't use prebuilts when address sanitizer is on
+ forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
+ android.InList("hwaddress", ctx.Config().SanitizeDevice())
+
+ if forceDisable && p.prebuilt.SourceExists() {
+ p.properties.ForceDisable = true
+ return true
+ }
+ return false
+}
+
type Prebuilt struct {
android.ModuleBase
- prebuilt android.Prebuilt
+ prebuiltCommon
properties PrebuiltProperties
@@ -57,8 +104,7 @@
type PrebuiltProperties struct {
// the path to the prebuilt .apex file to import.
- Source string `blueprint:"mutated"`
- ForceDisable bool `blueprint:"mutated"`
+ Source string `blueprint:"mutated"`
Src *string
Arch struct {
@@ -93,10 +139,6 @@
return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
}
-func (p *Prebuilt) isForceDisabled() bool {
- return p.properties.ForceDisable
-}
-
func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -110,12 +152,8 @@
return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
}
-func (p *Prebuilt) Prebuilt() *android.Prebuilt {
- return &p.prebuilt
-}
-
func (p *Prebuilt) Name() string {
- return p.prebuilt.Name(p.ModuleBase.Name())
+ return p.prebuiltCommon.prebuilt.Name(p.ModuleBase.Name())
}
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
@@ -128,27 +166,6 @@
}
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
- // If the device is configured to use flattened APEX, force disable the prebuilt because
- // the prebuilt is a non-flattened one.
- forceDisable := ctx.Config().FlattenApex()
-
- // Force disable the prebuilts when we are doing unbundled build. We do unbundled build
- // to build the prebuilts themselves.
- forceDisable = forceDisable || ctx.Config().UnbundledBuild()
-
- // Force disable the prebuilts when coverage is enabled.
- forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
- forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
-
- // b/137216042 don't use prebuilts when address sanitizer is on
- forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
- android.InList("hwaddress", ctx.Config().SanitizeDevice())
-
- if forceDisable && p.prebuilt.SourceExists() {
- p.properties.ForceDisable = true
- return
- }
-
// This is called before prebuilt_select and prebuilt_postdeps mutators
// The mutators requires that src to be set correctly for each arch so that
// arch variants are disabled when src is not provided for the arch.
@@ -177,10 +194,6 @@
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- if p.properties.ForceDisable {
- return
- }
-
// TODO(jungjw): Check the key validity.
p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
p.installDir = android.PathForModuleInstall(ctx, "apex")
@@ -194,6 +207,12 @@
Input: p.inputApex,
Output: p.outputApex,
})
+
+ if p.prebuiltCommon.checkForceDisable(ctx) {
+ p.SkipInstall()
+ return
+ }
+
if p.installable() {
ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
}
@@ -227,7 +246,7 @@
type ApexSet struct {
android.ModuleBase
- prebuilt android.Prebuilt
+ prebuiltCommon
properties ApexSetProperties
@@ -270,12 +289,8 @@
return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
}
-func (a *ApexSet) Prebuilt() *android.Prebuilt {
- return &a.prebuilt
-}
-
func (a *ApexSet) Name() string {
- return a.prebuilt.Name(a.ModuleBase.Name())
+ return a.prebuiltCommon.prebuilt.Name(a.ModuleBase.Name())
}
func (a *ApexSet) Overrides() []string {
@@ -297,7 +312,7 @@
ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
}
- apexSet := a.prebuilt.SingleSourcePath(ctx)
+ apexSet := a.prebuiltCommon.prebuilt.SingleSourcePath(ctx)
a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
ctx.Build(pctx,
android.BuildParams{
@@ -311,6 +326,12 @@
"sdk-version": ctx.Config().PlatformSdkVersion(),
},
})
+
+ if a.prebuiltCommon.checkForceDisable(ctx) {
+ a.SkipInstall()
+ return
+ }
+
a.installDir = android.PathForModuleInstall(ctx, "apex")
if a.installable() {
ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 372a72e..51d8b4e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -140,10 +140,6 @@
}
func (p *nativeBinaryInfoProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
- if p.Compile_multilib != "" {
- propertySet.AddProperty("compile_multilib", p.Compile_multilib)
- }
-
builder := ctx.SnapshotBuilder()
if p.outputFile != nil {
propertySet.AddProperty("srcs", []string{nativeBinaryPathFor(*p)})
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 8eb79e3..81c907d 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -66,6 +66,7 @@
"10.13",
"10.14",
"10.15",
+ "11.0",
}
darwinAvailableLibraries = append(
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 9c54399..4b9eb30 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -234,7 +234,7 @@
for _, propertyInfo := range includeDirProperties {
// Calculate the base directory in the snapshot into which the files will be copied.
// lib.ArchType is "" for common properties.
- targetDir := filepath.Join(libInfo.archType, propertyInfo.snapshotDir)
+ targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir)
propertyName := propertyInfo.propertyName
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 7ff20f4..71c9204 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -225,6 +225,8 @@
func llndkHeadersFactory() android.Module {
module, library := NewLibrary(android.DeviceSupported)
library.HeaderOnly()
+ module.stl = nil
+ module.sanitize = nil
decorator := &llndkHeadersDecorator{
libraryDecorator: library,
diff --git a/cc/ndk_api_coverage_parser/.gitignore b/cc/ndk_api_coverage_parser/.gitignore
new file mode 100644
index 0000000..fd94eac
--- /dev/null
+++ b/cc/ndk_api_coverage_parser/.gitignore
@@ -0,0 +1,140 @@
+# From https://github.com/github/gitignore/blob/master/Python.gitignore
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
diff --git a/cc/scriptlib/Android.bp b/cc/ndk_api_coverage_parser/Android.bp
similarity index 73%
rename from cc/scriptlib/Android.bp
rename to cc/ndk_api_coverage_parser/Android.bp
index ff9a2f0..8d9827c 100644
--- a/cc/scriptlib/Android.bp
+++ b/cc/ndk_api_coverage_parser/Android.bp
@@ -14,19 +14,33 @@
// limitations under the License.
//
+python_library_host {
+ name: "ndk_api_coverage_parser_lib",
+ pkg_path: "ndk_api_coverage_parser",
+ srcs: [
+ "__init__.py",
+ ],
+}
+
python_test_host {
name: "test_ndk_api_coverage_parser",
main: "test_ndk_api_coverage_parser.py",
srcs: [
"test_ndk_api_coverage_parser.py",
],
+ libs: [
+ "ndk_api_coverage_parser_lib",
+ "symbolfile",
+ ],
}
python_binary_host {
name: "ndk_api_coverage_parser",
- main: "ndk_api_coverage_parser.py",
+ main: "__init__.py",
srcs: [
- "gen_stub_libs.py",
- "ndk_api_coverage_parser.py",
+ "__init__.py",
+ ],
+ libs: [
+ "symbolfile",
],
}
diff --git a/cc/ndk_api_coverage_parser/OWNERS b/cc/ndk_api_coverage_parser/OWNERS
new file mode 100644
index 0000000..a90c48c
--- /dev/null
+++ b/cc/ndk_api_coverage_parser/OWNERS
@@ -0,0 +1 @@
+sophiez@google.com
diff --git a/cc/scriptlib/ndk_api_coverage_parser.py b/cc/ndk_api_coverage_parser/__init__.py
similarity index 96%
rename from cc/scriptlib/ndk_api_coverage_parser.py
rename to cc/ndk_api_coverage_parser/__init__.py
index d74035b..7817c78 100755
--- a/cc/scriptlib/ndk_api_coverage_parser.py
+++ b/cc/ndk_api_coverage_parser/__init__.py
@@ -21,7 +21,7 @@
import sys
from xml.etree.ElementTree import Element, SubElement, tostring
-from gen_stub_libs import ALL_ARCHITECTURES, FUTURE_API_LEVEL, MultiplyDefinedSymbolError, SymbolFileParser
+from symbolfile import ALL_ARCHITECTURES, FUTURE_API_LEVEL, MultiplyDefinedSymbolError, SymbolFileParser
ROOT_ELEMENT_TAG = 'ndk-library'
diff --git a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
new file mode 100644
index 0000000..3ec14c1
--- /dev/null
+++ b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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.
+#
+"""Tests for ndk_api_coverage_parser.py."""
+import io
+import textwrap
+import unittest
+
+from xml.etree.ElementTree import fromstring
+from symbolfile import FUTURE_API_LEVEL, SymbolFileParser
+import ndk_api_coverage_parser as nparser
+
+
+# pylint: disable=missing-docstring
+
+
+# https://stackoverflow.com/a/24349916/632035
+def etree_equal(elem1, elem2):
+ """Returns true if the two XML elements are equal.
+
+ xml.etree.ElementTree's comparison operator cares about the ordering of
+ elements and attributes, but they are stored in an unordered dict so the
+ ordering is not deterministic.
+
+ lxml is apparently API compatible with xml and does use an OrderedDict, but
+ we don't have it in the tree.
+ """
+ if elem1.tag != elem2.tag:
+ return False
+ if elem1.text != elem2.text:
+ return False
+ if elem1.tail != elem2.tail:
+ return False
+ if elem1.attrib != elem2.attrib:
+ return False
+ if len(elem1) != len(elem2):
+ return False
+ return all(etree_equal(c1, c2) for c1, c2 in zip(elem1, elem2))
+
+
+class ApiCoverageSymbolFileParserTest(unittest.TestCase):
+ def test_parse(self):
+ input_file = io.StringIO(textwrap.dedent(u"""\
+ LIBLOG { # introduced-arm64=24 introduced-x86=24 introduced-x86_64=24
+ global:
+ android_name_to_log_id; # apex llndk introduced=23
+ android_log_id_to_name; # llndk arm
+ __android_log_assert; # introduced-x86=23
+ __android_log_buf_print; # var
+ __android_log_buf_write;
+ local:
+ *;
+ };
+
+ LIBLOG_PLATFORM {
+ android_fdtrack; # llndk
+ android_net; # introduced=23
+ };
+
+ LIBLOG_FOO { # var
+ android_var;
+ };
+ """))
+ parser = SymbolFileParser(input_file, {}, "", FUTURE_API_LEVEL, True, True)
+ generator = nparser.XmlGenerator(io.StringIO())
+ result = generator.convertToXml(parser.parse())
+ expected = fromstring('<ndk-library><symbol apex="True" arch="" introduced="23" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_name_to_log_id" /><symbol arch="arm" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_log_id_to_name" /><symbol arch="" introduced-arm64="24" introduced-x86="23" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_assert" /><symbol arch="" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_buf_write" /><symbol arch="" is_deprecated="False" is_platform="True" llndk="True" name="android_fdtrack" /><symbol arch="" introduced="23" is_deprecated="False" is_platform="True" name="android_net" /></ndk-library>')
+ self.assertTrue(etree_equal(expected, result))
+
+
+def main():
+ suite = unittest.TestLoader().loadTestsFromName(__name__)
+ unittest.TextTestRunner(verbosity=3).run(suite)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 6299b00..4578fd3 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -26,17 +26,16 @@
)
func init() {
+ pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser")
}
var (
- toolPath = pctx.SourcePathVariable("toolPath", "build/soong/cc/scriptlib/gen_stub_libs.py")
-
genStubSrc = pctx.AndroidStaticRule("genStubSrc",
blueprint.RuleParams{
- Command: "$toolPath --arch $arch --api $apiLevel --api-map " +
- "$apiMap $flags $in $out",
- CommandDeps: []string{"$toolPath"},
+ Command: "$ndkStubGenerator --arch $arch --api $apiLevel " +
+ "--api-map $apiMap $flags $in $out",
+ CommandDeps: []string{"$ndkStubGenerator"},
}, "arch", "apiLevel", "apiMap", "flags")
parseNdkApiRule = pctx.AndroidStaticRule("parseNdkApiRule",
@@ -78,9 +77,9 @@
// https://github.com/android-ndk/ndk/issues/265.
Unversioned_until *string
- // Private property for use by the mutator that splits per-API level.
- // can be one of <number:sdk_version> or <codename> or "current"
- // passed to "gen_stub_libs.py" as it is
+ // Private property for use by the mutator that splits per-API level. Can be
+ // one of <number:sdk_version> or <codename> or "current" passed to
+ // "ndkstubgen.py" as it is
ApiLevel string `blueprint:"mutated"`
// True if this API is not yet ready to be shipped in the NDK. It will be
diff --git a/cc/ndkstubgen/.gitignore b/cc/ndkstubgen/.gitignore
new file mode 100644
index 0000000..fd94eac
--- /dev/null
+++ b/cc/ndkstubgen/.gitignore
@@ -0,0 +1,140 @@
+# From https://github.com/github/gitignore/blob/master/Python.gitignore
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
diff --git a/cc/scriptlib/Android.bp b/cc/ndkstubgen/Android.bp
similarity index 63%
copy from cc/scriptlib/Android.bp
copy to cc/ndkstubgen/Android.bp
index ff9a2f0..85dfaee 100644
--- a/cc/scriptlib/Android.bp
+++ b/cc/ndkstubgen/Android.bp
@@ -14,19 +14,35 @@
// limitations under the License.
//
-python_test_host {
- name: "test_ndk_api_coverage_parser",
- main: "test_ndk_api_coverage_parser.py",
+python_binary_host {
+ name: "ndkstubgen",
+ pkg_path: "ndkstubgen",
+ main: "__init__.py",
srcs: [
- "test_ndk_api_coverage_parser.py",
+ "__init__.py",
+ ],
+ libs: [
+ "symbolfile",
],
}
-python_binary_host {
- name: "ndk_api_coverage_parser",
- main: "ndk_api_coverage_parser.py",
+python_library_host {
+ name: "ndkstubgenlib",
+ pkg_path: "ndkstubgen",
srcs: [
- "gen_stub_libs.py",
- "ndk_api_coverage_parser.py",
+ "__init__.py",
+ ],
+ libs: [
+ "symbolfile",
+ ],
+}
+
+python_test_host {
+ name: "test_ndkstubgen",
+ srcs: [
+ "test_ndkstubgen.py",
+ ],
+ libs: [
+ "ndkstubgenlib",
],
}
diff --git a/cc/ndkstubgen/OWNERS b/cc/ndkstubgen/OWNERS
new file mode 100644
index 0000000..f0d8733
--- /dev/null
+++ b/cc/ndkstubgen/OWNERS
@@ -0,0 +1 @@
+danalbert@google.com
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
new file mode 100755
index 0000000..2f4326a
--- /dev/null
+++ b/cc/ndkstubgen/__init__.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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.
+#
+"""Generates source for stub shared libraries for the NDK."""
+import argparse
+import json
+import logging
+import os
+import sys
+
+import symbolfile
+
+
+class Generator:
+ """Output generator that writes stub source files and version scripts."""
+ def __init__(self, src_file, version_script, arch, api, llndk, apex):
+ self.src_file = src_file
+ self.version_script = version_script
+ self.arch = arch
+ self.api = api
+ self.llndk = llndk
+ self.apex = apex
+
+ def write(self, versions):
+ """Writes all symbol data to the output files."""
+ for version in versions:
+ self.write_version(version)
+
+ def write_version(self, version):
+ """Writes a single version block's data to the output files."""
+ if symbolfile.should_omit_version(version, self.arch, self.api,
+ self.llndk, self.apex):
+ return
+
+ section_versioned = symbolfile.symbol_versioned_in_api(
+ version.tags, self.api)
+ version_empty = True
+ pruned_symbols = []
+ for symbol in version.symbols:
+ if symbolfile.should_omit_symbol(symbol, self.arch, self.api,
+ self.llndk, self.apex):
+ continue
+
+ if symbolfile.symbol_versioned_in_api(symbol.tags, self.api):
+ version_empty = False
+ pruned_symbols.append(symbol)
+
+ if len(pruned_symbols) > 0:
+ if not version_empty and section_versioned:
+ self.version_script.write(version.name + ' {\n')
+ self.version_script.write(' global:\n')
+ for symbol in pruned_symbols:
+ emit_version = symbolfile.symbol_versioned_in_api(
+ symbol.tags, self.api)
+ if section_versioned and emit_version:
+ self.version_script.write(' ' + symbol.name + ';\n')
+
+ weak = ''
+ if 'weak' in symbol.tags:
+ weak = '__attribute__((weak)) '
+
+ if 'var' in symbol.tags:
+ self.src_file.write('{}int {} = 0;\n'.format(
+ weak, symbol.name))
+ else:
+ self.src_file.write('{}void {}() {{}}\n'.format(
+ weak, symbol.name))
+
+ if not version_empty and section_versioned:
+ base = '' if version.base is None else ' ' + version.base
+ self.version_script.write('}' + base + ';\n')
+
+
+def parse_args():
+ """Parses and returns command line arguments."""
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument('-v', '--verbose', action='count', default=0)
+
+ parser.add_argument(
+ '--api', required=True, help='API level being targeted.')
+ parser.add_argument(
+ '--arch', choices=symbolfile.ALL_ARCHITECTURES, required=True,
+ help='Architecture being targeted.')
+ parser.add_argument(
+ '--llndk', action='store_true', help='Use the LLNDK variant.')
+ parser.add_argument(
+ '--apex', action='store_true', help='Use the APEX variant.')
+
+ parser.add_argument(
+ '--api-map', type=os.path.realpath, required=True,
+ help='Path to the API level map JSON file.')
+
+ parser.add_argument(
+ 'symbol_file', type=os.path.realpath, help='Path to symbol file.')
+ parser.add_argument(
+ 'stub_src', type=os.path.realpath,
+ help='Path to output stub source file.')
+ parser.add_argument(
+ 'version_script', type=os.path.realpath,
+ help='Path to output version script.')
+
+ return parser.parse_args()
+
+
+def main():
+ """Program entry point."""
+ args = parse_args()
+
+ with open(args.api_map) as map_file:
+ api_map = json.load(map_file)
+ api = symbolfile.decode_api_level(args.api, api_map)
+
+ verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG)
+ verbosity = args.verbose
+ if verbosity > 2:
+ verbosity = 2
+ logging.basicConfig(level=verbose_map[verbosity])
+
+ with open(args.symbol_file) as symbol_file:
+ try:
+ versions = symbolfile.SymbolFileParser(symbol_file, api_map,
+ args.arch, api, args.llndk,
+ args.apex).parse()
+ except symbolfile.MultiplyDefinedSymbolError as ex:
+ sys.exit('{}: error: {}'.format(args.symbol_file, ex))
+
+ with open(args.stub_src, 'w') as src_file:
+ with open(args.version_script, 'w') as version_file:
+ generator = Generator(src_file, version_file, args.arch, api,
+ args.llndk, args.apex)
+ generator.write(versions)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
new file mode 100755
index 0000000..70bcf78
--- /dev/null
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -0,0 +1,378 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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.
+#
+"""Tests for ndkstubgen.py."""
+import io
+import textwrap
+import unittest
+
+import ndkstubgen
+import symbolfile
+
+
+# pylint: disable=missing-docstring
+
+
+class GeneratorTest(unittest.TestCase):
+ def test_omit_version(self):
+ # Thorough testing of the cases involved here is handled by
+ # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
+ False, False)
+
+ version = symbolfile.Version('VERSION_PRIVATE', None, [], [
+ symbolfile.Symbol('foo', []),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ version = symbolfile.Version('VERSION', None, ['x86'], [
+ symbolfile.Symbol('foo', []),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ version = symbolfile.Version('VERSION', None, ['introduced=14'], [
+ symbolfile.Symbol('foo', []),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ def test_omit_symbol(self):
+ # Thorough testing of the cases involved here is handled by
+ # SymbolPresenceTest.
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
+ False, False)
+
+ version = symbolfile.Version('VERSION_1', None, [], [
+ symbolfile.Symbol('foo', ['x86']),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ version = symbolfile.Version('VERSION_1', None, [], [
+ symbolfile.Symbol('foo', ['introduced=14']),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ version = symbolfile.Version('VERSION_1', None, [], [
+ symbolfile.Symbol('foo', ['llndk']),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ version = symbolfile.Version('VERSION_1', None, [], [
+ symbolfile.Symbol('foo', ['apex']),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
+ def test_write(self):
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
+ False, False)
+
+ versions = [
+ symbolfile.Version('VERSION_1', None, [], [
+ symbolfile.Symbol('foo', []),
+ symbolfile.Symbol('bar', ['var']),
+ symbolfile.Symbol('woodly', ['weak']),
+ symbolfile.Symbol('doodly', ['weak', 'var']),
+ ]),
+ symbolfile.Version('VERSION_2', 'VERSION_1', [], [
+ symbolfile.Symbol('baz', []),
+ ]),
+ symbolfile.Version('VERSION_3', 'VERSION_1', [], [
+ symbolfile.Symbol('qux', ['versioned=14']),
+ ]),
+ ]
+
+ generator.write(versions)
+ expected_src = textwrap.dedent("""\
+ void foo() {}
+ int bar = 0;
+ __attribute__((weak)) void woodly() {}
+ __attribute__((weak)) int doodly = 0;
+ void baz() {}
+ void qux() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ bar;
+ woodly;
+ doodly;
+ };
+ VERSION_2 {
+ global:
+ baz;
+ } VERSION_1;
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
+
+class IntegrationTest(unittest.TestCase):
+ def test_integration(self):
+ api_map = {
+ 'O': 9000,
+ 'P': 9001,
+ }
+
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo; # var
+ bar; # x86
+ fizz; # introduced=O
+ buzz; # introduced=P
+ local:
+ *;
+ };
+
+ VERSION_2 { # arm
+ baz; # introduced=9
+ qux; # versioned=14
+ } VERSION_1;
+
+ VERSION_3 { # introduced=14
+ woodly;
+ doodly; # var
+ } VERSION_2;
+
+ VERSION_4 { # versioned=9
+ wibble;
+ wizzes; # llndk
+ waggle; # apex
+ } VERSION_2;
+
+ VERSION_5 { # versioned=14
+ wobble;
+ } VERSION_4;
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
+ False, False)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
+ False, False)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ int foo = 0;
+ void baz() {}
+ void qux() {}
+ void wibble() {}
+ void wobble() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ };
+ VERSION_2 {
+ global:
+ baz;
+ } VERSION_1;
+ VERSION_4 {
+ global:
+ wibble;
+ } VERSION_2;
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
+ def test_integration_future_api(self):
+ api_map = {
+ 'O': 9000,
+ 'P': 9001,
+ 'Q': 9002,
+ }
+
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo; # introduced=O
+ bar; # introduced=P
+ baz; # introduced=Q
+ local:
+ *;
+ };
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9001,
+ False, False)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9001,
+ False, False)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ void foo() {}
+ void bar() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ bar;
+ };
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
+ def test_multiple_definition(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ foo;
+ bar;
+ baz;
+ qux; # arm
+ local:
+ *;
+ };
+
+ VERSION_2 {
+ global:
+ bar;
+ qux; # arm64
+ } VERSION_1;
+
+ VERSION_PRIVATE {
+ global:
+ baz;
+ } VERSION_2;
+
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False,
+ False)
+
+ with self.assertRaises(
+ symbolfile.MultiplyDefinedSymbolError) as ex_context:
+ parser.parse()
+ self.assertEqual(['bar', 'foo'],
+ ex_context.exception.multiply_defined_symbols)
+
+ def test_integration_with_apex(self):
+ api_map = {
+ 'O': 9000,
+ 'P': 9001,
+ }
+
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo; # var
+ bar; # x86
+ fizz; # introduced=O
+ buzz; # introduced=P
+ local:
+ *;
+ };
+
+ VERSION_2 { # arm
+ baz; # introduced=9
+ qux; # versioned=14
+ } VERSION_1;
+
+ VERSION_3 { # introduced=14
+ woodly;
+ doodly; # var
+ } VERSION_2;
+
+ VERSION_4 { # versioned=9
+ wibble;
+ wizzes; # llndk
+ waggle; # apex
+ bubble; # apex llndk
+ duddle; # llndk apex
+ } VERSION_2;
+
+ VERSION_5 { # versioned=14
+ wobble;
+ } VERSION_4;
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
+ False, True)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
+ False, True)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ int foo = 0;
+ void baz() {}
+ void qux() {}
+ void wibble() {}
+ void waggle() {}
+ void bubble() {}
+ void duddle() {}
+ void wobble() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ };
+ VERSION_2 {
+ global:
+ baz;
+ } VERSION_1;
+ VERSION_4 {
+ global:
+ wibble;
+ waggle;
+ bubble;
+ duddle;
+ } VERSION_2;
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
+def main():
+ suite = unittest.TestLoader().loadTestsFromName(__name__)
+ unittest.TextTestRunner(verbosity=3).run(suite)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cc/pylintrc b/cc/pylintrc
index ed49dd7..2032d4e 100644
--- a/cc/pylintrc
+++ b/cc/pylintrc
@@ -1,280 +1,11 @@
-[MASTER]
-
-# Specify a configuration file.
-#rcfile=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Profiled execution.
-profile=no
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-
[MESSAGES CONTROL]
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time. See also the "--disable" option for examples.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
disable=design,fixme
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html. You can also give a reporter class, eg
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]".
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=yes
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Add a comment according to your evaluation note. This is used by the global
-# evaluation report (RP0004).
-comment=no
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details
-#msg-template=
-
-
[BASIC]
-
-# Required attributes for module, separated by a comma
-required-attributes=
-
-# List of builtins function names that should not be used, separated by a comma
-bad-functions=map,filter,apply,input
-
-# Regular expression which should only match correct module names
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Regular expression which should only match correct module level names
-const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Regular expression which should only match correct class names
-class-rgx=[A-Z_][a-zA-Z0-9]+$
-
-# Regular expression which should only match correct function names
-function-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct method names
-method-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct instance attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct argument names
-argument-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct variable names
-variable-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct attribute names in class
-# bodies
-class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Regular expression which should only match correct list comprehension /
-# generator expression variable names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=__.*__
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=-1
-
-
-[TYPECHECK]
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of classes names for which member attributes should not be checked
-# (useful for classes with attributes dynamically set).
-ignored-classes=SQLObject
-
-# When zope mode is activated, add a predefined set of Zope acquired attributes
-# to generated-members.
-zope=no
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E0201 when accessed. Python regular
-# expressions are accepted.
-generated-members=REQUEST,acl_users,aq_parent
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,XXX,TODO
-
-
[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
+ignore-imports=yes
[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the beginning of the name of dummy variables
-# (i.e. not used).
dummy-variables-rgx=_|dummy
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=80
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )?<?https?://\S+>?$
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-# List of optional constructs for which whitespace checking is disabled
-no-space-check=trailing-comma,dict-separator
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,TERMIOS,Bastion,rexec
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=5
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore
-ignored-argument-names=_.*
-
-# Maximum number of locals for function / method body
-max-locals=15
-
-# Maximum number of return / yield for function / method body
-max-returns=6
-
-# Maximum number of branch for function / method body
-max-branches=12
-
-# Maximum number of statements in function / method body
-max-statements=50
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=2
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-
-[CLASSES]
-
-# List of interface methods to ignore, separated by a comma. This is used for
-# instance to not check methods defines in Zope's Interface base class.
-ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=Exception
diff --git a/cc/scriptlib/__init__.py b/cc/scriptlib/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/cc/scriptlib/__init__.py
+++ /dev/null
diff --git a/cc/scriptlib/test_gen_stub_libs.py b/cc/scriptlib/test_gen_stub_libs.py
deleted file mode 100755
index 0b45e71..0000000
--- a/cc/scriptlib/test_gen_stub_libs.py
+++ /dev/null
@@ -1,807 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-"""Tests for gen_stub_libs.py."""
-import io
-import textwrap
-import unittest
-
-import gen_stub_libs as gsl
-
-
-# pylint: disable=missing-docstring
-
-
-class DecodeApiLevelTest(unittest.TestCase):
- def test_decode_api_level(self):
- self.assertEqual(9, gsl.decode_api_level('9', {}))
- self.assertEqual(9000, gsl.decode_api_level('O', {'O': 9000}))
-
- with self.assertRaises(KeyError):
- gsl.decode_api_level('O', {})
-
-
-class TagsTest(unittest.TestCase):
- def test_get_tags_no_tags(self):
- self.assertEqual([], gsl.get_tags(''))
- self.assertEqual([], gsl.get_tags('foo bar baz'))
-
- def test_get_tags(self):
- self.assertEqual(['foo', 'bar'], gsl.get_tags('# foo bar'))
- self.assertEqual(['bar', 'baz'], gsl.get_tags('foo # bar baz'))
-
- def test_split_tag(self):
- self.assertTupleEqual(('foo', 'bar'), gsl.split_tag('foo=bar'))
- self.assertTupleEqual(('foo', 'bar=baz'), gsl.split_tag('foo=bar=baz'))
- with self.assertRaises(ValueError):
- gsl.split_tag('foo')
-
- def test_get_tag_value(self):
- self.assertEqual('bar', gsl.get_tag_value('foo=bar'))
- self.assertEqual('bar=baz', gsl.get_tag_value('foo=bar=baz'))
- with self.assertRaises(ValueError):
- gsl.get_tag_value('foo')
-
- def test_is_api_level_tag(self):
- self.assertTrue(gsl.is_api_level_tag('introduced=24'))
- self.assertTrue(gsl.is_api_level_tag('introduced-arm=24'))
- self.assertTrue(gsl.is_api_level_tag('versioned=24'))
-
- # Shouldn't try to process things that aren't a key/value tag.
- self.assertFalse(gsl.is_api_level_tag('arm'))
- self.assertFalse(gsl.is_api_level_tag('introduced'))
- self.assertFalse(gsl.is_api_level_tag('versioned'))
-
- # We don't support arch specific `versioned` tags.
- self.assertFalse(gsl.is_api_level_tag('versioned-arm=24'))
-
- def test_decode_api_level_tags(self):
- api_map = {
- 'O': 9000,
- 'P': 9001,
- }
-
- tags = [
- 'introduced=9',
- 'introduced-arm=14',
- 'versioned=16',
- 'arm',
- 'introduced=O',
- 'introduced=P',
- ]
- expected_tags = [
- 'introduced=9',
- 'introduced-arm=14',
- 'versioned=16',
- 'arm',
- 'introduced=9000',
- 'introduced=9001',
- ]
- self.assertListEqual(
- expected_tags, gsl.decode_api_level_tags(tags, api_map))
-
- with self.assertRaises(gsl.ParseError):
- gsl.decode_api_level_tags(['introduced=O'], {})
-
-
-class PrivateVersionTest(unittest.TestCase):
- def test_version_is_private(self):
- self.assertFalse(gsl.version_is_private('foo'))
- self.assertFalse(gsl.version_is_private('PRIVATE'))
- self.assertFalse(gsl.version_is_private('PLATFORM'))
- self.assertFalse(gsl.version_is_private('foo_private'))
- self.assertFalse(gsl.version_is_private('foo_platform'))
- self.assertFalse(gsl.version_is_private('foo_PRIVATE_'))
- self.assertFalse(gsl.version_is_private('foo_PLATFORM_'))
-
- self.assertTrue(gsl.version_is_private('foo_PRIVATE'))
- self.assertTrue(gsl.version_is_private('foo_PLATFORM'))
-
-
-class SymbolPresenceTest(unittest.TestCase):
- def test_symbol_in_arch(self):
- self.assertTrue(gsl.symbol_in_arch([], 'arm'))
- self.assertTrue(gsl.symbol_in_arch(['arm'], 'arm'))
-
- self.assertFalse(gsl.symbol_in_arch(['x86'], 'arm'))
-
- def test_symbol_in_api(self):
- self.assertTrue(gsl.symbol_in_api([], 'arm', 9))
- self.assertTrue(gsl.symbol_in_api(['introduced=9'], 'arm', 9))
- self.assertTrue(gsl.symbol_in_api(['introduced=9'], 'arm', 14))
- self.assertTrue(gsl.symbol_in_api(['introduced-arm=9'], 'arm', 14))
- self.assertTrue(gsl.symbol_in_api(['introduced-arm=9'], 'arm', 14))
- self.assertTrue(gsl.symbol_in_api(['introduced-x86=14'], 'arm', 9))
- self.assertTrue(gsl.symbol_in_api(
- ['introduced-arm=9', 'introduced-x86=21'], 'arm', 14))
- self.assertTrue(gsl.symbol_in_api(
- ['introduced=9', 'introduced-x86=21'], 'arm', 14))
- self.assertTrue(gsl.symbol_in_api(
- ['introduced=21', 'introduced-arm=9'], 'arm', 14))
- self.assertTrue(gsl.symbol_in_api(
- ['future'], 'arm', gsl.FUTURE_API_LEVEL))
-
- self.assertFalse(gsl.symbol_in_api(['introduced=14'], 'arm', 9))
- self.assertFalse(gsl.symbol_in_api(['introduced-arm=14'], 'arm', 9))
- self.assertFalse(gsl.symbol_in_api(['future'], 'arm', 9))
- self.assertFalse(gsl.symbol_in_api(
- ['introduced=9', 'future'], 'arm', 14))
- self.assertFalse(gsl.symbol_in_api(
- ['introduced-arm=9', 'future'], 'arm', 14))
- self.assertFalse(gsl.symbol_in_api(
- ['introduced-arm=21', 'introduced-x86=9'], 'arm', 14))
- self.assertFalse(gsl.symbol_in_api(
- ['introduced=9', 'introduced-arm=21'], 'arm', 14))
- self.assertFalse(gsl.symbol_in_api(
- ['introduced=21', 'introduced-x86=9'], 'arm', 14))
-
- # Interesting edge case: this symbol should be omitted from the
- # library, but this call should still return true because none of the
- # tags indiciate that it's not present in this API level.
- self.assertTrue(gsl.symbol_in_api(['x86'], 'arm', 9))
-
- def test_verioned_in_api(self):
- self.assertTrue(gsl.symbol_versioned_in_api([], 9))
- self.assertTrue(gsl.symbol_versioned_in_api(['versioned=9'], 9))
- self.assertTrue(gsl.symbol_versioned_in_api(['versioned=9'], 14))
-
- self.assertFalse(gsl.symbol_versioned_in_api(['versioned=14'], 9))
-
-
-class OmitVersionTest(unittest.TestCase):
- def test_omit_private(self):
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, [], []), 'arm', 9, False, False))
-
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo_PRIVATE', None, [], []), 'arm', 9, False, False))
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo_PLATFORM', None, [], []), 'arm', 9, False, False))
-
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['platform-only'], []), 'arm', 9,
- False, False))
-
- def test_omit_llndk(self):
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['llndk'], []), 'arm', 9, False, False))
-
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, [], []), 'arm', 9, True, False))
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['llndk'], []), 'arm', 9, True, False))
-
- def test_omit_apex(self):
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['apex'], []), 'arm', 9, False, False))
-
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, [], []), 'arm', 9, False, True))
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['apex'], []), 'arm', 9, False, True))
-
- def test_omit_arch(self):
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, [], []), 'arm', 9, False, False))
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['arm'], []), 'arm', 9, False, False))
-
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['x86'], []), 'arm', 9, False, False))
-
- def test_omit_api(self):
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, [], []), 'arm', 9, False, False))
- self.assertFalse(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['introduced=9'], []), 'arm', 9,
- False, False))
-
- self.assertTrue(
- gsl.should_omit_version(
- gsl.Version('foo', None, ['introduced=14'], []), 'arm', 9,
- False, False))
-
-
-class OmitSymbolTest(unittest.TestCase):
- def test_omit_llndk(self):
- self.assertTrue(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['llndk']), 'arm', 9, False, False))
-
- self.assertFalse(
- gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, True, False))
- self.assertFalse(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['llndk']), 'arm', 9, True, False))
-
- def test_omit_apex(self):
- self.assertTrue(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['apex']), 'arm', 9, False, False))
-
- self.assertFalse(
- gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False, True))
- self.assertFalse(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['apex']), 'arm', 9, False, True))
-
- def test_omit_arch(self):
- self.assertFalse(
- gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False, False))
- self.assertFalse(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['arm']), 'arm', 9, False, False))
-
- self.assertTrue(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['x86']), 'arm', 9, False, False))
-
- def test_omit_api(self):
- self.assertFalse(
- gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False, False))
- self.assertFalse(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['introduced=9']), 'arm', 9, False, False))
-
- self.assertTrue(
- gsl.should_omit_symbol(
- gsl.Symbol('foo', ['introduced=14']), 'arm', 9, False, False))
-
-
-class SymbolFileParseTest(unittest.TestCase):
- def test_next_line(self):
- input_file = io.StringIO(textwrap.dedent("""\
- foo
-
- bar
- # baz
- qux
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- self.assertIsNone(parser.current_line)
-
- self.assertEqual('foo', parser.next_line().strip())
- self.assertEqual('foo', parser.current_line.strip())
-
- self.assertEqual('bar', parser.next_line().strip())
- self.assertEqual('bar', parser.current_line.strip())
-
- self.assertEqual('qux', parser.next_line().strip())
- self.assertEqual('qux', parser.current_line.strip())
-
- self.assertEqual('', parser.next_line())
- self.assertEqual('', parser.current_line)
-
- def test_parse_version(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 { # foo bar
- baz;
- qux; # woodly doodly
- };
-
- VERSION_2 {
- } VERSION_1; # asdf
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
-
- parser.next_line()
- version = parser.parse_version()
- self.assertEqual('VERSION_1', version.name)
- self.assertIsNone(version.base)
- self.assertEqual(['foo', 'bar'], version.tags)
-
- expected_symbols = [
- gsl.Symbol('baz', []),
- gsl.Symbol('qux', ['woodly', 'doodly']),
- ]
- self.assertEqual(expected_symbols, version.symbols)
-
- parser.next_line()
- version = parser.parse_version()
- self.assertEqual('VERSION_2', version.name)
- self.assertEqual('VERSION_1', version.base)
- self.assertEqual([], version.tags)
-
- def test_parse_version_eof(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- parser.next_line()
- with self.assertRaises(gsl.ParseError):
- parser.parse_version()
-
- def test_unknown_scope_label(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- foo:
- }
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- parser.next_line()
- with self.assertRaises(gsl.ParseError):
- parser.parse_version()
-
- def test_parse_symbol(self):
- input_file = io.StringIO(textwrap.dedent("""\
- foo;
- bar; # baz qux
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
-
- parser.next_line()
- symbol = parser.parse_symbol()
- self.assertEqual('foo', symbol.name)
- self.assertEqual([], symbol.tags)
-
- parser.next_line()
- symbol = parser.parse_symbol()
- self.assertEqual('bar', symbol.name)
- self.assertEqual(['baz', 'qux'], symbol.tags)
-
- def test_wildcard_symbol_global(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- *;
- };
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- parser.next_line()
- with self.assertRaises(gsl.ParseError):
- parser.parse_version()
-
- def test_wildcard_symbol_local(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- local:
- *;
- };
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- parser.next_line()
- version = parser.parse_version()
- self.assertEqual([], version.symbols)
-
- def test_missing_semicolon(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- foo
- };
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- parser.next_line()
- with self.assertRaises(gsl.ParseError):
- parser.parse_version()
-
- def test_parse_fails_invalid_input(self):
- with self.assertRaises(gsl.ParseError):
- input_file = io.StringIO('foo')
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- parser.parse()
-
- def test_parse(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- local:
- hidden1;
- global:
- foo;
- bar; # baz
- };
-
- VERSION_2 { # wasd
- # Implicit global scope.
- woodly;
- doodly; # asdf
- local:
- qwerty;
- } VERSION_1;
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
- versions = parser.parse()
-
- expected = [
- gsl.Version('VERSION_1', None, [], [
- gsl.Symbol('foo', []),
- gsl.Symbol('bar', ['baz']),
- ]),
- gsl.Version('VERSION_2', 'VERSION_1', ['wasd'], [
- gsl.Symbol('woodly', []),
- gsl.Symbol('doodly', ['asdf']),
- ]),
- ]
-
- self.assertEqual(expected, versions)
-
- def test_parse_llndk_apex_symbol(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- foo;
- bar; # llndk
- baz; # llndk apex
- qux; # apex
- };
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
-
- parser.next_line()
- version = parser.parse_version()
- self.assertEqual('VERSION_1', version.name)
- self.assertIsNone(version.base)
-
- expected_symbols = [
- gsl.Symbol('foo', []),
- gsl.Symbol('bar', ['llndk']),
- gsl.Symbol('baz', ['llndk', 'apex']),
- gsl.Symbol('qux', ['apex']),
- ]
- self.assertEqual(expected_symbols, version.symbols)
-
-
-class GeneratorTest(unittest.TestCase):
- def test_omit_version(self):
- # Thorough testing of the cases involved here is handled by
- # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
- src_file = io.StringIO()
- version_file = io.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
-
- version = gsl.Version('VERSION_PRIVATE', None, [], [
- gsl.Symbol('foo', []),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- version = gsl.Version('VERSION', None, ['x86'], [
- gsl.Symbol('foo', []),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- version = gsl.Version('VERSION', None, ['introduced=14'], [
- gsl.Symbol('foo', []),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- def test_omit_symbol(self):
- # Thorough testing of the cases involved here is handled by
- # SymbolPresenceTest.
- src_file = io.StringIO()
- version_file = io.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
-
- version = gsl.Version('VERSION_1', None, [], [
- gsl.Symbol('foo', ['x86']),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- version = gsl.Version('VERSION_1', None, [], [
- gsl.Symbol('foo', ['introduced=14']),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- version = gsl.Version('VERSION_1', None, [], [
- gsl.Symbol('foo', ['llndk']),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- version = gsl.Version('VERSION_1', None, [], [
- gsl.Symbol('foo', ['apex']),
- ])
- generator.write_version(version)
- self.assertEqual('', src_file.getvalue())
- self.assertEqual('', version_file.getvalue())
-
- def test_write(self):
- src_file = io.StringIO()
- version_file = io.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
-
- versions = [
- gsl.Version('VERSION_1', None, [], [
- gsl.Symbol('foo', []),
- gsl.Symbol('bar', ['var']),
- gsl.Symbol('woodly', ['weak']),
- gsl.Symbol('doodly', ['weak', 'var']),
- ]),
- gsl.Version('VERSION_2', 'VERSION_1', [], [
- gsl.Symbol('baz', []),
- ]),
- gsl.Version('VERSION_3', 'VERSION_1', [], [
- gsl.Symbol('qux', ['versioned=14']),
- ]),
- ]
-
- generator.write(versions)
- expected_src = textwrap.dedent("""\
- void foo() {}
- int bar = 0;
- __attribute__((weak)) void woodly() {}
- __attribute__((weak)) int doodly = 0;
- void baz() {}
- void qux() {}
- """)
- self.assertEqual(expected_src, src_file.getvalue())
-
- expected_version = textwrap.dedent("""\
- VERSION_1 {
- global:
- foo;
- bar;
- woodly;
- doodly;
- };
- VERSION_2 {
- global:
- baz;
- } VERSION_1;
- """)
- self.assertEqual(expected_version, version_file.getvalue())
-
-
-class IntegrationTest(unittest.TestCase):
- def test_integration(self):
- api_map = {
- 'O': 9000,
- 'P': 9001,
- }
-
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- global:
- foo; # var
- bar; # x86
- fizz; # introduced=O
- buzz; # introduced=P
- local:
- *;
- };
-
- VERSION_2 { # arm
- baz; # introduced=9
- qux; # versioned=14
- } VERSION_1;
-
- VERSION_3 { # introduced=14
- woodly;
- doodly; # var
- } VERSION_2;
-
- VERSION_4 { # versioned=9
- wibble;
- wizzes; # llndk
- waggle; # apex
- } VERSION_2;
-
- VERSION_5 { # versioned=14
- wobble;
- } VERSION_4;
- """))
- parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9, False, False)
- versions = parser.parse()
-
- src_file = io.StringIO()
- version_file = io.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
- generator.write(versions)
-
- expected_src = textwrap.dedent("""\
- int foo = 0;
- void baz() {}
- void qux() {}
- void wibble() {}
- void wobble() {}
- """)
- self.assertEqual(expected_src, src_file.getvalue())
-
- expected_version = textwrap.dedent("""\
- VERSION_1 {
- global:
- foo;
- };
- VERSION_2 {
- global:
- baz;
- } VERSION_1;
- VERSION_4 {
- global:
- wibble;
- } VERSION_2;
- """)
- self.assertEqual(expected_version, version_file.getvalue())
-
- def test_integration_future_api(self):
- api_map = {
- 'O': 9000,
- 'P': 9001,
- 'Q': 9002,
- }
-
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- global:
- foo; # introduced=O
- bar; # introduced=P
- baz; # introduced=Q
- local:
- *;
- };
- """))
- parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9001, False, False)
- versions = parser.parse()
-
- src_file = io.StringIO()
- version_file = io.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9001, False, False)
- generator.write(versions)
-
- expected_src = textwrap.dedent("""\
- void foo() {}
- void bar() {}
- """)
- self.assertEqual(expected_src, src_file.getvalue())
-
- expected_version = textwrap.dedent("""\
- VERSION_1 {
- global:
- foo;
- bar;
- };
- """)
- self.assertEqual(expected_version, version_file.getvalue())
-
- def test_multiple_definition(self):
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- global:
- foo;
- foo;
- bar;
- baz;
- qux; # arm
- local:
- *;
- };
-
- VERSION_2 {
- global:
- bar;
- qux; # arm64
- } VERSION_1;
-
- VERSION_PRIVATE {
- global:
- baz;
- } VERSION_2;
-
- """))
- parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
-
- with self.assertRaises(gsl.MultiplyDefinedSymbolError) as cm:
- parser.parse()
- self.assertEquals(['bar', 'foo'],
- cm.exception.multiply_defined_symbols)
-
- def test_integration_with_apex(self):
- api_map = {
- 'O': 9000,
- 'P': 9001,
- }
-
- input_file = io.StringIO(textwrap.dedent("""\
- VERSION_1 {
- global:
- foo; # var
- bar; # x86
- fizz; # introduced=O
- buzz; # introduced=P
- local:
- *;
- };
-
- VERSION_2 { # arm
- baz; # introduced=9
- qux; # versioned=14
- } VERSION_1;
-
- VERSION_3 { # introduced=14
- woodly;
- doodly; # var
- } VERSION_2;
-
- VERSION_4 { # versioned=9
- wibble;
- wizzes; # llndk
- waggle; # apex
- bubble; # apex llndk
- duddle; # llndk apex
- } VERSION_2;
-
- VERSION_5 { # versioned=14
- wobble;
- } VERSION_4;
- """))
- parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9, False, True)
- versions = parser.parse()
-
- src_file = io.StringIO()
- version_file = io.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False, True)
- generator.write(versions)
-
- expected_src = textwrap.dedent("""\
- int foo = 0;
- void baz() {}
- void qux() {}
- void wibble() {}
- void waggle() {}
- void bubble() {}
- void duddle() {}
- void wobble() {}
- """)
- self.assertEqual(expected_src, src_file.getvalue())
-
- expected_version = textwrap.dedent("""\
- VERSION_1 {
- global:
- foo;
- };
- VERSION_2 {
- global:
- baz;
- } VERSION_1;
- VERSION_4 {
- global:
- wibble;
- waggle;
- bubble;
- duddle;
- } VERSION_2;
- """)
- self.assertEqual(expected_version, version_file.getvalue())
-
-def main():
- suite = unittest.TestLoader().loadTestsFromName(__name__)
- unittest.TextTestRunner(verbosity=3).run(suite)
-
-
-if __name__ == '__main__':
- main()
diff --git a/cc/scriptlib/test_ndk_api_coverage_parser.py b/cc/scriptlib/test_ndk_api_coverage_parser.py
deleted file mode 100644
index a3c9b70..0000000
--- a/cc/scriptlib/test_ndk_api_coverage_parser.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-"""Tests for ndk_api_coverage_parser.py."""
-import io
-import textwrap
-import unittest
-
-from xml.etree.ElementTree import tostring
-from gen_stub_libs import FUTURE_API_LEVEL, SymbolFileParser
-import ndk_api_coverage_parser as nparser
-
-
-# pylint: disable=missing-docstring
-
-
-class ApiCoverageSymbolFileParserTest(unittest.TestCase):
- def test_parse(self):
- input_file = io.StringIO(textwrap.dedent(u"""\
- LIBLOG { # introduced-arm64=24 introduced-x86=24 introduced-x86_64=24
- global:
- android_name_to_log_id; # apex llndk introduced=23
- android_log_id_to_name; # llndk arm
- __android_log_assert; # introduced-x86=23
- __android_log_buf_print; # var
- __android_log_buf_write;
- local:
- *;
- };
-
- LIBLOG_PLATFORM {
- android_fdtrack; # llndk
- android_net; # introduced=23
- };
-
- LIBLOG_FOO { # var
- android_var;
- };
- """))
- parser = SymbolFileParser(input_file, {}, "", FUTURE_API_LEVEL, True, True)
- generator = nparser.XmlGenerator(io.StringIO())
- result = tostring(generator.convertToXml(parser.parse())).decode()
- expected = '<ndk-library><symbol apex="True" arch="" introduced="23" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_name_to_log_id" /><symbol arch="arm" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_log_id_to_name" /><symbol arch="" introduced-arm64="24" introduced-x86="23" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_assert" /><symbol arch="" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_buf_write" /><symbol arch="" is_deprecated="False" is_platform="True" llndk="True" name="android_fdtrack" /><symbol arch="" introduced="23" is_deprecated="False" is_platform="True" name="android_net" /></ndk-library>'
- self.assertEqual(expected, result)
-
-
-def main():
- suite = unittest.TestLoader().loadTestsFromName(__name__)
- unittest.TextTestRunner(verbosity=3).run(suite)
-
-
-if __name__ == '__main__':
- main()
diff --git a/cc/symbolfile/.gitignore b/cc/symbolfile/.gitignore
new file mode 100644
index 0000000..fd94eac
--- /dev/null
+++ b/cc/symbolfile/.gitignore
@@ -0,0 +1,140 @@
+# From https://github.com/github/gitignore/blob/master/Python.gitignore
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
diff --git a/cc/scriptlib/Android.bp b/cc/symbolfile/Android.bp
similarity index 69%
copy from cc/scriptlib/Android.bp
copy to cc/symbolfile/Android.bp
index ff9a2f0..5b43309 100644
--- a/cc/scriptlib/Android.bp
+++ b/cc/symbolfile/Android.bp
@@ -14,19 +14,20 @@
// limitations under the License.
//
-python_test_host {
- name: "test_ndk_api_coverage_parser",
- main: "test_ndk_api_coverage_parser.py",
+python_library_host {
+ name: "symbolfile",
+ pkg_path: "symbolfile",
srcs: [
- "test_ndk_api_coverage_parser.py",
+ "__init__.py",
],
}
-python_binary_host {
- name: "ndk_api_coverage_parser",
- main: "ndk_api_coverage_parser.py",
+python_test_host {
+ name: "test_symbolfile",
srcs: [
- "gen_stub_libs.py",
- "ndk_api_coverage_parser.py",
+ "test_symbolfile.py",
+ ],
+ libs: [
+ "symbolfile",
],
}
diff --git a/cc/symbolfile/OWNERS b/cc/symbolfile/OWNERS
new file mode 100644
index 0000000..f0d8733
--- /dev/null
+++ b/cc/symbolfile/OWNERS
@@ -0,0 +1 @@
+danalbert@google.com
diff --git a/cc/scriptlib/gen_stub_libs.py b/cc/symbolfile/__init__.py
old mode 100755
new mode 100644
similarity index 72%
rename from cc/scriptlib/gen_stub_libs.py
rename to cc/symbolfile/__init__.py
index d61dfbb..faa3823
--- a/cc/scriptlib/gen_stub_libs.py
+++ b/cc/symbolfile/__init__.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -14,13 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-"""Generates source for stub shared libraries for the NDK."""
-import argparse
-import json
+"""Parser for Android's version script information."""
import logging
-import os
import re
-import sys
ALL_ARCHITECTURES = (
@@ -57,6 +52,24 @@
return False
+def decode_api_level(api, api_map):
+ """Decodes the API level argument into the API level number.
+
+ For the average case, this just decodes the integer value from the string,
+ but for unreleased APIs we need to translate from the API codename (like
+ "O") to the future API level for that codename.
+ """
+ try:
+ return int(api)
+ except ValueError:
+ pass
+
+ if api == "current":
+ return FUTURE_API_LEVEL
+
+ return api_map[api]
+
+
def decode_api_level_tags(tags, api_map):
"""Decodes API level code names in a list of tags.
@@ -118,7 +131,8 @@
if 'platform-only' in version.tags:
return True
- no_llndk_no_apex = 'llndk' not in version.tags and 'apex' not in version.tags
+ no_llndk_no_apex = ('llndk' not in version.tags
+ and 'apex' not in version.tags)
keep = no_llndk_no_apex or \
('llndk' in version.tags and llndk) or \
('apex' in version.tags and apex)
@@ -205,7 +219,6 @@
class ParseError(RuntimeError):
"""An error that occurred while parsing a symbol file."""
- pass
class MultiplyDefinedSymbolError(RuntimeError):
@@ -217,7 +230,7 @@
self.multiply_defined_symbols = multiply_defined_symbols
-class Version(object):
+class Version:
"""A version block of a symbol file."""
def __init__(self, name, base, tags, symbols):
self.name = name
@@ -237,7 +250,7 @@
return True
-class Symbol(object):
+class Symbol:
"""A symbol definition from a symbol file."""
def __init__(self, name, tags):
self.name = name
@@ -247,7 +260,7 @@
return self.name == other.name and set(self.tags) == set(other.tags)
-class SymbolFileParser(object):
+class SymbolFileParser:
"""Parses NDK symbol files."""
def __init__(self, input_file, api_map, arch, api, llndk, apex):
self.input_file = input_file
@@ -283,11 +296,13 @@
symbol_names = set()
multiply_defined_symbols = set()
for version in versions:
- if should_omit_version(version, self.arch, self.api, self.llndk, self.apex):
+ if should_omit_version(version, self.arch, self.api, self.llndk,
+ self.apex):
continue
for symbol in version.symbols:
- if should_omit_symbol(symbol, self.arch, self.api, self.llndk, self.apex):
+ if should_omit_symbol(symbol, self.arch, self.api, self.llndk,
+ self.apex):
continue
if symbol.name in symbol_names:
@@ -367,141 +382,3 @@
break
self.current_line = line
return self.current_line
-
-
-class Generator(object):
- """Output generator that writes stub source files and version scripts."""
- def __init__(self, src_file, version_script, arch, api, llndk, apex):
- self.src_file = src_file
- self.version_script = version_script
- self.arch = arch
- self.api = api
- self.llndk = llndk
- self.apex = apex
-
- def write(self, versions):
- """Writes all symbol data to the output files."""
- for version in versions:
- self.write_version(version)
-
- def write_version(self, version):
- """Writes a single version block's data to the output files."""
- if should_omit_version(version, self.arch, self.api, self.llndk, self.apex):
- return
-
- section_versioned = symbol_versioned_in_api(version.tags, self.api)
- version_empty = True
- pruned_symbols = []
- for symbol in version.symbols:
- if should_omit_symbol(symbol, self.arch, self.api, self.llndk, self.apex):
- continue
-
- if symbol_versioned_in_api(symbol.tags, self.api):
- version_empty = False
- pruned_symbols.append(symbol)
-
- if len(pruned_symbols) > 0:
- if not version_empty and section_versioned:
- self.version_script.write(version.name + ' {\n')
- self.version_script.write(' global:\n')
- for symbol in pruned_symbols:
- emit_version = symbol_versioned_in_api(symbol.tags, self.api)
- if section_versioned and emit_version:
- self.version_script.write(' ' + symbol.name + ';\n')
-
- weak = ''
- if 'weak' in symbol.tags:
- weak = '__attribute__((weak)) '
-
- if 'var' in symbol.tags:
- self.src_file.write('{}int {} = 0;\n'.format(
- weak, symbol.name))
- else:
- self.src_file.write('{}void {}() {{}}\n'.format(
- weak, symbol.name))
-
- if not version_empty and section_versioned:
- base = '' if version.base is None else ' ' + version.base
- self.version_script.write('}' + base + ';\n')
-
-
-def decode_api_level(api, api_map):
- """Decodes the API level argument into the API level number.
-
- For the average case, this just decodes the integer value from the string,
- but for unreleased APIs we need to translate from the API codename (like
- "O") to the future API level for that codename.
- """
- try:
- return int(api)
- except ValueError:
- pass
-
- if api == "current":
- return FUTURE_API_LEVEL
-
- return api_map[api]
-
-
-def parse_args():
- """Parses and returns command line arguments."""
- parser = argparse.ArgumentParser()
-
- parser.add_argument('-v', '--verbose', action='count', default=0)
-
- parser.add_argument(
- '--api', required=True, help='API level being targeted.')
- parser.add_argument(
- '--arch', choices=ALL_ARCHITECTURES, required=True,
- help='Architecture being targeted.')
- parser.add_argument(
- '--llndk', action='store_true', help='Use the LLNDK variant.')
- parser.add_argument(
- '--apex', action='store_true', help='Use the APEX variant.')
-
- parser.add_argument(
- '--api-map', type=os.path.realpath, required=True,
- help='Path to the API level map JSON file.')
-
- parser.add_argument(
- 'symbol_file', type=os.path.realpath, help='Path to symbol file.')
- parser.add_argument(
- 'stub_src', type=os.path.realpath,
- help='Path to output stub source file.')
- parser.add_argument(
- 'version_script', type=os.path.realpath,
- help='Path to output version script.')
-
- return parser.parse_args()
-
-
-def main():
- """Program entry point."""
- args = parse_args()
-
- with open(args.api_map) as map_file:
- api_map = json.load(map_file)
- api = decode_api_level(args.api, api_map)
-
- verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG)
- verbosity = args.verbose
- if verbosity > 2:
- verbosity = 2
- logging.basicConfig(level=verbose_map[verbosity])
-
- with open(args.symbol_file) as symbol_file:
- try:
- versions = SymbolFileParser(symbol_file, api_map, args.arch, api,
- args.llndk, args.apex).parse()
- except MultiplyDefinedSymbolError as ex:
- sys.exit('{}: error: {}'.format(args.symbol_file, ex))
-
- with open(args.stub_src, 'w') as src_file:
- with open(args.version_script, 'w') as version_file:
- generator = Generator(src_file, version_file, args.arch, api,
- args.llndk, args.apex)
- generator.write(versions)
-
-
-if __name__ == '__main__':
- main()
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
new file mode 100644
index 0000000..c91131f
--- /dev/null
+++ b/cc/symbolfile/test_symbolfile.py
@@ -0,0 +1,493 @@
+#
+# Copyright (C) 2016 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.
+#
+"""Tests for symbolfile."""
+import io
+import textwrap
+import unittest
+
+import symbolfile
+
+# pylint: disable=missing-docstring
+
+
+class DecodeApiLevelTest(unittest.TestCase):
+ def test_decode_api_level(self):
+ self.assertEqual(9, symbolfile.decode_api_level('9', {}))
+ self.assertEqual(9000, symbolfile.decode_api_level('O', {'O': 9000}))
+
+ with self.assertRaises(KeyError):
+ symbolfile.decode_api_level('O', {})
+
+
+class TagsTest(unittest.TestCase):
+ def test_get_tags_no_tags(self):
+ self.assertEqual([], symbolfile.get_tags(''))
+ self.assertEqual([], symbolfile.get_tags('foo bar baz'))
+
+ def test_get_tags(self):
+ self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
+ self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
+
+ def test_split_tag(self):
+ self.assertTupleEqual(('foo', 'bar'), symbolfile.split_tag('foo=bar'))
+ self.assertTupleEqual(('foo', 'bar=baz'), symbolfile.split_tag('foo=bar=baz'))
+ with self.assertRaises(ValueError):
+ symbolfile.split_tag('foo')
+
+ def test_get_tag_value(self):
+ self.assertEqual('bar', symbolfile.get_tag_value('foo=bar'))
+ self.assertEqual('bar=baz', symbolfile.get_tag_value('foo=bar=baz'))
+ with self.assertRaises(ValueError):
+ symbolfile.get_tag_value('foo')
+
+ def test_is_api_level_tag(self):
+ self.assertTrue(symbolfile.is_api_level_tag('introduced=24'))
+ self.assertTrue(symbolfile.is_api_level_tag('introduced-arm=24'))
+ self.assertTrue(symbolfile.is_api_level_tag('versioned=24'))
+
+ # Shouldn't try to process things that aren't a key/value tag.
+ self.assertFalse(symbolfile.is_api_level_tag('arm'))
+ self.assertFalse(symbolfile.is_api_level_tag('introduced'))
+ self.assertFalse(symbolfile.is_api_level_tag('versioned'))
+
+ # We don't support arch specific `versioned` tags.
+ self.assertFalse(symbolfile.is_api_level_tag('versioned-arm=24'))
+
+ def test_decode_api_level_tags(self):
+ api_map = {
+ 'O': 9000,
+ 'P': 9001,
+ }
+
+ tags = [
+ 'introduced=9',
+ 'introduced-arm=14',
+ 'versioned=16',
+ 'arm',
+ 'introduced=O',
+ 'introduced=P',
+ ]
+ expected_tags = [
+ 'introduced=9',
+ 'introduced-arm=14',
+ 'versioned=16',
+ 'arm',
+ 'introduced=9000',
+ 'introduced=9001',
+ ]
+ self.assertListEqual(
+ expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
+
+ with self.assertRaises(symbolfile.ParseError):
+ symbolfile.decode_api_level_tags(['introduced=O'], {})
+
+
+class PrivateVersionTest(unittest.TestCase):
+ def test_version_is_private(self):
+ self.assertFalse(symbolfile.version_is_private('foo'))
+ self.assertFalse(symbolfile.version_is_private('PRIVATE'))
+ self.assertFalse(symbolfile.version_is_private('PLATFORM'))
+ self.assertFalse(symbolfile.version_is_private('foo_private'))
+ self.assertFalse(symbolfile.version_is_private('foo_platform'))
+ self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_'))
+ self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_'))
+
+ self.assertTrue(symbolfile.version_is_private('foo_PRIVATE'))
+ self.assertTrue(symbolfile.version_is_private('foo_PLATFORM'))
+
+
+class SymbolPresenceTest(unittest.TestCase):
+ def test_symbol_in_arch(self):
+ self.assertTrue(symbolfile.symbol_in_arch([], 'arm'))
+ self.assertTrue(symbolfile.symbol_in_arch(['arm'], 'arm'))
+
+ self.assertFalse(symbolfile.symbol_in_arch(['x86'], 'arm'))
+
+ def test_symbol_in_api(self):
+ self.assertTrue(symbolfile.symbol_in_api([], 'arm', 9))
+ self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 9))
+ self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 14))
+ self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
+ self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
+ self.assertTrue(symbolfile.symbol_in_api(['introduced-x86=14'], 'arm', 9))
+ self.assertTrue(symbolfile.symbol_in_api(
+ ['introduced-arm=9', 'introduced-x86=21'], 'arm', 14))
+ self.assertTrue(symbolfile.symbol_in_api(
+ ['introduced=9', 'introduced-x86=21'], 'arm', 14))
+ self.assertTrue(symbolfile.symbol_in_api(
+ ['introduced=21', 'introduced-arm=9'], 'arm', 14))
+ self.assertTrue(symbolfile.symbol_in_api(
+ ['future'], 'arm', symbolfile.FUTURE_API_LEVEL))
+
+ self.assertFalse(symbolfile.symbol_in_api(['introduced=14'], 'arm', 9))
+ self.assertFalse(symbolfile.symbol_in_api(['introduced-arm=14'], 'arm', 9))
+ self.assertFalse(symbolfile.symbol_in_api(['future'], 'arm', 9))
+ self.assertFalse(symbolfile.symbol_in_api(
+ ['introduced=9', 'future'], 'arm', 14))
+ self.assertFalse(symbolfile.symbol_in_api(
+ ['introduced-arm=9', 'future'], 'arm', 14))
+ self.assertFalse(symbolfile.symbol_in_api(
+ ['introduced-arm=21', 'introduced-x86=9'], 'arm', 14))
+ self.assertFalse(symbolfile.symbol_in_api(
+ ['introduced=9', 'introduced-arm=21'], 'arm', 14))
+ self.assertFalse(symbolfile.symbol_in_api(
+ ['introduced=21', 'introduced-x86=9'], 'arm', 14))
+
+ # Interesting edge case: this symbol should be omitted from the
+ # library, but this call should still return true because none of the
+ # tags indiciate that it's not present in this API level.
+ self.assertTrue(symbolfile.symbol_in_api(['x86'], 'arm', 9))
+
+ def test_verioned_in_api(self):
+ self.assertTrue(symbolfile.symbol_versioned_in_api([], 9))
+ self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 9))
+ self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 14))
+
+ self.assertFalse(symbolfile.symbol_versioned_in_api(['versioned=14'], 9))
+
+
+class OmitVersionTest(unittest.TestCase):
+ def test_omit_private(self):
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+ False))
+
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo_PRIVATE', None, [], []), 'arm', 9,
+ False, False))
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo_PLATFORM', None, [], []), 'arm', 9,
+ False, False))
+
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['platform-only'], []), 'arm',
+ 9, False, False))
+
+ def test_omit_llndk(self):
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9,
+ False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, [], []), 'arm', 9, True,
+ False))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9, True,
+ False))
+
+ def test_omit_apex(self):
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
+ False))
+
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+ True))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
+ True))
+
+ def test_omit_arch(self):
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+ False))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['arm'], []), 'arm', 9, False,
+ False))
+
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['x86'], []), 'arm', 9, False,
+ False))
+
+ def test_omit_api(self):
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+ False))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['introduced=9'], []), 'arm',
+ 9, False, False))
+
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, ['introduced=14'], []), 'arm',
+ 9, False, False))
+
+
+class OmitSymbolTest(unittest.TestCase):
+ def test_omit_llndk(self):
+ self.assertTrue(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
+ 'arm', 9, False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
+ 9, True, False))
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
+ 'arm', 9, True, False))
+
+ def test_omit_apex(self):
+ self.assertTrue(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
+ 'arm', 9, False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
+ 9, False, True))
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
+ 'arm', 9, False, True))
+
+ def test_omit_arch(self):
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
+ 9, False, False))
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['arm']),
+ 'arm', 9, False, False))
+
+ self.assertTrue(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['x86']),
+ 'arm', 9, False, False))
+
+ def test_omit_api(self):
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
+ 9, False, False))
+ self.assertFalse(
+ symbolfile.should_omit_symbol(
+ symbolfile.Symbol('foo', ['introduced=9']), 'arm', 9, False,
+ False))
+
+ self.assertTrue(
+ symbolfile.should_omit_symbol(
+ symbolfile.Symbol('foo', ['introduced=14']), 'arm', 9, False,
+ False))
+
+
+class SymbolFileParseTest(unittest.TestCase):
+ def test_next_line(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ foo
+
+ bar
+ # baz
+ qux
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ self.assertIsNone(parser.current_line)
+
+ self.assertEqual('foo', parser.next_line().strip())
+ self.assertEqual('foo', parser.current_line.strip())
+
+ self.assertEqual('bar', parser.next_line().strip())
+ self.assertEqual('bar', parser.current_line.strip())
+
+ self.assertEqual('qux', parser.next_line().strip())
+ self.assertEqual('qux', parser.current_line.strip())
+
+ self.assertEqual('', parser.next_line())
+ self.assertEqual('', parser.current_line)
+
+ def test_parse_version(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 { # foo bar
+ baz;
+ qux; # woodly doodly
+ };
+
+ VERSION_2 {
+ } VERSION_1; # asdf
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+
+ parser.next_line()
+ version = parser.parse_version()
+ self.assertEqual('VERSION_1', version.name)
+ self.assertIsNone(version.base)
+ self.assertEqual(['foo', 'bar'], version.tags)
+
+ expected_symbols = [
+ symbolfile.Symbol('baz', []),
+ symbolfile.Symbol('qux', ['woodly', 'doodly']),
+ ]
+ self.assertEqual(expected_symbols, version.symbols)
+
+ parser.next_line()
+ version = parser.parse_version()
+ self.assertEqual('VERSION_2', version.name)
+ self.assertEqual('VERSION_1', version.base)
+ self.assertEqual([], version.tags)
+
+ def test_parse_version_eof(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ parser.next_line()
+ with self.assertRaises(symbolfile.ParseError):
+ parser.parse_version()
+
+ def test_unknown_scope_label(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ foo:
+ }
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ parser.next_line()
+ with self.assertRaises(symbolfile.ParseError):
+ parser.parse_version()
+
+ def test_parse_symbol(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ foo;
+ bar; # baz qux
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+
+ parser.next_line()
+ symbol = parser.parse_symbol()
+ self.assertEqual('foo', symbol.name)
+ self.assertEqual([], symbol.tags)
+
+ parser.next_line()
+ symbol = parser.parse_symbol()
+ self.assertEqual('bar', symbol.name)
+ self.assertEqual(['baz', 'qux'], symbol.tags)
+
+ def test_wildcard_symbol_global(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ *;
+ };
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ parser.next_line()
+ with self.assertRaises(symbolfile.ParseError):
+ parser.parse_version()
+
+ def test_wildcard_symbol_local(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ local:
+ *;
+ };
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ parser.next_line()
+ version = parser.parse_version()
+ self.assertEqual([], version.symbols)
+
+ def test_missing_semicolon(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ foo
+ };
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ parser.next_line()
+ with self.assertRaises(symbolfile.ParseError):
+ parser.parse_version()
+
+ def test_parse_fails_invalid_input(self):
+ with self.assertRaises(symbolfile.ParseError):
+ input_file = io.StringIO('foo')
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16,
+ False, False)
+ parser.parse()
+
+ def test_parse(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ local:
+ hidden1;
+ global:
+ foo;
+ bar; # baz
+ };
+
+ VERSION_2 { # wasd
+ # Implicit global scope.
+ woodly;
+ doodly; # asdf
+ local:
+ qwerty;
+ } VERSION_1;
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+ versions = parser.parse()
+
+ expected = [
+ symbolfile.Version('VERSION_1', None, [], [
+ symbolfile.Symbol('foo', []),
+ symbolfile.Symbol('bar', ['baz']),
+ ]),
+ symbolfile.Version('VERSION_2', 'VERSION_1', ['wasd'], [
+ symbolfile.Symbol('woodly', []),
+ symbolfile.Symbol('doodly', ['asdf']),
+ ]),
+ ]
+
+ self.assertEqual(expected, versions)
+
+ def test_parse_llndk_apex_symbol(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ foo;
+ bar; # llndk
+ baz; # llndk apex
+ qux; # apex
+ };
+ """))
+ parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
+
+ parser.next_line()
+ version = parser.parse_version()
+ self.assertEqual('VERSION_1', version.name)
+ self.assertIsNone(version.base)
+
+ expected_symbols = [
+ symbolfile.Symbol('foo', []),
+ symbolfile.Symbol('bar', ['llndk']),
+ symbolfile.Symbol('baz', ['llndk', 'apex']),
+ symbolfile.Symbol('qux', ['apex']),
+ ]
+ self.assertEqual(expected_symbols, version.symbols)
+
+
+def main():
+ suite = unittest.TestLoader().loadTestsFromName(__name__)
+ unittest.TextTestRunner(verbosity=3).run(suite)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cc/testing.go b/cc/testing.go
index a106d46..4d0b28b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -30,6 +30,7 @@
ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
ctx.RegisterModuleType("cc_object", ObjectFactory)
+ ctx.RegisterModuleType("cc_genrule", genRuleFactory)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
@@ -39,6 +40,7 @@
ret := `
toolchain_library {
name: "libatomic",
+ defaults: ["linux_bionic_supported"],
vendor_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -92,6 +94,7 @@
toolchain_library {
name: "libclang_rt.builtins-x86_64-android",
+ defaults: ["linux_bionic_supported"],
vendor_available: true,
recovery_available: true,
native_bridge_supported: true,
@@ -121,6 +124,7 @@
toolchain_library {
name: "libclang_rt.fuzzer-x86_64-android",
+ defaults: ["linux_bionic_supported"],
vendor_available: true,
recovery_available: true,
src: "",
@@ -144,6 +148,7 @@
toolchain_library {
name: "libgcc",
+ defaults: ["linux_bionic_supported"],
vendor_available: true,
recovery_available: true,
src: "",
@@ -151,6 +156,7 @@
toolchain_library {
name: "libgcc_stripped",
+ defaults: ["linux_bionic_supported"],
vendor_available: true,
recovery_available: true,
sdk_version: "current",
@@ -159,6 +165,7 @@
cc_library {
name: "libc",
+ defaults: ["linux_bionic_supported"],
no_libcrt: true,
nocrt: true,
stl: "none",
@@ -175,6 +182,7 @@
}
cc_library {
name: "libm",
+ defaults: ["linux_bionic_supported"],
no_libcrt: true,
nocrt: true,
stl: "none",
@@ -234,6 +242,7 @@
cc_library {
name: "libdl",
+ defaults: ["linux_bionic_supported"],
no_libcrt: true,
nocrt: true,
stl: "none",
@@ -326,6 +335,7 @@
cc_defaults {
name: "crt_defaults",
+ defaults: ["linux_bionic_supported"],
recovery_available: true,
vendor_available: true,
native_bridge_supported: true,
@@ -437,6 +447,7 @@
}
`
+ supportLinuxBionic := false
for _, os := range oses {
if os == android.Fuchsia {
ret += `
@@ -465,7 +476,59 @@
}
`
}
+ if os == android.LinuxBionic {
+ supportLinuxBionic = true
+ ret += `
+ cc_binary {
+ name: "linker",
+ defaults: ["linux_bionic_supported"],
+ recovery_available: true,
+ stl: "none",
+ nocrt: true,
+ static_executable: true,
+ native_coverage: false,
+ system_shared_libs: [],
+ }
+
+ cc_genrule {
+ name: "host_bionic_linker_flags",
+ host_supported: true,
+ device_supported: false,
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ },
+ out: ["linker.flags"],
+ }
+
+ cc_defaults {
+ name: "linux_bionic_supported",
+ host_supported: true,
+ target: {
+ host: {
+ enabled: false,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ },
+ }
+ `
+ }
}
+
+ if !supportLinuxBionic {
+ ret += `
+ cc_defaults {
+ name: "linux_bionic_supported",
+ }
+ `
+ }
+
return ret
}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index c965107..e485c60 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -117,7 +117,7 @@
// Command is the type of soong_ui execution. Only one type of
// execution is specified. The args are specific to the command.
func main() {
- buildStartedMilli := time.Now().UnixNano() / int64(time.Millisecond)
+ buildStarted := time.Now()
c, args := getCommand(os.Args)
if c == nil {
@@ -138,6 +138,7 @@
defer trace.Close()
met := metrics.New()
+ met.SetBuildDateTime(buildStarted)
stat := &status.Status{}
defer stat.Finish()
@@ -171,7 +172,7 @@
buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
- defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+ defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
os.MkdirAll(logsDir, 0777)
log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
@@ -187,6 +188,7 @@
config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
defer met.Dump(soongMetricsFile)
+ defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
if !strings.HasSuffix(start, "N") {
diff --git a/docs/perf.md b/docs/perf.md
index 538adff..86a27b4 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -12,6 +12,41 @@

+### Critical path
+
+soong_ui logs the wall time of the longest dependency chain compared to the
+elapsed wall time in `$OUT_DIR/soong.log`. For example:
+```
+critical path took 3m10s
+elapsed time 5m16s
+perfect parallelism ratio 60%
+critical path:
+ 0:00 build out/target/product/generic_arm64/obj/FAKE/sepolicy_neverallows_intermediates/policy_2.conf
+ 0:04 build out/target/product/generic_arm64/obj/FAKE/sepolicy_neverallows_intermediates/sepolicy_neverallows
+ 0:13 build out/target/product/generic_arm64/obj/ETC/plat_sepolicy.cil_intermediates/plat_sepolicy.cil
+ 0:01 build out/target/product/generic_arm64/obj/ETC/plat_pub_versioned.cil_intermediates/plat_pub_versioned.cil
+ 0:02 build out/target/product/generic_arm64/obj/ETC/vendor_sepolicy.cil_intermediates/vendor_sepolicy.cil
+ 0:16 build out/target/product/generic_arm64/obj/ETC/sepolicy_intermediates/sepolicy
+ 0:00 build out/target/product/generic_arm64/obj/ETC/plat_seapp_contexts_intermediates/plat_seapp_contexts
+ 0:00 Install: out/target/product/generic_arm64/system/etc/selinux/plat_seapp_contexts
+ 0:02 build out/target/product/generic_arm64/obj/NOTICE.txt
+ 0:00 build out/target/product/generic_arm64/obj/NOTICE.xml.gz
+ 0:00 build out/target/product/generic_arm64/system/etc/NOTICE.xml.gz
+ 0:01 Installed file list: out/target/product/generic_arm64/installed-files.txt
+ 1:00 Target system fs image: out/target/product/generic_arm64/obj/PACKAGING/systemimage_intermediates/system.img
+ 0:01 Install system fs image: out/target/product/generic_arm64/system.img
+ 0:01 Target vbmeta image: out/target/product/generic_arm64/vbmeta.img
+ 1:26 Package target files: out/target/product/generic_arm64/obj/PACKAGING/target_files_intermediates/aosp_arm64-target_files-6663974.zip
+ 0:01 Package: out/target/product/generic_arm64/aosp_arm64-img-6663974.zip
+ 0:01 Dist: /buildbot/dist_dirs/aosp-master-linux-aosp_arm64-userdebug/6663974/aosp_arm64-img-6663974.zip
+```
+
+If the elapsed time is much longer than the critical path then additional
+parallelism on the build machine will improve total build times. If there are
+long individual times listed in the critical path then improving build times
+for those steps or adjusting dependencies so that those steps can run earlier
+in the build graph will improve total build times.
+
### Soong
Soong can be traced and profiled using the standard Go tools. It understands
diff --git a/java/aar.go b/java/aar.go
index 500788f..ad9b5e7 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -641,7 +641,7 @@
var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
blueprint.RuleParams{
Command: `rm -rf $outDir && mkdir -p $outDir && ` +
- `unzip -qo -d $outDir $in && rm -rf $outDir/res && touch $out`,
+ `unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out`,
},
"outDir")
diff --git a/java/androidmk.go b/java/androidmk.go
index 618e15d..03994bf 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -131,6 +131,10 @@
entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
}
entries.SetString("LOCAL_MODULE_STEM", library.Stem())
+
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveHTMLZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveTextZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveXMLZip)
},
},
}
@@ -370,9 +374,15 @@
entries.SetString("LOCAL_CERTIFICATE", app.certificate.AndroidMkString())
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", app.getOverriddenPackages()...)
- for _, jniLib := range app.installJniLibs {
- entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+ if app.embeddedJniLibs {
+ jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
+ entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
+ } else {
+ for _, jniLib := range app.jniLibs {
+ entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+ }
}
+
if len(app.jniCoverageOutputs) > 0 {
entries.AddStrings("LOCAL_PREBUILT_COVERAGE_ARCHIVE", app.jniCoverageOutputs.Strings()...)
}
@@ -383,6 +393,10 @@
install := app.onDeviceDir + "/" + extra.Base()
entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install)
}
+
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveHTMLZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveTextZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveXMLZip)
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
diff --git a/java/app.go b/java/app.go
index 98bce94..4031cfe 100755
--- a/java/app.go
+++ b/java/app.go
@@ -106,6 +106,10 @@
return as.masterFile
}
+func (as *AndroidAppSet) APKCertsFile() android.Path {
+ return as.apkcertsFile
+}
+
var TargetCpuAbi = map[string]string{
"arm": "ARMEABI_V7A",
"arm64": "ARM64_V8A",
@@ -288,8 +292,10 @@
overridableAppProperties overridableAppProperties
- installJniLibs []jniLib
- jniCoverageOutputs android.Paths
+ jniLibs []jniLib
+ installPathForJNISymbols android.Path
+ embeddedJniLibs bool
+ jniCoverageOutputs android.Paths
bundleFile android.Path
@@ -566,8 +572,7 @@
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
}
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
-
+func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath {
var installDir string
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
@@ -577,7 +582,12 @@
} else {
installDir = filepath.Join("app", a.installApkName)
}
- a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+
+ return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+}
+
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+ a.dexpreopter.installPath = a.installPath(ctx)
if a.deviceProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
@@ -599,8 +609,10 @@
func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
var jniJarFile android.WritablePath
if len(jniLibs) > 0 {
+ a.jniLibs = jniLibs
if a.shouldEmbedJnis(ctx) {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
+ a.installPathForJNISymbols = a.installPath(ctx).ToMakePath()
TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
for _, jni := range jniLibs {
if jni.coverageFile.Valid() {
@@ -618,13 +630,25 @@
}
}
}
- } else {
- a.installJniLibs = jniLibs
+ a.embeddedJniLibs = true
}
}
return jniJarFile
}
+func (a *AndroidApp) JNISymbolsInstalls(installPath string) android.RuleBuilderInstalls {
+ var jniSymbols android.RuleBuilderInstalls
+ for _, jniLib := range a.jniLibs {
+ if jniLib.unstrippedFile != nil {
+ jniSymbols = append(jniSymbols, android.RuleBuilderInstall{
+ From: jniLib.unstrippedFile,
+ To: filepath.Join(installPath, targetToJniDir(jniLib.target), jniLib.unstrippedFile.Base()),
+ })
+ }
+ }
+ return jniSymbols
+}
+
func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
// Collect NOTICE files from all dependencies.
seenModules := make(map[android.Module]bool)
@@ -752,6 +776,7 @@
a.linter.mergedManifest = a.aapt.mergedManifestFile
a.linter.manifest = a.aapt.manifestPath
a.linter.resources = a.aapt.resourceFiles
+ a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
dexJarFile := a.dexBuildActions(ctx)
@@ -840,10 +865,11 @@
if lib.Valid() {
jniLibs = append(jniLibs, jniLib{
- name: ctx.OtherModuleName(module),
- path: path,
- target: module.Target(),
- coverageFile: dep.CoverageOutputFile(),
+ name: ctx.OtherModuleName(module),
+ path: path,
+ target: module.Target(),
+ coverageFile: dep.CoverageOutputFile(),
+ unstrippedFile: dep.UnstrippedOutputFile(),
})
} else {
ctx.ModuleErrorf("dependency %q missing output file", otherName)
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 99bfb6d..190a052 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -112,13 +112,20 @@
// local files that are used within user customized droiddoc options.
Arg_files []string `android:"path"`
- // user customized droiddoc args.
+ // user customized droiddoc args. Deprecated, use flags instead.
// Available variables for substitution:
//
// $(location <label>): the path to the arg_files with name <label>
// $$: a literal $
Args *string
+ // user customized droiddoc args. Not compatible with property args.
+ // Available variables for substitution:
+ //
+ // $(location <label>): the path to the arg_files with name <label>
+ // $$: a literal $
+ Flags []string
+
// names of the output files used in args that will be generated
Out []string
@@ -382,7 +389,7 @@
argFiles android.Paths
implicits android.Paths
- args string
+ args []string
docZip android.WritablePath
stubsSrcJar android.WritablePath
@@ -619,8 +626,8 @@
}
srcFiles = filterHtml(srcFiles)
- flags := j.collectAidlFlags(ctx, deps)
- srcFiles = j.genSources(ctx, srcFiles, flags)
+ aidlFlags := j.collectAidlFlags(ctx, deps)
+ srcFiles = j.genSources(ctx, srcFiles, aidlFlags)
// srcs may depend on some genrule output.
j.srcJars = srcFiles.FilterByExt(".srcjar")
@@ -649,24 +656,38 @@
}
}
- var err error
- j.args, err = android.Expand(String(j.properties.Args), func(name string) (string, error) {
- if strings.HasPrefix(name, "location ") {
- label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if paths, ok := argFilesMap[label]; ok {
- return paths, nil
- } else {
- return "", fmt.Errorf("unknown location label %q, expecting one of %q",
- label, strings.Join(argFileLabels, ", "))
- }
- } else if name == "genDir" {
- return android.PathForModuleGen(ctx).String(), nil
- }
- return "", fmt.Errorf("unknown variable '$(%s)'", name)
- })
+ var argsPropertyName string
+ flags := make([]string, 0)
+ if j.properties.Args != nil && j.properties.Flags != nil {
+ ctx.PropertyErrorf("args", "flags is set. Cannot set args")
+ } else if args := proptools.String(j.properties.Args); args != "" {
+ flags = append(flags, args)
+ argsPropertyName = "args"
+ } else {
+ flags = append(flags, j.properties.Flags...)
+ argsPropertyName = "flags"
+ }
- if err != nil {
- ctx.PropertyErrorf("args", "%s", err.Error())
+ for _, flag := range flags {
+ args, err := android.Expand(flag, func(name string) (string, error) {
+ if strings.HasPrefix(name, "location ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+ if paths, ok := argFilesMap[label]; ok {
+ return paths, nil
+ } else {
+ return "", fmt.Errorf("unknown location label %q, expecting one of %q",
+ label, strings.Join(argFileLabels, ", "))
+ }
+ } else if name == "genDir" {
+ return android.PathForModuleGen(ctx).String(), nil
+ }
+ return "", fmt.Errorf("unknown variable '$(%s)'", name)
+ })
+
+ if err != nil {
+ ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
+ }
+ j.args = append(j.args, args)
}
return deps
@@ -1010,7 +1031,7 @@
d.stubsFlags(ctx, cmd, stubsDir)
- cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+ cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
if d.properties.Compat_config != nil {
compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
@@ -1327,7 +1348,7 @@
cmd.Flag("--include-annotations")
validatingNullability :=
- strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
+ android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
String(d.properties.Validate_nullability_from_list) != ""
migratingNullability := String(d.properties.Previous_api) != ""
@@ -1498,7 +1519,9 @@
cmd.Flag("--no-banner").
Flag("--color").
Flag("--quiet").
- Flag("--format=v2")
+ Flag("--format=v2").
+ FlagWithArg("--repeat-errors-max ", "10").
+ FlagWithArg("--hide ", "UnresolvedImport")
return cmd
}
@@ -1539,14 +1562,14 @@
d.apiLevelsAnnotationsFlags(ctx, cmd)
d.apiToXmlFlags(ctx, cmd)
- if strings.Contains(d.Javadoc.args, "--generate-documentation") {
+ if android.InList("--generate-documentation", d.Javadoc.args) {
// Currently Metalava have the ability to invoke Javadoc in a seperate process.
// Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
- d.Javadoc.args = d.Javadoc.args + " -nodocs "
+ d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
}
- cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+ cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
for _, o := range d.Javadoc.properties.Out {
cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 130b634..b5a0217 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -146,7 +146,7 @@
var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output &&
- unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input &&
+ unzip -qoDD $in 'classes*.dex' -d $tmpDir/dex-input &&
for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do
echo "--input-dex=$${INPUT_DEX}";
echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})";
diff --git a/java/java.go b/java/java.go
index 46ef98b..367b09c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -635,10 +635,11 @@
}
type jniLib struct {
- name string
- path android.Path
- target android.Target
- coverageFile android.OptionalPath
+ name string
+ path android.Path
+ target android.Target
+ coverageFile android.OptionalPath
+ unstrippedFile android.Path
}
func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool {
diff --git a/java/java_test.go b/java/java_test.go
index def42db..db3f187 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -25,7 +25,6 @@
"strings"
"testing"
- "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -86,6 +85,7 @@
RegisterStubsBuildComponents(ctx)
RegisterSdkLibraryBuildComponents(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(android.RegisterComponentsMutator)
RegisterPrebuiltApisBuildComponents(ctx)
@@ -172,20 +172,6 @@
}
}
-func checkModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
- t.Helper()
- module := ctx.ModuleForTests(name, variant).Module()
- deps := []string{}
- ctx.VisitDirectDeps(module, func(m blueprint.Module) {
- deps = append(deps, m.Name())
- })
- sort.Strings(deps)
-
- if actual := deps; !reflect.DeepEqual(expected, actual) {
- t.Errorf("expected %#q, found %#q", expected, actual)
- }
-}
-
func TestJavaLinkType(t *testing.T) {
testJava(t, `
java_library {
@@ -646,7 +632,7 @@
}
}
- checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
`prebuilt_sdklib.stubs`,
`prebuilt_sdklib.stubs.source.test`,
`prebuilt_sdklib.stubs.system`,
@@ -674,7 +660,7 @@
}
`)
- checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
`sdklib.impl`,
@@ -683,12 +669,12 @@
`sdklib.xml`,
})
- checkModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+ CheckModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+ `prebuilt_sdklib.stubs`,
`sdklib.impl`,
// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
// dependency is added after prebuilts may have been renamed and so has to use
// the renamed name.
- `sdklib.stubs`,
`sdklib.xml`,
})
}
@@ -714,17 +700,16 @@
}
`)
- checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
`dex2oatd`,
`prebuilt_sdklib`,
- // This should be sdklib.stubs but is switched to the prebuilt because it is preferred.
- `prebuilt_sdklib.stubs`,
`sdklib.impl`,
+ `sdklib.stubs`,
`sdklib.stubs.source`,
`sdklib.xml`,
})
- checkModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
+ CheckModuleDependencies(t, ctx, "prebuilt_sdklib", "android_common", []string{
`prebuilt_sdklib.stubs`,
`sdklib.impl`,
`sdklib.xml`,
@@ -1112,7 +1097,7 @@
],
proofread_file: "libcore-proofread.txt",
todo_file: "libcore-docs-todo.html",
- args: "-offlinemode -title \"libcore\"",
+ flags: ["-offlinemode -title \"libcore\""],
}
`,
map[string][]byte{
@@ -1139,6 +1124,42 @@
}
}
+func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+ testJavaError(t, "flags is set. Cannot set args", `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
+ droiddoc {
+ name: "bar-doc",
+ srcs: [
+ "bar-doc/a.java",
+ "bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
+ ],
+ exclude_srcs: [
+ "bar-doc/b.java"
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ hdf: [
+ "android.whichdoc offline",
+ ],
+ knowntags: [
+ "bar-doc/known_oj_tags.txt",
+ ],
+ proofread_file: "libcore-proofread.txt",
+ todo_file: "libcore-docs-todo.html",
+ flags: ["-offlinemode -title \"libcore\""],
+ args: "-offlinemode -title \"libcore\"",
+ }
+ `)
+}
+
func TestDroidstubsWithSystemModules(t *testing.T) {
ctx, _ := testJava(t, `
droidstubs {
@@ -1491,7 +1512,7 @@
}
`)
- checkModuleDependencies(t, ctx, "sdklib", "android_common", []string{
+ CheckModuleDependencies(t, ctx, "sdklib", "android_common", []string{
`dex2oatd`,
`sdklib.impl`,
`sdklib.stubs`,
diff --git a/java/lint.go b/java/lint.go
index b73d6a5..20a7dc4 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -67,12 +67,32 @@
kotlinLanguageLevel string
outputs lintOutputs
properties LintProperties
+
+ buildModuleReportZip bool
}
type lintOutputs struct {
html android.ModuleOutPath
text android.ModuleOutPath
xml android.ModuleOutPath
+
+ transitiveHTML *android.DepSet
+ transitiveText *android.DepSet
+ transitiveXML *android.DepSet
+
+ transitiveHTMLZip android.OptionalPath
+ transitiveTextZip android.OptionalPath
+ transitiveXMLZip android.OptionalPath
+}
+
+type lintOutputIntf interface {
+ lintOutputs() *lintOutputs
+}
+
+var _ lintOutputIntf = (*linter)(nil)
+
+func (l *linter) lintOutputs() *lintOutputs {
+ return &l.outputs
}
func (l *linter) enabled() bool {
@@ -213,27 +233,49 @@
projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
- l.outputs.html = android.PathForModuleOut(ctx, "lint-report.html")
- l.outputs.text = android.PathForModuleOut(ctx, "lint-report.txt")
- l.outputs.xml = android.PathForModuleOut(ctx, "lint-report.xml")
+ html := android.PathForModuleOut(ctx, "lint-report.html")
+ text := android.PathForModuleOut(ctx, "lint-report.txt")
+ xml := android.PathForModuleOut(ctx, "lint-report.xml")
+
+ htmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(html)
+ textDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(text)
+ xmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(xml)
+
+ ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+ if depLint, ok := dep.(lintOutputIntf); ok {
+ depLintOutputs := depLint.lintOutputs()
+ htmlDeps.Transitive(depLintOutputs.transitiveHTML)
+ textDeps.Transitive(depLintOutputs.transitiveText)
+ xmlDeps.Transitive(depLintOutputs.transitiveXML)
+ }
+ })
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
+ var annotationsZipPath, apiVersionsXMLPath android.Path
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
+ apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
+ } else {
+ annotationsZipPath = copiedAnnotationsZipPath(ctx)
+ apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
+ }
+
rule.Command().
Text("(").
Flag("JAVA_OPTS=-Xmx2048m").
FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
- FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath(ctx)).
- FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXmlPath(ctx)).
+ FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
+ FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath).
Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")).
Flag("--quiet").
FlagWithInput("--project ", projectXML).
FlagWithInput("--config ", lintXML).
- FlagWithOutput("--html ", l.outputs.html).
- FlagWithOutput("--text ", l.outputs.text).
- FlagWithOutput("--xml ", l.outputs.xml).
+ FlagWithOutput("--html ", html).
+ FlagWithOutput("--text ", text).
+ FlagWithOutput("--xml ", xml).
FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
FlagWithArg("--java-language-level ", l.javaLanguageLevel).
FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
@@ -241,23 +283,37 @@
Flag("--exitcode").
Flags(l.properties.Lint.Flags).
Implicits(deps).
- Text("|| (").Text("cat").Input(l.outputs.text).Text("; exit 7)").
+ Text("|| (").Text("cat").Input(text).Text("; exit 7)").
Text(")")
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
rule.Build(pctx, ctx, "lint", "lint")
-}
-func (l *linter) lintOutputs() *lintOutputs {
- return &l.outputs
-}
+ l.outputs = lintOutputs{
+ html: html,
+ text: text,
+ xml: xml,
-type lintOutputIntf interface {
- lintOutputs() *lintOutputs
-}
+ transitiveHTML: htmlDeps.Build(),
+ transitiveText: textDeps.Build(),
+ transitiveXML: xmlDeps.Build(),
+ }
-var _ lintOutputIntf = (*linter)(nil)
+ if l.buildModuleReportZip {
+ htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
+ l.outputs.transitiveHTMLZip = android.OptionalPathForPath(htmlZip)
+ lintZip(ctx, l.outputs.transitiveHTML.ToSortedList(), htmlZip)
+
+ textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
+ l.outputs.transitiveTextZip = android.OptionalPathForPath(textZip)
+ lintZip(ctx, l.outputs.transitiveText.ToSortedList(), textZip)
+
+ xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
+ l.outputs.transitiveXMLZip = android.OptionalPathForPath(xmlZip)
+ lintZip(ctx, l.outputs.transitiveXML.ToSortedList(), xmlZip)
+ }
+}
type lintSingleton struct {
htmlZip android.WritablePath
@@ -271,7 +327,7 @@
}
func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
- if ctx.Config().UnbundledBuild() {
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
return
}
@@ -297,25 +353,29 @@
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
- Output: annotationsZipPath(ctx),
+ Output: copiedAnnotationsZipPath(ctx),
})
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
- Output: apiVersionsXmlPath(ctx),
+ Output: copiedAPIVersionsXmlPath(ctx),
})
}
-func annotationsZipPath(ctx android.PathContext) android.WritablePath {
+func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
return android.PathForOutput(ctx, "lint", "annotations.zip")
}
-func apiVersionsXmlPath(ctx android.PathContext) android.WritablePath {
+func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
return android.PathForOutput(ctx, "lint", "api_versions.xml")
}
func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
+ if ctx.Config().UnbundledBuild() {
+ return
+ }
+
var outputs []*lintOutputs
var dirs []string
ctx.VisitAllModules(func(m android.Module) {
@@ -343,18 +403,7 @@
paths = append(paths, get(output))
}
- sort.Slice(paths, func(i, j int) bool {
- return paths[i].String() < paths[j].String()
- })
-
- rule := android.NewRuleBuilder()
-
- rule.Command().BuiltTool(ctx, "soong_zip").
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
- FlagWithRspFileInputList("-l ", paths)
-
- rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+ lintZip(ctx, paths, outputPath)
}
l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
@@ -370,7 +419,9 @@
}
func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
- ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+ if !ctx.Config().UnbundledBuild() {
+ ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+ }
}
var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
@@ -379,3 +430,20 @@
android.RegisterSingletonType("lint",
func() android.Singleton { return &lintSingleton{} })
}
+
+func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
+ paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
+
+ sort.Slice(paths, func(i, j int) bool {
+ return paths[i].String() < paths[j].String()
+ })
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().BuiltTool(ctx, "soong_zip").
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
+ FlagWithRspFileInputList("-l ", paths)
+
+ rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 999c72f..b10e6c7 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -83,8 +83,7 @@
}{}
props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
props.Jars = append(props.Jars, path)
- // TODO(hansson): change to scope after migration is done.
- props.Sdk_version = proptools.StringPtr("current")
+ props.Sdk_version = proptools.StringPtr(scope)
props.Installable = proptools.BoolPtr(false)
mctx.CreateModule(ImportFactory, &props)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 8f8f8ce..58e05e5 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -70,6 +70,12 @@
}
}
+var _ android.ReplaceSourceWithPrebuilt = (*scopeDependencyTag)(nil)
+
+func (tag scopeDependencyTag) ReplaceSourceWithPrebuilt() bool {
+ return false
+}
+
// Provides information about an api scope, e.g. public, system, test.
type apiScope struct {
// The name of the api scope, e.g. public, system, test
@@ -137,6 +143,7 @@
droidstubsArgsForGeneratingApi []string
// True if the stubs source and api can be created by the same metalava invocation.
+ // TODO(b/146727827) Now that metalava supports "API hierarchy", do we still need it?
createStubsSourceAndApiTogether bool
// Whether the api scope can be treated as unstable, and should skip compat checks.
@@ -278,6 +285,7 @@
sdkVersion: "module_current",
droidstubsArgs: []string{
"--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+ "--show-for-stub-purposes-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
},
})
apiScopeSystemServer = initApiScope(&apiScope{
@@ -973,7 +981,8 @@
var implLibraryTag = sdkLibraryComponentTag{name: "impl-library"}
-func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+// Add the dependencies on the child modules in the component deps mutator.
+func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
for _, apiScope := range module.getGeneratedApiScopes(ctx) {
// Add dependencies to the stubs library
ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
@@ -998,7 +1007,12 @@
// Add dependency to the rule for generating the xml permissions file
ctx.AddDependency(module, xmlPermissionsFileTag, module.xmlPermissionsModuleName())
}
+ }
+}
+// Add other dependencies as normal.
+func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if module.requiresRuntimeImplementationLibrary() {
// Only add the deps for the library if it is actually going to be built.
module.Library.deps(ctx)
}
@@ -1874,20 +1888,26 @@
props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer())
}
-func (module *SdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) {
+// Add the dependencies on the child module in the component deps mutator so that it
+// creates references to the prebuilt and not the source modules.
+func (module *SdkLibraryImport) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
for apiScope, scopeProperties := range module.scopeProperties {
if len(scopeProperties.Jars) == 0 {
continue
}
// Add dependencies to the prebuilt stubs library
- ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
+ ctx.AddVariationDependencies(nil, apiScope.stubsTag, "prebuilt_"+module.stubsLibraryModuleName(apiScope))
if len(scopeProperties.Stub_srcs) > 0 {
// Add dependencies to the prebuilt stubs source library
- ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope))
+ ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, "prebuilt_"+module.stubsSourceModuleName(apiScope))
}
}
+}
+
+// Add other dependencies as normal.
+func (module *SdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) {
implName := module.implLibraryModuleName()
if ctx.OtherModuleExists(implName) {
diff --git a/java/testing.go b/java/testing.go
index f5688e6..94f054e 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -16,9 +16,13 @@
import (
"fmt"
+ "reflect"
+ "sort"
+ "testing"
"android/soong/android"
"android/soong/cc"
+ "github.com/google/blueprint"
)
func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
@@ -216,3 +220,17 @@
return bp
}
+
+func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+ t.Helper()
+ module := ctx.ModuleForTests(name, variant).Module()
+ deps := []string{}
+ ctx.VisitDirectDeps(module, func(m blueprint.Module) {
+ deps = append(deps, m.Name())
+ })
+ sort.Strings(deps)
+
+ if actual := deps; !reflect.DeepEqual(expected, actual) {
+ t.Errorf("expected %#q, found %#q", expected, actual)
+ }
+}
diff --git a/rust/binary.go b/rust/binary.go
index a1cd410..9fc52cd 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -106,7 +106,8 @@
func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
- srcPath := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
+ srcPath, paths := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
+ deps.SrcDeps = paths
outputFile := android.PathForModuleOut(ctx, fileName)
binary.unstrippedOutputFile = outputFile
diff --git a/rust/builder.go b/rust/builder.go
index 16d7306..7f94bb5 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -166,6 +166,7 @@
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
implicits = append(implicits, deps.StaticLibs...)
implicits = append(implicits, deps.SharedLibs...)
+ implicits = append(implicits, deps.SrcDeps...)
if deps.CrtBegin.Valid() {
implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
}
diff --git a/rust/compiler.go b/rust/compiler.go
index 92a3b07..c20179b 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -253,10 +253,24 @@
return String(compiler.Properties.Relative_install_path)
}
-func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) android.Path {
- srcPaths := android.PathsForModuleSrc(ctx, srcs)
- if len(srcPaths) != 1 {
- ctx.PropertyErrorf("srcs", "srcs can only contain one path for rust modules")
+func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) (android.Path, android.Paths) {
+ // The srcs can contain strings with prefix ":".
+ // They are dependent modules of this module, with android.SourceDepTag.
+ // They are not the main source file compiled by rustc.
+ numSrcs := 0
+ srcIndex := 0
+ for i, s := range srcs {
+ if android.SrcIsModule(s) == "" {
+ numSrcs++
+ srcIndex = i
+ }
}
- return srcPaths[0]
+ if numSrcs != 1 {
+ ctx.PropertyErrorf("srcs", "srcs can only contain one path for a rust file")
+ }
+ if srcIndex != 0 {
+ ctx.PropertyErrorf("srcs", "main source file must be the first in srcs")
+ }
+ paths := android.PathsForModuleSrc(ctx, srcs)
+ return paths[srcIndex], paths
}
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index bcde757..58ca52a 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -43,7 +43,7 @@
// Test that we reject multiple source files.
func TestEnforceSingleSourceFile(t *testing.T) {
- singleSrcError := "srcs can only contain one path for rust modules"
+ singleSrcError := "srcs can only contain one path for a rust file"
// Test libraries
testRustError(t, singleSrcError, `
diff --git a/rust/config/global.go b/rust/config/global.go
index e1b1775..2020f46 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -54,6 +54,9 @@
"-Wl,--pack-dyn-relocs=android+relr",
"-Wl,--no-undefined",
"-Wl,--hash-style=gnu",
+
+ "-B${ccConfig.ClangBin}",
+ "-fuse-ld=lld",
}
)
@@ -80,7 +83,7 @@
pctx.ImportAs("ccConfig", "android/soong/cc/config")
pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
- pctx.StaticVariable("RustLinkerArgs", "-B ${ccConfig.ClangBin} -fuse-ld=lld")
+ pctx.StaticVariable("RustLinkerArgs", "")
pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 4c16693..4104400 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -21,8 +21,10 @@
)
var (
- DarwinRustFlags = []string{}
- DarwinRustLinkFlags = []string{}
+ DarwinRustFlags = []string{}
+ DarwinRustLinkFlags = []string{
+ "-B${ccConfig.MacToolPath}",
+ }
darwinX8664Rustflags = []string{}
darwinX8664Linkflags = []string{}
)
@@ -66,6 +68,10 @@
return ".dylib"
}
+func (t *toolchainDarwin) DylibSuffix() string {
+ return ".rustlib.dylib"
+}
+
func (t *toolchainDarwin) ProcMacroSuffix() string {
return ".dylib"
}
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index 5376e5b..acc99e1 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -21,8 +21,11 @@
)
var (
- LinuxRustFlags = []string{}
- LinuxRustLinkFlags = []string{}
+ LinuxRustFlags = []string{}
+ LinuxRustLinkFlags = []string{
+ "-B${ccConfig.ClangBin}",
+ "-fuse-ld=lld",
+ }
linuxX86Rustflags = []string{}
linuxX86Linkflags = []string{}
linuxX8664Rustflags = []string{}
diff --git a/rust/library.go b/rust/library.go
index 8b8e797..d718eb8 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -368,7 +368,8 @@
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
var outputFile android.WritablePath
- srcPath := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+ srcPath, paths := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+ deps.SrcDeps = paths
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 67d649d..3b4f40a 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -95,7 +95,8 @@
func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
prebuilt.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
- srcPath := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
+ srcPath, paths := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
+ deps.SrcDeps = paths
prebuilt.unstrippedOutputFile = srcPath
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 2719161..49dbd8d 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -65,7 +65,8 @@
fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix()
outputFile := android.PathForModuleOut(ctx, fileName)
- srcPath := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
+ srcPath, paths := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
+ deps.SrcDeps = paths
procMacro.unstrippedOutputFile = outputFile
diff --git a/rust/rust.go b/rust/rust.go
index 72301a7..7a98c64 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -239,6 +239,9 @@
CrtBegin android.OptionalPath
CrtEnd android.OptionalPath
+
+ // Paths to generated source files
+ SrcDeps android.Paths
}
type RustLibraries []RustLibrary
@@ -843,6 +846,7 @@
// Dedup exported flags from dependencies
depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
+ depPaths.SrcDeps = android.FirstUniquePaths(depPaths.SrcDeps)
return depPaths
}
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 08bc8ca..e803925 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -61,6 +61,7 @@
"foo.rs": nil,
"foo.c": nil,
"src/bar.rs": nil,
+ "src/any.h": nil,
"liby.so": nil,
"libz.so": nil,
}
@@ -181,7 +182,7 @@
}
rust_library_host_rlib {
name: "librlib",
- srcs: ["foo.rs"],
+ srcs: ["foo.rs", ":my_generator"],
crate_name: "rlib",
}
rust_proc_macro {
@@ -189,17 +190,38 @@
srcs: ["foo.rs"],
crate_name: "pm",
}
+ genrule {
+ name: "my_generator",
+ tools: ["any_rust_binary"],
+ cmd: "$(location) -o $(out) $(in)",
+ srcs: ["src/any.h"],
+ out: ["src/any.rs"],
+ }
rust_binary_host {
- name: "fizz-buzz",
+ name: "fizz-buzz-dep",
dylibs: ["libdylib"],
rlibs: ["librlib"],
proc_macros: ["libpm"],
static_libs: ["libstatic"],
shared_libs: ["libshared"],
- srcs: ["foo.rs"],
+ srcs: [
+ "foo.rs",
+ ":my_generator",
+ ],
}
`)
- module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+ module := ctx.ModuleForTests("fizz-buzz-dep", "linux_glibc_x86_64").Module().(*Module)
+ rlibmodule := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib").Module().(*Module)
+
+ srcs := module.compiler.(*binaryDecorator).baseCompiler.Properties.Srcs
+ if len(srcs) != 2 || !android.InList(":my_generator", srcs) {
+ t.Errorf("missing module dependency in fizz-buzz)")
+ }
+
+ srcs = rlibmodule.compiler.(*libraryDecorator).baseCompiler.Properties.Srcs
+ if len(srcs) != 2 || !android.InList(":my_generator", srcs) {
+ t.Errorf("missing module dependency in rlib")
+ }
// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
diff --git a/rust/testing.go b/rust/testing.go
index 3d583e1..430b40b 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"android/soong/cc"
+ "android/soong/genrule"
)
func GatherRequiredDepsForTest() string {
@@ -77,6 +78,7 @@
func CreateTestContext() *android.TestContext {
ctx := android.NewTestArchContext()
cc.RegisterRequiredBuildComponentsForTest(ctx)
+ ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
ctx.RegisterModuleType("rust_test", RustTestFactory)
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 123fe70..497f14b 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -21,20 +21,20 @@
"android/soong/cc"
)
+var ccTestFs = map[string][]byte{
+ "Test.cpp": nil,
+ "include/Test.h": nil,
+ "include-android/AndroidTest.h": nil,
+ "include-host/HostTest.h": nil,
+ "arm64/include/Arm64Test.h": nil,
+ "libfoo.so": nil,
+ "aidl/foo/bar/Test.aidl": nil,
+ "some/where/stubslib.map.txt": nil,
+}
+
func testSdkWithCc(t *testing.T, bp string) *testSdkResult {
t.Helper()
-
- fs := map[string][]byte{
- "Test.cpp": nil,
- "include/Test.h": nil,
- "include-android/AndroidTest.h": nil,
- "include-host/HostTest.h": nil,
- "arm64/include/Arm64Test.h": nil,
- "libfoo.so": nil,
- "aidl/foo/bar/Test.aidl": nil,
- "some/where/stubslib.map.txt": nil,
- }
- return testSdkWithFs(t, bp, fs)
+ return testSdkWithFs(t, bp, ccTestFs)
}
// Contains tests for SDK members provided by the cc package.
@@ -69,6 +69,76 @@
ensureListContains(t, inputs, arm64Output.String())
}
+func TestSdkCompileMultilibOverride(t *testing.T) {
+ result := testSdkWithCc(t, `
+ sdk {
+ name: "mysdk",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["sdkmember"],
+ compile_multilib: "64",
+ }
+
+ cc_library_shared {
+ name: "sdkmember",
+ device_supported: false,
+ host_supported: true,
+ srcs: ["Test.cpp"],
+ stl: "none",
+ compile_multilib: "64",
+ }
+ `)
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+ name: "mysdk_sdkmember@current",
+ sdk_member_name: "sdkmember",
+ device_supported: false,
+ host_supported: true,
+ installable: false,
+ stl: "none",
+ compile_multilib: "64",
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/sdkmember.so"],
+ },
+ },
+}
+
+cc_prebuilt_library_shared {
+ name: "sdkmember",
+ prefer: false,
+ device_supported: false,
+ host_supported: true,
+ stl: "none",
+ compile_multilib: "64",
+ arch: {
+ x86_64: {
+ srcs: ["x86_64/lib/sdkmember.so"],
+ },
+ },
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ device_supported: false,
+ host_supported: true,
+ native_shared_libs: ["mysdk_sdkmember@current"],
+ target: {
+ linux_glibc: {
+ compile_multilib: "64",
+ },
+ },
+}
+`),
+ checkAllCopyRules(`
+.intermediates/sdkmember/linux_glibc_x86_64_shared/sdkmember.so -> x86_64/lib/sdkmember.so
+`))
+}
+
func TestBasicSdkWithCc(t *testing.T) {
result := testSdkWithCc(t, `
sdk {
@@ -79,6 +149,8 @@
cc_library_shared {
name: "sdkmember",
system_shared_libs: [],
+ stl: "none",
+ apex_available: ["mysdkapex"],
}
sdk_snapshot {
@@ -152,6 +224,13 @@
key: "myapex.key",
certificate: ":myapex.cert",
}
+
+ apex {
+ name: "mysdkapex",
+ native_shared_libs: ["sdkmember"],
+ key: "myapex.key",
+ certificate: ":myapex.cert",
+ }
`)
sdkMemberV1 := result.ModuleForTests("sdkmember_mysdk_1", "android_arm64_armv8-a_shared_myapex").Rule("toc").Output
@@ -240,6 +319,7 @@
name: "mysdk_crtobj@current",
sdk_member_name: "crtobj",
stl: "none",
+ compile_multilib: "both",
arch: {
arm64: {
srcs: ["arm64/lib/crtobj.o"],
@@ -254,6 +334,7 @@
name: "crtobj",
prefer: false,
stl: "none",
+ compile_multilib: "both",
arch: {
arm64: {
srcs: ["arm64/lib/crtobj.o"],
@@ -347,6 +428,7 @@
sdk_member_name: "mynativelib",
installable: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -363,6 +445,7 @@
name: "mynativelib",
prefer: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -450,9 +533,6 @@
}
func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "myexports",
@@ -561,9 +641,6 @@
// Test that we support the necessary flags for the linker binary, which is
// special in several ways.
func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "mymodule_exports",
@@ -596,9 +673,9 @@
host_supported: true,
installable: false,
stl: "none",
+ compile_multilib: "both",
static_executable: true,
nocrt: true,
- compile_multilib: "both",
arch: {
x86_64: {
srcs: ["x86_64/bin/linker"],
@@ -615,9 +692,9 @@
device_supported: false,
host_supported: true,
stl: "none",
+ compile_multilib: "both",
static_executable: true,
nocrt: true,
- compile_multilib: "both",
arch: {
x86_64: {
srcs: ["x86_64/bin/linker"],
@@ -677,6 +754,7 @@
],
installable: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -698,6 +776,7 @@
"apex2",
],
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -799,6 +878,7 @@
sdk_member_name: "mynativelib",
installable: false,
stl: "none",
+ compile_multilib: "both",
shared_libs: [
"mysdk_myothernativelib@current",
"libc",
@@ -817,6 +897,7 @@
name: "mynativelib",
prefer: false,
stl: "none",
+ compile_multilib: "both",
shared_libs: [
"myothernativelib",
"libc",
@@ -836,6 +917,7 @@
sdk_member_name: "myothernativelib",
installable: false,
stl: "none",
+ compile_multilib: "both",
system_shared_libs: ["libm"],
arch: {
arm64: {
@@ -851,6 +933,7 @@
name: "myothernativelib",
prefer: false,
stl: "none",
+ compile_multilib: "both",
system_shared_libs: ["libm"],
arch: {
arm64: {
@@ -867,6 +950,7 @@
sdk_member_name: "mysystemnativelib",
installable: false,
stl: "none",
+ compile_multilib: "both",
arch: {
arm64: {
srcs: ["arm64/lib/mysystemnativelib.so"],
@@ -881,6 +965,7 @@
name: "mysystemnativelib",
prefer: false,
stl: "none",
+ compile_multilib: "both",
arch: {
arm64: {
srcs: ["arm64/lib/mysystemnativelib.so"],
@@ -912,9 +997,6 @@
}
func TestHostSnapshotWithCcSharedLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -952,6 +1034,7 @@
installable: false,
sdk_version: "minimum",
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
x86_64: {
@@ -972,6 +1055,7 @@
host_supported: true,
sdk_version: "minimum",
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
x86_64: {
@@ -1007,9 +1091,6 @@
}
func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1051,12 +1132,18 @@
installable: false,
stl: "none",
target: {
+ linux_glibc: {
+ compile_multilib: "both",
+ },
linux_glibc_x86_64: {
srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
},
linux_glibc_x86: {
srcs: ["linux_glibc/x86/lib/mynativelib.so"],
},
+ windows: {
+ compile_multilib: "64",
+ },
windows_x86_64: {
srcs: ["windows/x86_64/lib/mynativelib.dll"],
},
@@ -1070,12 +1157,18 @@
host_supported: true,
stl: "none",
target: {
+ linux_glibc: {
+ compile_multilib: "both",
+ },
linux_glibc_x86_64: {
srcs: ["linux_glibc/x86_64/lib/mynativelib.so"],
},
linux_glibc_x86: {
srcs: ["linux_glibc/x86/lib/mynativelib.so"],
},
+ windows: {
+ compile_multilib: "64",
+ },
windows_x86_64: {
srcs: ["windows/x86_64/lib/mynativelib.dll"],
},
@@ -1132,6 +1225,7 @@
sdk_member_name: "mynativelib",
installable: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -1149,6 +1243,7 @@
name: "mynativelib",
prefer: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -1182,9 +1277,6 @@
}
func TestHostSnapshotWithCcStaticLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "myexports",
@@ -1220,6 +1312,7 @@
host_supported: true,
installable: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
x86_64: {
@@ -1239,6 +1332,7 @@
device_supported: false,
host_supported: true,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
x86_64: {
@@ -1299,6 +1393,7 @@
sdk_member_name: "mynativelib",
installable: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -1324,6 +1419,7 @@
name: "mynativelib",
prefer: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
arm64: {
@@ -1360,9 +1456,6 @@
}
func TestHostSnapshotWithMultiLib64(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "myexports",
@@ -1403,6 +1496,7 @@
host_supported: true,
installable: false,
stl: "none",
+ compile_multilib: "64",
export_include_dirs: ["include/include"],
arch: {
x86_64: {
@@ -1418,6 +1512,7 @@
device_supported: false,
host_supported: true,
stl: "none",
+ compile_multilib: "64",
export_include_dirs: ["include/include"],
arch: {
x86_64: {
@@ -1470,6 +1565,7 @@
name: "mysdk_mynativeheaders@current",
sdk_member_name: "mynativeheaders",
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
}
@@ -1477,6 +1573,7 @@
name: "mynativeheaders",
prefer: false,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
}
@@ -1492,9 +1589,6 @@
}
func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1522,6 +1616,7 @@
device_supported: false,
host_supported: true,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
}
@@ -1531,6 +1626,7 @@
device_supported: false,
host_supported: true,
stl: "none",
+ compile_multilib: "both",
export_include_dirs: ["include/include"],
}
@@ -1548,9 +1644,6 @@
}
func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1583,13 +1676,14 @@
sdk_member_name: "mynativeheaders",
host_supported: true,
stl: "none",
- export_system_include_dirs: ["include/include"],
+ compile_multilib: "both",
+ export_system_include_dirs: ["common_os/include/include"],
target: {
android: {
- export_include_dirs: ["include/include-android"],
+ export_include_dirs: ["android/include/include-android"],
},
linux_glibc: {
- export_include_dirs: ["include/include-host"],
+ export_include_dirs: ["linux_glibc/include/include-host"],
},
},
}
@@ -1599,13 +1693,14 @@
prefer: false,
host_supported: true,
stl: "none",
- export_system_include_dirs: ["include/include"],
+ compile_multilib: "both",
+ export_system_include_dirs: ["common_os/include/include"],
target: {
android: {
- export_include_dirs: ["include/include-android"],
+ export_include_dirs: ["android/include/include-android"],
},
linux_glibc: {
- export_include_dirs: ["include/include-host"],
+ export_include_dirs: ["linux_glibc/include/include-host"],
},
},
}
@@ -1617,17 +1712,14 @@
}
`),
checkAllCopyRules(`
-include/Test.h -> include/include/Test.h
-include-android/AndroidTest.h -> include/include-android/AndroidTest.h
-include-host/HostTest.h -> include/include-host/HostTest.h
+include/Test.h -> common_os/include/include/Test.h
+include-android/AndroidTest.h -> android/include/include-android/AndroidTest.h
+include-host/HostTest.h -> linux_glibc/include/include-host/HostTest.h
`),
)
}
func TestSystemSharedLibPropagation(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1658,6 +1750,7 @@
name: "mysdk_sslnil@current",
sdk_member_name: "sslnil",
installable: false,
+ compile_multilib: "both",
arch: {
arm64: {
srcs: ["arm64/lib/sslnil.so"],
@@ -1671,6 +1764,7 @@
cc_prebuilt_library_shared {
name: "sslnil",
prefer: false,
+ compile_multilib: "both",
arch: {
arm64: {
srcs: ["arm64/lib/sslnil.so"],
@@ -1685,6 +1779,7 @@
name: "mysdk_sslempty@current",
sdk_member_name: "sslempty",
installable: false,
+ compile_multilib: "both",
system_shared_libs: [],
arch: {
arm64: {
@@ -1699,6 +1794,7 @@
cc_prebuilt_library_shared {
name: "sslempty",
prefer: false,
+ compile_multilib: "both",
system_shared_libs: [],
arch: {
arm64: {
@@ -1714,6 +1810,7 @@
name: "mysdk_sslnonempty@current",
sdk_member_name: "sslnonempty",
installable: false,
+ compile_multilib: "both",
system_shared_libs: ["mysdk_sslnil@current"],
arch: {
arm64: {
@@ -1728,6 +1825,7 @@
cc_prebuilt_library_shared {
name: "sslnonempty",
prefer: false,
+ compile_multilib: "both",
system_shared_libs: ["sslnil"],
arch: {
arm64: {
@@ -1776,6 +1874,7 @@
sdk_member_name: "sslvariants",
host_supported: true,
installable: false,
+ compile_multilib: "both",
target: {
android: {
system_shared_libs: [],
@@ -1799,6 +1898,7 @@
name: "sslvariants",
prefer: false,
host_supported: true,
+ compile_multilib: "both",
target: {
android: {
system_shared_libs: [],
@@ -1855,6 +1955,7 @@
name: "mysdk_stubslib@current",
sdk_member_name: "stubslib",
installable: false,
+ compile_multilib: "both",
stubs: {
versions: ["3"],
},
@@ -1871,6 +1972,7 @@
cc_prebuilt_library_shared {
name: "stubslib",
prefer: false,
+ compile_multilib: "both",
stubs: {
versions: ["3"],
},
@@ -1892,9 +1994,6 @@
}
func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1927,6 +2026,7 @@
sdk_member_name: "stubslib",
host_supported: true,
installable: false,
+ compile_multilib: "both",
stubs: {
versions: ["3"],
},
@@ -1950,6 +2050,7 @@
name: "stubslib",
prefer: false,
host_supported: true,
+ compile_multilib: "both",
stubs: {
versions: ["3"],
},
@@ -1978,9 +2079,6 @@
}
func TestUniqueHostSoname(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -2005,6 +2103,7 @@
host_supported: true,
installable: false,
unique_host_soname: true,
+ compile_multilib: "both",
target: {
android_arm64: {
srcs: ["android/arm64/lib/mylib.so"],
@@ -2026,6 +2125,7 @@
prefer: false,
host_supported: true,
unique_host_soname: true,
+ compile_multilib: "both",
target: {
android_arm64: {
srcs: ["android/arm64/lib/mylib.so"],
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 7496b20..aee04a1 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -16,6 +16,8 @@
import (
"testing"
+
+ "android/soong/java"
)
func testSdkWithJava(t *testing.T, bp string) *testSdkResult {
@@ -26,6 +28,9 @@
"resource.test": nil,
"aidl/foo/bar/Test.aidl": nil,
+ // For java_import
+ "prebuilt.jar": nil,
+
// For java_sdk_library
"api/current.txt": nil,
"api/removed.txt": nil,
@@ -85,6 +90,52 @@
// Contains tests for SDK members provided by the java package.
+func TestSdkDependsOnSourceEvenWhenPrebuiltPreferred(t *testing.T) {
+ result := testSdkWithJava(t, `
+ sdk {
+ name: "mysdk",
+ java_header_libs: ["sdkmember"],
+ }
+
+ java_library {
+ name: "sdkmember",
+ srcs: ["Test.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ }
+
+ java_import {
+ name: "sdkmember",
+ prefer: true,
+ jars: ["prebuilt.jar"],
+ }
+ `)
+
+ // Make sure that the mysdk module depends on "sdkmember" and not "prebuilt_sdkmember".
+ java.CheckModuleDependencies(t, result.ctx, "mysdk", "android_common", []string{"sdkmember"})
+
+ result.CheckSnapshot("mysdk", "",
+ checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "mysdk_sdkmember@current",
+ sdk_member_name: "sdkmember",
+ jars: ["java/sdkmember.jar"],
+}
+
+java_import {
+ name: "sdkmember",
+ prefer: false,
+ jars: ["java/sdkmember.jar"],
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ java_header_libs: ["mysdk_sdkmember@current"],
+}
+`))
+}
+
func TestBasicSdkWithJavaLibrary(t *testing.T) {
result := testSdkWithJava(t, `
sdk {
@@ -214,9 +265,6 @@
}
func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
sdk {
name: "mysdk",
@@ -274,9 +322,6 @@
}
func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
sdk {
name: "mysdk",
@@ -390,9 +435,6 @@
}
func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
module_exports {
name: "myexports",
@@ -497,9 +539,6 @@
}
func TestHostSnapshotWithJavaTest(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
module_exports {
name: "myexports",
@@ -641,9 +680,6 @@
}
func TestHostSnapshotWithDroidstubs(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithDroidstubs(t, `
module_exports {
name: "myexports",
@@ -784,9 +820,6 @@
}
func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
sdk {
name: "mysdk",
@@ -862,9 +895,6 @@
}
func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
module_exports {
name: "myexports",
diff --git a/sdk/sdk.go b/sdk/sdk.go
index b9b8199..3e76008 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -218,7 +218,7 @@
Compile_multilib *string
}
p := &props{Compile_multilib: proptools.StringPtr("both")}
- ctx.AppendProperties(p)
+ ctx.PrependProperties(p)
})
return s
}
@@ -330,6 +330,11 @@
blueprint.BaseDependencyTag
}
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t dependencyTag) ExcludeFromApexContents() {}
+
+var _ android.ExcludeFromApexContentsTag = dependencyTag{}
+
// For dependencies from an in-development version of an SDK member to frozen versions of the same member
// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
type sdkMemberVersionedDepTag struct {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 56be741..ef62b79 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -15,6 +15,9 @@
package sdk
import (
+ "android/soong/android"
+ "log"
+ "os"
"testing"
"github.com/google/blueprint/proptools"
@@ -22,6 +25,12 @@
// Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
func TestMain(m *testing.M) {
+ if android.BuildOs != android.Linux {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ log.Printf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
+ os.Exit(0)
+ }
+
runTestWithBuildDir(m)
}
diff --git a/sdk/testing.go b/sdk/testing.go
index 4361754..34ea8f0 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -29,7 +29,9 @@
"android/soong/java"
)
-func testSdkContext(bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
+func testSdkContext(bp string, fs map[string][]byte, extraOsTypes []android.OsType) (*android.TestContext, android.Config) {
+ extraOsTypes = append(extraOsTypes, android.Android, android.Windows)
+
bp = bp + `
apex_key {
name: "myapex.key",
@@ -41,17 +43,18 @@
name: "myapex.cert",
certificate: "myapex",
}
- ` + cc.GatherRequiredDepsForTest(android.Android, android.Windows)
+ ` + cc.GatherRequiredDepsForTest(extraOsTypes...)
mockFS := map[string][]byte{
- "build/make/target/product/security": nil,
- "apex_manifest.json": nil,
- "system/sepolicy/apex/myapex-file_contexts": nil,
- "system/sepolicy/apex/myapex2-file_contexts": nil,
- "myapex.avbpubkey": nil,
- "myapex.pem": nil,
- "myapex.x509.pem": nil,
- "myapex.pk8": nil,
+ "build/make/target/product/security": nil,
+ "apex_manifest.json": nil,
+ "system/sepolicy/apex/myapex-file_contexts": nil,
+ "system/sepolicy/apex/myapex2-file_contexts": nil,
+ "system/sepolicy/apex/mysdkapex-file_contexts": nil,
+ "myapex.avbpubkey": nil,
+ "myapex.pem": nil,
+ "myapex.x509.pem": nil,
+ "myapex.pk8": nil,
}
cc.GatherRequiredFilesForTest(mockFS)
@@ -68,6 +71,15 @@
{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
}
+ for _, extraOsType := range extraOsTypes {
+ switch extraOsType {
+ case android.LinuxBionic:
+ config.Targets[android.LinuxBionic] = []android.Target{
+ {android.LinuxBionic, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", ""},
+ }
+ }
+ }
+
ctx := android.NewTestArchContext()
// Enable androidmk support.
@@ -84,6 +96,7 @@
android.RegisterPackageBuildComponents(ctx)
ctx.PreArchMutators(android.RegisterVisibilityRuleChecker)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(android.RegisterComponentsMutator)
ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer)
ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
@@ -115,9 +128,8 @@
return ctx, config
}
-func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
+func runTests(t *testing.T, ctx *android.TestContext, config android.Config) *testSdkResult {
t.Helper()
- ctx, config := testSdkContext(bp, fs)
_, errs := ctx.ParseBlueprintsFiles(".")
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
@@ -129,9 +141,15 @@
}
}
+func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
+ t.Helper()
+ ctx, config := testSdkContext(bp, fs, nil)
+ return runTests(t, ctx, config)
+}
+
func testSdkError(t *testing.T, pattern, bp string) {
t.Helper()
- ctx, config := testSdkContext(bp, nil)
+ ctx, config := testSdkContext(bp, nil, nil)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
if len(errs) > 0 {
android.FailIfNoMatchingErrors(t, pattern, errs)
@@ -420,10 +438,3 @@
os.Exit(run())
}
-
-func SkipIfNotLinux(t *testing.T) {
- t.Helper()
- if android.BuildOs != android.Linux {
- t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
- }
-}
diff --git a/sdk/update.go b/sdk/update.go
index 8241151..b8d73c6 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -794,6 +794,17 @@
return !ok
}
+// Add the properties from the given SdkMemberProperties to the blueprint
+// property set. This handles common properties in SdkMemberPropertiesBase and
+// calls the member-specific AddToPropertySet for the rest.
+func addSdkMemberPropertiesToSet(ctx *memberContext, memberProperties android.SdkMemberProperties, targetPropertySet android.BpPropertySet) {
+ if memberProperties.Base().Compile_multilib != "" {
+ targetPropertySet.AddProperty("compile_multilib", memberProperties.Base().Compile_multilib)
+ }
+
+ memberProperties.AddToPropertySet(ctx, targetPropertySet)
+}
+
type sdkMemberRef struct {
memberType android.SdkMemberType
variant android.SdkAware
@@ -911,7 +922,7 @@
if commonVariants, ok := variantsByArchName["common"]; ok {
if len(osTypeVariants) != 1 {
- panic("Expected to only have 1 variant when arch type is common but found " + string(len(osTypeVariants)))
+ panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants)))
}
// A common arch type only has one variant and its properties should be treated
@@ -1009,7 +1020,7 @@
}
// Add the os specific but arch independent properties to the module.
- osInfo.Properties.AddToPropertySet(ctx, osPropertySet)
+ addSdkMemberPropertiesToSet(ctx, osInfo.Properties, osPropertySet)
// Add arch (and possibly os) specific sections for each set of arch (and possibly
// os) specific properties.
@@ -1111,11 +1122,11 @@
func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archPropertySet android.BpPropertySet, archOsPrefix string) {
archTypeName := archInfo.archType.Name
archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + archTypeName)
- archInfo.Properties.AddToPropertySet(ctx, archTypePropertySet)
+ addSdkMemberPropertiesToSet(ctx, archInfo.Properties, archTypePropertySet)
for _, linkInfo := range archInfo.linkInfos {
linkPropertySet := archTypePropertySet.AddPropertySet(linkInfo.linkType)
- linkInfo.Properties.AddToPropertySet(ctx, linkPropertySet)
+ addSdkMemberPropertiesToSet(ctx, linkInfo.Properties, linkPropertySet)
}
}
@@ -1221,7 +1232,7 @@
extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
// Add the common properties to the module.
- commonProperties.AddToPropertySet(ctx, bpModule)
+ addSdkMemberPropertiesToSet(ctx, commonProperties, bpModule)
// Create a target property set into which target specific properties can be
// added.
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 0a0bb16..4ef2721 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -63,6 +63,7 @@
"cleanbuild_test.go",
"config_test.go",
"environment_test.go",
+ "rbe_test.go",
"upload_test.go",
"util_test.go",
"proc_sync_test.go",
diff --git a/ui/build/config.go b/ui/build/config.go
index c4bbad7..ba477e6 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -184,7 +184,8 @@
// Tell python not to spam the source tree with .pyc files.
ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
- ret.environ.Set("TMPDIR", absPath(ctx, ret.TempDir()))
+ tmpDir := absPath(ctx, ret.TempDir())
+ ret.environ.Set("TMPDIR", tmpDir)
// Always set ASAN_SYMBOLIZER_PATH so that ASAN-based tools can symbolize any crashes
symbolizerPath := filepath.Join("prebuilts/clang/host", ret.HostPrebuiltTag(),
@@ -256,11 +257,14 @@
ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10)
}
- if ctx.Metrics != nil {
- ctx.Metrics.SetBuildDateTime(ret.buildDateTime)
- }
ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
+ if ret.UseRBE() {
+ for k, v := range getRBEVars(ctx, tmpDir) {
+ ret.environ.Set(k, v)
+ }
+ }
+
return Config{ret}
}
@@ -804,6 +808,15 @@
return true
}
+func (c *configImpl) RBEStatsOutputDir() string {
+ for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
+ if v, ok := c.environ.Get(f); ok {
+ return v
+ }
+ }
+ return ""
+}
+
func (c *configImpl) UseRemoteBuild() bool {
return c.UseGoma() || c.UseRBE()
}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index ceea4bf..fd3b7ab 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -15,14 +15,47 @@
package build
import (
+ "fmt"
+ "math/rand"
+ "os"
"path/filepath"
+ "time"
"android/soong/ui/metrics"
)
-const bootstrapCmd = "bootstrap"
-const rbeLeastNProcs = 2500
-const rbeLeastNFiles = 16000
+const (
+ rbeLeastNProcs = 2500
+ rbeLeastNFiles = 16000
+
+ // prebuilt RBE binaries
+ bootstrapCmd = "bootstrap"
+
+ // RBE metrics proto buffer file
+ rbeMetricsPBFilename = "rbe_metrics.pb"
+)
+
+func rbeCommand(ctx Context, config Config, rbeCmd string) string {
+ var cmdPath string
+ if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
+ cmdPath = filepath.Join(rbeDir, rbeCmd)
+ } else if home, ok := config.Environment().Get("HOME"); ok {
+ cmdPath = filepath.Join(home, "rbe", rbeCmd)
+ } else {
+ ctx.Fatalf("rbe command path not found")
+ }
+
+ if _, err := os.Stat(cmdPath); err != nil && os.IsNotExist(err) {
+ ctx.Fatalf("rbe command %q not found", rbeCmd)
+ }
+
+ return cmdPath
+}
+
+func getRBEVars(ctx Context, tmpDir string) map[string]string {
+ rand.Seed(time.Now().UnixNano())
+ return map[string]string{"RBE_server_address": fmt.Sprintf("unix://%v/reproxy_%v.sock", tmpDir, rand.Intn(1000))}
+}
func startRBE(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
@@ -35,18 +68,50 @@
ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
}
- var rbeBootstrap string
- if rbeDir, ok := config.Environment().Get("RBE_DIR"); ok {
- rbeBootstrap = filepath.Join(rbeDir, bootstrapCmd)
- } else if home, ok := config.Environment().Get("HOME"); ok {
- rbeBootstrap = filepath.Join(home, "rbe", bootstrapCmd)
- } else {
- ctx.Fatalln("rbe bootstrap not found")
- }
-
- cmd := Command(ctx, config, "boostrap", rbeBootstrap)
+ cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
if output, err := cmd.CombinedOutput(); err != nil {
ctx.Fatalf("rbe bootstrap failed with: %v\n%s\n", err, output)
}
}
+
+func stopRBE(ctx Context, config Config) {
+ cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
+ if output, err := cmd.CombinedOutput(); err != nil {
+ ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
+ }
+}
+
+// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
+// The protobuf file is created if RBE is enabled and the proxy service has
+// started. The proxy service is shutdown in order to dump the RBE metrics to the
+// protobuf file.
+func DumpRBEMetrics(ctx Context, config Config, filename string) {
+ ctx.BeginTrace(metrics.RunShutdownTool, "dump_rbe_metrics")
+ defer ctx.EndTrace()
+
+ // Remove the previous metrics file in case there is a failure or RBE has been
+ // disable for this run.
+ os.Remove(filename)
+
+ // If RBE is not enabled then there are no metrics to generate.
+ // If RBE does not require to start, the RBE proxy maybe started
+ // manually for debugging purpose and can generate the metrics
+ // afterwards.
+ if !config.StartRBE() {
+ return
+ }
+
+ outputDir := config.RBEStatsOutputDir()
+ if outputDir == "" {
+ ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
+ }
+ metricsFile := filepath.Join(outputDir, rbeMetricsPBFilename)
+
+ // Stop the proxy first in order to generate the RBE metrics protobuf file.
+ stopRBE(ctx, config)
+
+ if _, err := copyFile(metricsFile, filename); err != nil {
+ ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
+ }
+}
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
new file mode 100644
index 0000000..2c4995b
--- /dev/null
+++ b/ui/build/rbe_test.go
@@ -0,0 +1,142 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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 build
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "android/soong/ui/logger"
+)
+
+func TestDumpRBEMetrics(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ description string
+ env []string
+ generated bool
+ }{{
+ description: "RBE disabled",
+ env: []string{
+ "NOSTART_RBE=true",
+ },
+ }, {
+ description: "rbe metrics generated",
+ env: []string{
+ "USE_RBE=true",
+ },
+ generated: true,
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ tmpDir := t.TempDir()
+
+ rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
+ if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(rbeBootstrapProgram), 0755); err != nil {
+ t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
+ }
+
+ env := Environment(tt.env)
+ env.Set("OUT_DIR", tmpDir)
+ env.Set("RBE_DIR", tmpDir)
+ env.Set("RBE_output_dir", t.TempDir())
+ config := Config{&configImpl{
+ environ: &env,
+ }}
+
+ rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
+ DumpRBEMetrics(ctx, config, rbeMetricsFilename)
+
+ // Validate that the rbe metrics file exists if RBE is enabled.
+ if _, err := os.Stat(rbeMetricsFilename); err == nil {
+ if !tt.generated {
+ t.Errorf("got true, want false for rbe metrics file %s to exist.", rbeMetricsFilename)
+ }
+ } else if os.IsNotExist(err) {
+ if tt.generated {
+ t.Errorf("got false, want true for rbe metrics file %s to exist.", rbeMetricsFilename)
+ }
+ } else {
+ t.Errorf("unknown error found on checking %s exists: %v", rbeMetricsFilename, err)
+ }
+ })
+ }
+}
+
+func TestDumpRBEMetricsErrors(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ description string
+ rbeOutputDirDefined bool
+ bootstrapProgram string
+ expectedErr string
+ }{{
+ description: "output_dir not defined",
+ bootstrapProgram: rbeBootstrapProgram,
+ expectedErr: "RBE output dir variable not defined",
+ }, {
+ description: "stopRBE failed",
+ rbeOutputDirDefined: true,
+ bootstrapProgram: "#!/bin/bash\nexit 1",
+ expectedErr: "shutdown failed",
+ }, {
+ description: "failed to copy metrics file",
+ rbeOutputDirDefined: true,
+ bootstrapProgram: "#!/bin/bash",
+ expectedErr: "failed to copy",
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ got := err.Error()
+ if !strings.Contains(got, tt.expectedErr) {
+ t.Errorf("got %q, want %q to be contained in error", got, tt.expectedErr)
+ }
+ })
+
+ tmpDir := t.TempDir()
+
+ rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
+ if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(tt.bootstrapProgram), 0755); err != nil {
+ t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
+ }
+
+ env := &Environment{}
+ env.Set("USE_RBE", "true")
+ env.Set("OUT_DIR", tmpDir)
+ env.Set("RBE_DIR", tmpDir)
+
+ if tt.rbeOutputDirDefined {
+ env.Set("RBE_output_dir", t.TempDir())
+ }
+
+ config := Config{&configImpl{
+ environ: env,
+ }}
+
+ rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
+ DumpRBEMetrics(ctx, config, rbeMetricsFilename)
+ t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
+ })
+ }
+}
+
+var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s", rbeMetricsPBFilename)
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 749acb3..fb21430 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -38,7 +38,7 @@
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
- cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", "-t")
+ cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", "-t", "-n")
cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint")
cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash")
cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 3a23a80..1cc2e94 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -44,7 +44,7 @@
// environment variable. The metrics files are copied to a temporary directory
// and the uploader is then executed in the background to allow the user to continue
// working.
-func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStartedMilli int64, files ...string) {
+func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStarted time.Time, files ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
@@ -86,7 +86,7 @@
// For platform builds, the branch and target name is hardcoded to specific
// values for later extraction of the metrics in the data metrics pipeline.
data, err := proto.Marshal(&upload_proto.Upload{
- CreationTimestampMs: proto.Uint64(uint64(buildStartedMilli)),
+ CreationTimestampMs: proto.Uint64(uint64(buildStarted.UnixNano() / int64(time.Millisecond))),
CompletionTimestampMs: proto.Uint64(uint64(time.Now().UnixNano() / int64(time.Millisecond))),
BranchName: proto.String("developer-metrics"),
TargetName: proto.String("platform-build-systems-metrics"),
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index c812730..dccf156 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -97,7 +97,7 @@
buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
}}
- UploadMetrics(ctx, config, false, 1591031903, metricsFiles...)
+ UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
})
}
}
@@ -151,7 +151,7 @@
"OUT_DIR=/bad",
}}}
- UploadMetrics(ctx, config, true, 1591031903, metricsFile)
+ UploadMetrics(ctx, config, true, time.Now(), metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
})
}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 3596e10..8188a69 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -25,6 +25,9 @@
"metrics.go",
"time.go",
],
+ testSrcs: [
+ "time_test.go",
+ ],
}
bootstrap_go_package {
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 3e76d37..2b5c4c3 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -17,7 +17,7 @@
import (
"io/ioutil"
"os"
- "strconv"
+ "time"
"github.com/golang/protobuf/proto"
@@ -25,12 +25,13 @@
)
const (
- RunSetupTool = "setup"
- RunKati = "kati"
- RunSoong = "soong"
- PrimaryNinja = "ninja"
- TestRun = "test"
- Total = "total"
+ PrimaryNinja = "ninja"
+ RunKati = "kati"
+ RunSetupTool = "setup"
+ RunShutdownTool = "shutdown"
+ RunSoong = "soong"
+ TestRun = "test"
+ Total = "total"
)
type Metrics struct {
@@ -130,14 +131,8 @@
}
}
-func (m *Metrics) SetBuildDateTime(date_time string) {
- if date_time != "" {
- date_time_timestamp, err := strconv.ParseInt(date_time, 10, 64)
- if err != nil {
- panic(err)
- }
- m.metrics.BuildDateTimestamp = &date_time_timestamp
- }
+func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
+ m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
// exports the output to the file at outputPath
diff --git a/ui/metrics/time.go b/ui/metrics/time.go
index b8baf16..4016563 100644
--- a/ui/metrics/time.go
+++ b/ui/metrics/time.go
@@ -19,13 +19,18 @@
"android/soong/ui/metrics/metrics_proto"
"android/soong/ui/tracer"
+ "github.com/golang/protobuf/proto"
)
+// for testing purpose only
+var _now = now
+
type timeEvent struct {
desc string
name string
- atNanos uint64 // timestamp measured in nanoseconds since the reference date
+ // the time that the event started to occur.
+ start time.Time
}
type TimeTracer interface {
@@ -39,33 +44,26 @@
var _ TimeTracer = &timeTracerImpl{}
-func (t *timeTracerImpl) now() uint64 {
- return uint64(time.Now().UnixNano())
+func now() time.Time {
+ return time.Now()
}
-func (t *timeTracerImpl) Begin(name, desc string, thread tracer.Thread) {
- t.beginAt(name, desc, t.now())
+func (t *timeTracerImpl) Begin(name, desc string, _ tracer.Thread) {
+ t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, start: _now()})
}
-func (t *timeTracerImpl) beginAt(name, desc string, atNanos uint64) {
- t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, atNanos: atNanos})
-}
-
-func (t *timeTracerImpl) End(thread tracer.Thread) soong_metrics_proto.PerfInfo {
- return t.endAt(t.now())
-}
-
-func (t *timeTracerImpl) endAt(atNanos uint64) soong_metrics_proto.PerfInfo {
+func (t *timeTracerImpl) End(tracer.Thread) soong_metrics_proto.PerfInfo {
if len(t.activeEvents) < 1 {
panic("Internal error: No pending events for endAt to end!")
}
lastEvent := t.activeEvents[len(t.activeEvents)-1]
t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
- realTime := atNanos - lastEvent.atNanos
+ realTime := uint64(_now().Sub(lastEvent.start).Nanoseconds())
return soong_metrics_proto.PerfInfo{
- Desc: &lastEvent.desc,
- Name: &lastEvent.name,
- StartTime: &lastEvent.atNanos,
- RealTime: &realTime}
+ Desc: proto.String(lastEvent.desc),
+ Name: proto.String(lastEvent.name),
+ StartTime: proto.Uint64(uint64(lastEvent.start.UnixNano())),
+ RealTime: proto.Uint64(realTime),
+ }
}
diff --git a/ui/metrics/time_test.go b/ui/metrics/time_test.go
new file mode 100644
index 0000000..d73080a
--- /dev/null
+++ b/ui/metrics/time_test.go
@@ -0,0 +1,42 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// 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 metrics
+
+import (
+ "testing"
+ "time"
+
+ "android/soong/ui/tracer"
+)
+
+func TestEnd(t *testing.T) {
+ startTime := time.Date(2020, time.July, 13, 13, 0, 0, 0, time.UTC)
+ dur := time.Nanosecond * 10
+ initialNow := _now
+ _now = func() time.Time { return startTime.Add(dur) }
+ defer func() { _now = initialNow }()
+
+ timeTracer := &timeTracerImpl{}
+ timeTracer.activeEvents = append(timeTracer.activeEvents, timeEvent{
+ desc: "test",
+ name: "test",
+ start: startTime,
+ })
+
+ perf := timeTracer.End(tracer.Thread(0))
+ if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
+ t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
+ }
+}