Merge "Repeat metalava errors at the end to make it easy to find them"
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 487372b..977345b 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",
@@ -61,6 +62,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/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 f80f37e..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
}
@@ -1561,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,
}
@@ -1580,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
}
diff --git a/android/paths.go b/android/paths.go
index d13b6d8..20ff55e 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -486,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 {
@@ -496,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/apex/androidmk.go b/apex/androidmk.go
index 156906d..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:
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/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/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 4071c0a..4031cfe 100755
--- a/java/app.go
+++ b/java/app.go
@@ -292,8 +292,10 @@
overridableAppProperties overridableAppProperties
- installJniLibs []jniLib
- jniCoverageOutputs android.Paths
+ jniLibs []jniLib
+ installPathForJNISymbols android.Path
+ embeddedJniLibs bool
+ jniCoverageOutputs android.Paths
bundleFile android.Path
@@ -570,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
@@ -581,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))
@@ -603,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() {
@@ -622,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)
@@ -756,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)
@@ -844,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/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/lint.go b/java/lint.go
index 6fbef18..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,9 +233,22 @@
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())
@@ -240,9 +273,9 @@
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).
@@ -250,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
@@ -356,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")
@@ -394,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/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 2dd23c7..935d348 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.
diff --git a/sdk/testing.go b/sdk/testing.go
index 378ce1f..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,7 +43,7 @@
name: "myapex.cert",
certificate: "myapex",
}
- ` + cc.GatherRequiredDepsForTest(android.Android, android.Windows)
+ ` + cc.GatherRequiredDepsForTest(extraOsTypes...)
mockFS := map[string][]byte{
"build/make/target/product/security": nil,
@@ -69,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.
@@ -117,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)
@@ -131,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)
diff --git a/ui/build/config.go b/ui/build/config.go
index a60d70e..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(),
@@ -258,6 +259,12 @@
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}
}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index fcdab3b..fd3b7ab 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -15,8 +15,11 @@
package build
import (
+ "fmt"
+ "math/rand"
"os"
"path/filepath"
+ "time"
"android/soong/ui/metrics"
)
@@ -49,6 +52,11 @@
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")
defer ctx.EndTrace()
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())