Merge "Fix non-determinism in tests."
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/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/android/prebuilt.go b/android/prebuilt.go
index 9f4df28..269ad5d 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -225,7 +225,7 @@
// PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
// corresponding source module, if one exists for the same variant.
func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
- if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+ if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Enabled() && m.Prebuilt() != nil {
p := m.Prebuilt()
if !p.properties.PrebuiltRenamedToSource {
name := m.base().BaseModuleName()
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8029b85..6c3cd9e 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -22,9 +22,10 @@
)
var prebuiltsTests = []struct {
- name string
- modules string
- prebuilt []OsClass
+ name string
+ replaceBp bool // modules is added to default bp boilerplate if false.
+ modules string
+ prebuilt []OsType
}{
{
name: "no prebuilt",
@@ -42,7 +43,7 @@
prefer: false,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "no source prebuilt preferred",
@@ -52,7 +53,7 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "prebuilt not preferred",
@@ -80,7 +81,7 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "prebuilt no file not preferred",
@@ -120,7 +121,7 @@
prefer: true,
srcs: [":fg"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
},
{
name: "prebuilt module for device only",
@@ -135,7 +136,7 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device},
+ prebuilt: []OsType{Android},
},
{
name: "prebuilt file for host only",
@@ -153,7 +154,7 @@
},
},
}`,
- prebuilt: []OsClass{Host},
+ prebuilt: []OsType{BuildOs},
},
{
name: "prebuilt override not preferred",
@@ -191,7 +192,72 @@
prefer: true,
srcs: ["prebuilt_file"],
}`,
- prebuilt: []OsClass{Device, Host},
+ prebuilt: []OsType{Android, BuildOs},
+ },
+ {
+ name: "prebuilt including default-disabled OS",
+ replaceBp: true,
+ modules: `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ source {
+ name: "bar",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt_file"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }`,
+ prebuilt: []OsType{Android, BuildOs, Windows},
+ },
+ {
+ name: "fall back to source for default-disabled OS",
+ replaceBp: true,
+ modules: `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ source {
+ name: "bar",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ }
+
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt_file"],
+ }`,
+ prebuilt: []OsType{Android, BuildOs},
},
}
@@ -203,14 +269,25 @@
for _, test := range prebuiltsTests {
t.Run(test.name, func(t *testing.T) {
- bp := `
- source {
- name: "foo",
- deps: [":bar"],
- }
- ` + test.modules
+ bp := test.modules
+ if !test.replaceBp {
+ bp = bp + `
+ source {
+ name: "foo",
+ deps: [":bar"],
+ }`
+ }
config := TestArchConfig(buildDir, nil, bp, fs)
+ // Add windows to the target list to test the logic when a variant is
+ // disabled by default.
+ if !Windows.DefaultDisabled {
+ t.Errorf("windows is assumed to be disabled by default")
+ }
+ config.config.Targets[Windows] = []Target{
+ {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+ }
+
ctx := NewTestArchContext()
registerTestPrebuiltBuildComponents(ctx)
ctx.RegisterModuleType("filegroup", FileGroupFactory)
@@ -223,7 +300,7 @@
for _, variant := range ctx.ModuleVariantsForTests("foo") {
foo := ctx.ModuleForTests("foo", variant)
- t.Run(foo.Module().Target().Os.Class.String(), func(t *testing.T) {
+ t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
var dependsOnSourceModule, dependsOnPrebuiltModule bool
ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
if _, ok := m.(*sourceModule); ok {
@@ -237,26 +314,38 @@
}
})
+ moduleIsDisabled := !foo.Module().Enabled()
deps := foo.Module().(*sourceModule).deps
- if deps == nil || len(deps) != 1 {
- t.Errorf("deps does not have single path, but is %v", deps)
+ if moduleIsDisabled {
+ if len(deps) > 0 {
+ t.Errorf("disabled module got deps: %v", deps)
+ }
+ } else {
+ if len(deps) != 1 {
+ t.Errorf("deps does not have single path, but is %v", deps)
+ }
}
+
var usingSourceFile, usingPrebuiltFile bool
- if deps[0].String() == "source_file" {
+ if len(deps) > 0 && deps[0].String() == "source_file" {
usingSourceFile = true
}
- if deps[0].String() == "prebuilt_file" {
+ if len(deps) > 0 && deps[0].String() == "prebuilt_file" {
usingPrebuiltFile = true
}
prebuilt := false
for _, os := range test.prebuilt {
- if os == foo.Module().Target().Os.Class {
+ if os == foo.Module().Target().Os {
prebuilt = true
}
}
if prebuilt {
+ if moduleIsDisabled {
+ t.Errorf("dependent module for prebuilt is disabled")
+ }
+
if !dependsOnPrebuiltModule {
t.Errorf("doesn't depend on prebuilt module")
}
@@ -270,7 +359,7 @@
if usingSourceFile {
t.Errorf("using source_file")
}
- } else {
+ } else if !moduleIsDisabled {
if dependsOnPrebuiltModule {
t.Errorf("depends on prebuilt module")
}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 7595238..b9bcc3a 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -105,9 +105,10 @@
fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
// /apex/<apex_name>/{lib|framework|...}
pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+ var modulePath string
if apexType == flattenedApex {
// /system/apex/<name>/{lib|framework|...}
- modulePath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+ modulePath = filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
if a.primaryApexType && !symbolFilesNotNeeded {
fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
@@ -131,6 +132,7 @@
fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
}
} else {
+ modulePath = pathWhenActivated
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
@@ -193,8 +195,13 @@
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.apk.apk
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
- if app, ok := fi.module.(*java.AndroidApp); ok && len(app.JniCoverageOutputs()) > 0 {
- fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(app.JniCoverageOutputs().Strings(), " "))
+ if app, ok := fi.module.(*java.AndroidApp); ok {
+ if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
+ }
+ if jniLibSymbols := app.JNISymbolsInstalls(modulePath); len(jniLibSymbols) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_SYMBOLS :=", jniLibSymbols.String())
+ }
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
case appSet:
@@ -203,6 +210,7 @@
panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
}
fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+ fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
case nativeSharedLib, nativeExecutable, nativeTest:
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
diff --git a/apex/builder.go b/apex/builder.go
index 5fb9a5f..49e4642 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -390,7 +390,7 @@
} else {
if fi.class == appSet {
copyCommands = append(copyCommands,
- fmt.Sprintf("unzip -q -d %s %s", destPathDir, fi.builtFile.String()))
+ fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, fi.builtFile.String()))
} else {
copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 7ff20f4..71c9204 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -225,6 +225,8 @@
func llndkHeadersFactory() android.Module {
module, library := NewLibrary(android.DeviceSupported)
library.HeaderOnly()
+ module.stl = nil
+ module.sanitize = nil
decorator := &llndkHeadersDecorator{
libraryDecorator: library,
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index d3802f9..e485c60 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -117,7 +117,7 @@
// Command is the type of soong_ui execution. Only one type of
// execution is specified. The args are specific to the command.
func main() {
- buildStartedMilli := time.Now().UnixNano() / int64(time.Millisecond)
+ buildStarted := time.Now()
c, args := getCommand(os.Args)
if c == nil {
@@ -138,6 +138,7 @@
defer trace.Close()
met := metrics.New()
+ met.SetBuildDateTime(buildStarted)
stat := &status.Status{}
defer stat.Finish()
@@ -171,7 +172,7 @@
buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
- defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+ defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
os.MkdirAll(logsDir, 0777)
log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
diff --git a/java/aar.go b/java/aar.go
index 500788f..ad9b5e7 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -641,7 +641,7 @@
var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
blueprint.RuleParams{
Command: `rm -rf $outDir && mkdir -p $outDir && ` +
- `unzip -qo -d $outDir $in && rm -rf $outDir/res && touch $out`,
+ `unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out`,
},
"outDir")
diff --git a/java/androidmk.go b/java/androidmk.go
index 618e15d..03994bf 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -131,6 +131,10 @@
entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
}
entries.SetString("LOCAL_MODULE_STEM", library.Stem())
+
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveHTMLZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveTextZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveXMLZip)
},
},
}
@@ -370,9 +374,15 @@
entries.SetString("LOCAL_CERTIFICATE", app.certificate.AndroidMkString())
entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", app.getOverriddenPackages()...)
- for _, jniLib := range app.installJniLibs {
- entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+ if app.embeddedJniLibs {
+ jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
+ entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
+ } else {
+ for _, jniLib := range app.jniLibs {
+ entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+ }
}
+
if len(app.jniCoverageOutputs) > 0 {
entries.AddStrings("LOCAL_PREBUILT_COVERAGE_ARCHIVE", app.jniCoverageOutputs.Strings()...)
}
@@ -383,6 +393,10 @@
install := app.onDeviceDir + "/" + extra.Base()
entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install)
}
+
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveHTMLZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveTextZip)
+ entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveXMLZip)
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
diff --git a/java/app.go b/java/app.go
index 98bce94..4031cfe 100755
--- a/java/app.go
+++ b/java/app.go
@@ -106,6 +106,10 @@
return as.masterFile
}
+func (as *AndroidAppSet) APKCertsFile() android.Path {
+ return as.apkcertsFile
+}
+
var TargetCpuAbi = map[string]string{
"arm": "ARMEABI_V7A",
"arm64": "ARM64_V8A",
@@ -288,8 +292,10 @@
overridableAppProperties overridableAppProperties
- installJniLibs []jniLib
- jniCoverageOutputs android.Paths
+ jniLibs []jniLib
+ installPathForJNISymbols android.Path
+ embeddedJniLibs bool
+ jniCoverageOutputs android.Paths
bundleFile android.Path
@@ -566,8 +572,7 @@
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
}
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
-
+func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath {
var installDir string
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
@@ -577,7 +582,12 @@
} else {
installDir = filepath.Join("app", a.installApkName)
}
- a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+
+ return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+}
+
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+ a.dexpreopter.installPath = a.installPath(ctx)
if a.deviceProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
@@ -599,8 +609,10 @@
func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
var jniJarFile android.WritablePath
if len(jniLibs) > 0 {
+ a.jniLibs = jniLibs
if a.shouldEmbedJnis(ctx) {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
+ a.installPathForJNISymbols = a.installPath(ctx).ToMakePath()
TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
for _, jni := range jniLibs {
if jni.coverageFile.Valid() {
@@ -618,13 +630,25 @@
}
}
}
- } else {
- a.installJniLibs = jniLibs
+ a.embeddedJniLibs = true
}
}
return jniJarFile
}
+func (a *AndroidApp) JNISymbolsInstalls(installPath string) android.RuleBuilderInstalls {
+ var jniSymbols android.RuleBuilderInstalls
+ for _, jniLib := range a.jniLibs {
+ if jniLib.unstrippedFile != nil {
+ jniSymbols = append(jniSymbols, android.RuleBuilderInstall{
+ From: jniLib.unstrippedFile,
+ To: filepath.Join(installPath, targetToJniDir(jniLib.target), jniLib.unstrippedFile.Base()),
+ })
+ }
+ }
+ return jniSymbols
+}
+
func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
// Collect NOTICE files from all dependencies.
seenModules := make(map[android.Module]bool)
@@ -752,6 +776,7 @@
a.linter.mergedManifest = a.aapt.mergedManifestFile
a.linter.manifest = a.aapt.manifestPath
a.linter.resources = a.aapt.resourceFiles
+ a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
dexJarFile := a.dexBuildActions(ctx)
@@ -840,10 +865,11 @@
if lib.Valid() {
jniLibs = append(jniLibs, jniLib{
- name: ctx.OtherModuleName(module),
- path: path,
- target: module.Target(),
- coverageFile: dep.CoverageOutputFile(),
+ name: ctx.OtherModuleName(module),
+ path: path,
+ target: module.Target(),
+ coverageFile: dep.CoverageOutputFile(),
+ unstrippedFile: dep.UnstrippedOutputFile(),
})
} else {
ctx.ModuleErrorf("dependency %q missing output file", otherName)
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 99bfb6d..08865b6 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -112,13 +112,20 @@
// local files that are used within user customized droiddoc options.
Arg_files []string `android:"path"`
- // user customized droiddoc args.
+ // user customized droiddoc args. Deprecated, use flags instead.
// Available variables for substitution:
//
// $(location <label>): the path to the arg_files with name <label>
// $$: a literal $
Args *string
+ // user customized droiddoc args. Not compatible with property args.
+ // Available variables for substitution:
+ //
+ // $(location <label>): the path to the arg_files with name <label>
+ // $$: a literal $
+ Flags []string
+
// names of the output files used in args that will be generated
Out []string
@@ -382,7 +389,7 @@
argFiles android.Paths
implicits android.Paths
- args string
+ args []string
docZip android.WritablePath
stubsSrcJar android.WritablePath
@@ -619,8 +626,8 @@
}
srcFiles = filterHtml(srcFiles)
- flags := j.collectAidlFlags(ctx, deps)
- srcFiles = j.genSources(ctx, srcFiles, flags)
+ aidlFlags := j.collectAidlFlags(ctx, deps)
+ srcFiles = j.genSources(ctx, srcFiles, aidlFlags)
// srcs may depend on some genrule output.
j.srcJars = srcFiles.FilterByExt(".srcjar")
@@ -649,24 +656,38 @@
}
}
- var err error
- j.args, err = android.Expand(String(j.properties.Args), func(name string) (string, error) {
- if strings.HasPrefix(name, "location ") {
- label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if paths, ok := argFilesMap[label]; ok {
- return paths, nil
- } else {
- return "", fmt.Errorf("unknown location label %q, expecting one of %q",
- label, strings.Join(argFileLabels, ", "))
- }
- } else if name == "genDir" {
- return android.PathForModuleGen(ctx).String(), nil
- }
- return "", fmt.Errorf("unknown variable '$(%s)'", name)
- })
+ var argsPropertyName string
+ flags := make([]string, 0)
+ if j.properties.Args != nil && j.properties.Flags != nil {
+ ctx.PropertyErrorf("args", "flags is set. Cannot set args")
+ } else if args := proptools.String(j.properties.Args); args != "" {
+ flags = append(flags, args)
+ argsPropertyName = "args"
+ } else {
+ flags = append(flags, j.properties.Flags...)
+ argsPropertyName = "flags"
+ }
- if err != nil {
- ctx.PropertyErrorf("args", "%s", err.Error())
+ for _, flag := range flags {
+ args, err := android.Expand(flag, func(name string) (string, error) {
+ if strings.HasPrefix(name, "location ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+ if paths, ok := argFilesMap[label]; ok {
+ return paths, nil
+ } else {
+ return "", fmt.Errorf("unknown location label %q, expecting one of %q",
+ label, strings.Join(argFileLabels, ", "))
+ }
+ } else if name == "genDir" {
+ return android.PathForModuleGen(ctx).String(), nil
+ }
+ return "", fmt.Errorf("unknown variable '$(%s)'", name)
+ })
+
+ if err != nil {
+ ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
+ }
+ j.args = append(j.args, args)
}
return deps
@@ -1010,7 +1031,7 @@
d.stubsFlags(ctx, cmd, stubsDir)
- cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+ cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
if d.properties.Compat_config != nil {
compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
@@ -1327,7 +1348,7 @@
cmd.Flag("--include-annotations")
validatingNullability :=
- strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
+ android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
String(d.properties.Validate_nullability_from_list) != ""
migratingNullability := String(d.properties.Previous_api) != ""
@@ -1539,14 +1560,14 @@
d.apiLevelsAnnotationsFlags(ctx, cmd)
d.apiToXmlFlags(ctx, cmd)
- if strings.Contains(d.Javadoc.args, "--generate-documentation") {
+ if android.InList("--generate-documentation", d.Javadoc.args) {
// Currently Metalava have the ability to invoke Javadoc in a seperate process.
// Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
- d.Javadoc.args = d.Javadoc.args + " -nodocs "
+ d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
}
- cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+ cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
for _, o := range d.Javadoc.properties.Out {
cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 130b634..b5a0217 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -146,7 +146,7 @@
var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output &&
- unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input &&
+ unzip -qoDD $in 'classes*.dex' -d $tmpDir/dex-input &&
for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do
echo "--input-dex=$${INPUT_DEX}";
echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})";
diff --git a/java/java.go b/java/java.go
index 46ef98b..367b09c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -635,10 +635,11 @@
}
type jniLib struct {
- name string
- path android.Path
- target android.Target
- coverageFile android.OptionalPath
+ name string
+ path android.Path
+ target android.Target
+ coverageFile android.OptionalPath
+ unstrippedFile android.Path
}
func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool {
diff --git a/java/java_test.go b/java/java_test.go
index fb00361..db3f187 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1097,7 +1097,7 @@
],
proofread_file: "libcore-proofread.txt",
todo_file: "libcore-docs-todo.html",
- args: "-offlinemode -title \"libcore\"",
+ flags: ["-offlinemode -title \"libcore\""],
}
`,
map[string][]byte{
@@ -1124,6 +1124,42 @@
}
}
+func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+ testJavaError(t, "flags is set. Cannot set args", `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
+ droiddoc {
+ name: "bar-doc",
+ srcs: [
+ "bar-doc/a.java",
+ "bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
+ ],
+ exclude_srcs: [
+ "bar-doc/b.java"
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ hdf: [
+ "android.whichdoc offline",
+ ],
+ knowntags: [
+ "bar-doc/known_oj_tags.txt",
+ ],
+ proofread_file: "libcore-proofread.txt",
+ todo_file: "libcore-docs-todo.html",
+ flags: ["-offlinemode -title \"libcore\""],
+ args: "-offlinemode -title \"libcore\"",
+ }
+ `)
+}
+
func TestDroidstubsWithSystemModules(t *testing.T) {
ctx, _ := testJava(t, `
droidstubs {
diff --git a/java/lint.go b/java/lint.go
index b73d6a5..20a7dc4 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -67,12 +67,32 @@
kotlinLanguageLevel string
outputs lintOutputs
properties LintProperties
+
+ buildModuleReportZip bool
}
type lintOutputs struct {
html android.ModuleOutPath
text android.ModuleOutPath
xml android.ModuleOutPath
+
+ transitiveHTML *android.DepSet
+ transitiveText *android.DepSet
+ transitiveXML *android.DepSet
+
+ transitiveHTMLZip android.OptionalPath
+ transitiveTextZip android.OptionalPath
+ transitiveXMLZip android.OptionalPath
+}
+
+type lintOutputIntf interface {
+ lintOutputs() *lintOutputs
+}
+
+var _ lintOutputIntf = (*linter)(nil)
+
+func (l *linter) lintOutputs() *lintOutputs {
+ return &l.outputs
}
func (l *linter) enabled() bool {
@@ -213,27 +233,49 @@
projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
- l.outputs.html = android.PathForModuleOut(ctx, "lint-report.html")
- l.outputs.text = android.PathForModuleOut(ctx, "lint-report.txt")
- l.outputs.xml = android.PathForModuleOut(ctx, "lint-report.xml")
+ html := android.PathForModuleOut(ctx, "lint-report.html")
+ text := android.PathForModuleOut(ctx, "lint-report.txt")
+ xml := android.PathForModuleOut(ctx, "lint-report.xml")
+
+ htmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(html)
+ textDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(text)
+ xmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(xml)
+
+ ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+ if depLint, ok := dep.(lintOutputIntf); ok {
+ depLintOutputs := depLint.lintOutputs()
+ htmlDeps.Transitive(depLintOutputs.transitiveHTML)
+ textDeps.Transitive(depLintOutputs.transitiveText)
+ xmlDeps.Transitive(depLintOutputs.transitiveXML)
+ }
+ })
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
+ var annotationsZipPath, apiVersionsXMLPath android.Path
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
+ apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
+ } else {
+ annotationsZipPath = copiedAnnotationsZipPath(ctx)
+ apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
+ }
+
rule.Command().
Text("(").
Flag("JAVA_OPTS=-Xmx2048m").
FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
- FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath(ctx)).
- FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXmlPath(ctx)).
+ FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
+ FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath).
Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")).
Flag("--quiet").
FlagWithInput("--project ", projectXML).
FlagWithInput("--config ", lintXML).
- FlagWithOutput("--html ", l.outputs.html).
- FlagWithOutput("--text ", l.outputs.text).
- FlagWithOutput("--xml ", l.outputs.xml).
+ FlagWithOutput("--html ", html).
+ FlagWithOutput("--text ", text).
+ FlagWithOutput("--xml ", xml).
FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
FlagWithArg("--java-language-level ", l.javaLanguageLevel).
FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
@@ -241,23 +283,37 @@
Flag("--exitcode").
Flags(l.properties.Lint.Flags).
Implicits(deps).
- Text("|| (").Text("cat").Input(l.outputs.text).Text("; exit 7)").
+ Text("|| (").Text("cat").Input(text).Text("; exit 7)").
Text(")")
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
rule.Build(pctx, ctx, "lint", "lint")
-}
-func (l *linter) lintOutputs() *lintOutputs {
- return &l.outputs
-}
+ l.outputs = lintOutputs{
+ html: html,
+ text: text,
+ xml: xml,
-type lintOutputIntf interface {
- lintOutputs() *lintOutputs
-}
+ transitiveHTML: htmlDeps.Build(),
+ transitiveText: textDeps.Build(),
+ transitiveXML: xmlDeps.Build(),
+ }
-var _ lintOutputIntf = (*linter)(nil)
+ if l.buildModuleReportZip {
+ htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
+ l.outputs.transitiveHTMLZip = android.OptionalPathForPath(htmlZip)
+ lintZip(ctx, l.outputs.transitiveHTML.ToSortedList(), htmlZip)
+
+ textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
+ l.outputs.transitiveTextZip = android.OptionalPathForPath(textZip)
+ lintZip(ctx, l.outputs.transitiveText.ToSortedList(), textZip)
+
+ xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
+ l.outputs.transitiveXMLZip = android.OptionalPathForPath(xmlZip)
+ lintZip(ctx, l.outputs.transitiveXML.ToSortedList(), xmlZip)
+ }
+}
type lintSingleton struct {
htmlZip android.WritablePath
@@ -271,7 +327,7 @@
}
func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
- if ctx.Config().UnbundledBuild() {
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
return
}
@@ -297,25 +353,29 @@
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
- Output: annotationsZipPath(ctx),
+ Output: copiedAnnotationsZipPath(ctx),
})
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
- Output: apiVersionsXmlPath(ctx),
+ Output: copiedAPIVersionsXmlPath(ctx),
})
}
-func annotationsZipPath(ctx android.PathContext) android.WritablePath {
+func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
return android.PathForOutput(ctx, "lint", "annotations.zip")
}
-func apiVersionsXmlPath(ctx android.PathContext) android.WritablePath {
+func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
return android.PathForOutput(ctx, "lint", "api_versions.xml")
}
func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
+ if ctx.Config().UnbundledBuild() {
+ return
+ }
+
var outputs []*lintOutputs
var dirs []string
ctx.VisitAllModules(func(m android.Module) {
@@ -343,18 +403,7 @@
paths = append(paths, get(output))
}
- sort.Slice(paths, func(i, j int) bool {
- return paths[i].String() < paths[j].String()
- })
-
- rule := android.NewRuleBuilder()
-
- rule.Command().BuiltTool(ctx, "soong_zip").
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
- FlagWithRspFileInputList("-l ", paths)
-
- rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+ lintZip(ctx, paths, outputPath)
}
l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
@@ -370,7 +419,9 @@
}
func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
- ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+ if !ctx.Config().UnbundledBuild() {
+ ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+ }
}
var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
@@ -379,3 +430,20 @@
android.RegisterSingletonType("lint",
func() android.Singleton { return &lintSingleton{} })
}
+
+func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
+ paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
+
+ sort.Slice(paths, func(i, j int) bool {
+ return paths[i].String() < paths[j].String()
+ })
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().BuiltTool(ctx, "soong_zip").
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
+ FlagWithRspFileInputList("-l ", paths)
+
+ rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 999c72f..b10e6c7 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -83,8 +83,7 @@
}{}
props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
props.Jars = append(props.Jars, path)
- // TODO(hansson): change to scope after migration is done.
- props.Sdk_version = proptools.StringPtr("current")
+ props.Sdk_version = proptools.StringPtr(scope)
props.Installable = proptools.BoolPtr(false)
mctx.CreateModule(ImportFactory, &props)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index a5aa328..58e05e5 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -143,6 +143,7 @@
droidstubsArgsForGeneratingApi []string
// True if the stubs source and api can be created by the same metalava invocation.
+ // TODO(b/146727827) Now that metalava supports "API hierarchy", do we still need it?
createStubsSourceAndApiTogether bool
// Whether the api scope can be treated as unstable, and should skip compat checks.
@@ -284,6 +285,7 @@
sdkVersion: "module_current",
droidstubsArgs: []string{
"--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+ "--show-for-stub-purposes-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
},
})
apiScopeSystemServer = initApiScope(&apiScope{
diff --git a/rust/config/global.go b/rust/config/global.go
index e1b1775..2020f46 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -54,6 +54,9 @@
"-Wl,--pack-dyn-relocs=android+relr",
"-Wl,--no-undefined",
"-Wl,--hash-style=gnu",
+
+ "-B${ccConfig.ClangBin}",
+ "-fuse-ld=lld",
}
)
@@ -80,7 +83,7 @@
pctx.ImportAs("ccConfig", "android/soong/cc/config")
pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
- pctx.StaticVariable("RustLinkerArgs", "-B ${ccConfig.ClangBin} -fuse-ld=lld")
+ pctx.StaticVariable("RustLinkerArgs", "")
pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 4c16693..4104400 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -21,8 +21,10 @@
)
var (
- DarwinRustFlags = []string{}
- DarwinRustLinkFlags = []string{}
+ DarwinRustFlags = []string{}
+ DarwinRustLinkFlags = []string{
+ "-B${ccConfig.MacToolPath}",
+ }
darwinX8664Rustflags = []string{}
darwinX8664Linkflags = []string{}
)
@@ -66,6 +68,10 @@
return ".dylib"
}
+func (t *toolchainDarwin) DylibSuffix() string {
+ return ".rustlib.dylib"
+}
+
func (t *toolchainDarwin) ProcMacroSuffix() string {
return ".dylib"
}
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index 5376e5b..acc99e1 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -21,8 +21,11 @@
)
var (
- LinuxRustFlags = []string{}
- LinuxRustLinkFlags = []string{}
+ LinuxRustFlags = []string{}
+ LinuxRustLinkFlags = []string{
+ "-B${ccConfig.ClangBin}",
+ "-fuse-ld=lld",
+ }
linuxX86Rustflags = []string{}
linuxX86Linkflags = []string{}
linuxX8664Rustflags = []string{}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index b168cd0..2dd23c7 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -481,9 +481,6 @@
}
func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "myexports",
@@ -592,9 +589,6 @@
// Test that we support the necessary flags for the linker binary, which is
// special in several ways.
func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "mymodule_exports",
@@ -943,9 +937,6 @@
}
func TestHostSnapshotWithCcSharedLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1038,9 +1029,6 @@
}
func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1213,9 +1201,6 @@
}
func TestHostSnapshotWithCcStaticLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "myexports",
@@ -1391,9 +1376,6 @@
}
func TestHostSnapshotWithMultiLib64(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
module_exports {
name: "myexports",
@@ -1523,9 +1505,6 @@
}
func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1579,9 +1558,6 @@
}
func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1656,9 +1632,6 @@
}
func TestSystemSharedLibPropagation(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -1923,9 +1896,6 @@
}
func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
@@ -2009,9 +1979,6 @@
}
func TestUniqueHostSoname(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithCc(t, `
sdk {
name: "mysdk",
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 79da3f0..aee04a1 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -265,9 +265,6 @@
}
func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
sdk {
name: "mysdk",
@@ -325,9 +322,6 @@
}
func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
sdk {
name: "mysdk",
@@ -441,9 +435,6 @@
}
func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
module_exports {
name: "myexports",
@@ -548,9 +539,6 @@
}
func TestHostSnapshotWithJavaTest(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
module_exports {
name: "myexports",
@@ -692,9 +680,6 @@
}
func TestHostSnapshotWithDroidstubs(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithDroidstubs(t, `
module_exports {
name: "myexports",
@@ -835,9 +820,6 @@
}
func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
sdk {
name: "mysdk",
@@ -913,9 +895,6 @@
}
func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
- // b/145598135 - Generating host snapshots for anything other than linux is not supported.
- SkipIfNotLinux(t)
-
result := testSdkWithJava(t, `
module_exports {
name: "myexports",
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 56be741..ef62b79 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -15,6 +15,9 @@
package sdk
import (
+ "android/soong/android"
+ "log"
+ "os"
"testing"
"github.com/google/blueprint/proptools"
@@ -22,6 +25,12 @@
// Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
func TestMain(m *testing.M) {
+ if android.BuildOs != android.Linux {
+ // b/145598135 - Generating host snapshots for anything other than linux is not supported.
+ log.Printf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
+ os.Exit(0)
+ }
+
runTestWithBuildDir(m)
}
diff --git a/sdk/testing.go b/sdk/testing.go
index 4d029e4..378ce1f 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -422,10 +422,3 @@
os.Exit(run())
}
-
-func SkipIfNotLinux(t *testing.T) {
- t.Helper()
- if android.BuildOs != android.Linux {
- t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
- }
-}
diff --git a/sdk/update.go b/sdk/update.go
index 8241151..cf25008 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -911,7 +911,7 @@
if commonVariants, ok := variantsByArchName["common"]; ok {
if len(osTypeVariants) != 1 {
- panic("Expected to only have 1 variant when arch type is common but found " + string(len(osTypeVariants)))
+ panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants)))
}
// A common arch type only has one variant and its properties should be treated
diff --git a/ui/build/config.go b/ui/build/config.go
index e567e40..a60d70e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -256,9 +256,6 @@
ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10)
}
- if ctx.Metrics != nil {
- ctx.Metrics.SetBuildDateTime(ret.buildDateTime)
- }
ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
return Config{ret}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 3a23a80..1cc2e94 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -44,7 +44,7 @@
// environment variable. The metrics files are copied to a temporary directory
// and the uploader is then executed in the background to allow the user to continue
// working.
-func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStartedMilli int64, files ...string) {
+func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStarted time.Time, files ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
@@ -86,7 +86,7 @@
// For platform builds, the branch and target name is hardcoded to specific
// values for later extraction of the metrics in the data metrics pipeline.
data, err := proto.Marshal(&upload_proto.Upload{
- CreationTimestampMs: proto.Uint64(uint64(buildStartedMilli)),
+ CreationTimestampMs: proto.Uint64(uint64(buildStarted.UnixNano() / int64(time.Millisecond))),
CompletionTimestampMs: proto.Uint64(uint64(time.Now().UnixNano() / int64(time.Millisecond))),
BranchName: proto.String("developer-metrics"),
TargetName: proto.String("platform-build-systems-metrics"),
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index c812730..dccf156 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -97,7 +97,7 @@
buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
}}
- UploadMetrics(ctx, config, false, 1591031903, metricsFiles...)
+ UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
})
}
}
@@ -151,7 +151,7 @@
"OUT_DIR=/bad",
}}}
- UploadMetrics(ctx, config, true, 1591031903, metricsFile)
+ UploadMetrics(ctx, config, true, time.Now(), metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
})
}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 3596e10..8188a69 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -25,6 +25,9 @@
"metrics.go",
"time.go",
],
+ testSrcs: [
+ "time_test.go",
+ ],
}
bootstrap_go_package {
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index e055b76..2b5c4c3 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -17,7 +17,7 @@
import (
"io/ioutil"
"os"
- "strconv"
+ "time"
"github.com/golang/protobuf/proto"
@@ -131,14 +131,8 @@
}
}
-func (m *Metrics) SetBuildDateTime(date_time string) {
- if date_time != "" {
- date_time_timestamp, err := strconv.ParseInt(date_time, 10, 64)
- if err != nil {
- panic(err)
- }
- m.metrics.BuildDateTimestamp = &date_time_timestamp
- }
+func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
+ m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
// exports the output to the file at outputPath
diff --git a/ui/metrics/time.go b/ui/metrics/time.go
index b8baf16..4016563 100644
--- a/ui/metrics/time.go
+++ b/ui/metrics/time.go
@@ -19,13 +19,18 @@
"android/soong/ui/metrics/metrics_proto"
"android/soong/ui/tracer"
+ "github.com/golang/protobuf/proto"
)
+// for testing purpose only
+var _now = now
+
type timeEvent struct {
desc string
name string
- atNanos uint64 // timestamp measured in nanoseconds since the reference date
+ // the time that the event started to occur.
+ start time.Time
}
type TimeTracer interface {
@@ -39,33 +44,26 @@
var _ TimeTracer = &timeTracerImpl{}
-func (t *timeTracerImpl) now() uint64 {
- return uint64(time.Now().UnixNano())
+func now() time.Time {
+ return time.Now()
}
-func (t *timeTracerImpl) Begin(name, desc string, thread tracer.Thread) {
- t.beginAt(name, desc, t.now())
+func (t *timeTracerImpl) Begin(name, desc string, _ tracer.Thread) {
+ t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, start: _now()})
}
-func (t *timeTracerImpl) beginAt(name, desc string, atNanos uint64) {
- t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, atNanos: atNanos})
-}
-
-func (t *timeTracerImpl) End(thread tracer.Thread) soong_metrics_proto.PerfInfo {
- return t.endAt(t.now())
-}
-
-func (t *timeTracerImpl) endAt(atNanos uint64) soong_metrics_proto.PerfInfo {
+func (t *timeTracerImpl) End(tracer.Thread) soong_metrics_proto.PerfInfo {
if len(t.activeEvents) < 1 {
panic("Internal error: No pending events for endAt to end!")
}
lastEvent := t.activeEvents[len(t.activeEvents)-1]
t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
- realTime := atNanos - lastEvent.atNanos
+ realTime := uint64(_now().Sub(lastEvent.start).Nanoseconds())
return soong_metrics_proto.PerfInfo{
- Desc: &lastEvent.desc,
- Name: &lastEvent.name,
- StartTime: &lastEvent.atNanos,
- RealTime: &realTime}
+ Desc: proto.String(lastEvent.desc),
+ Name: proto.String(lastEvent.name),
+ StartTime: proto.Uint64(uint64(lastEvent.start.UnixNano())),
+ RealTime: proto.Uint64(realTime),
+ }
}
diff --git a/ui/metrics/time_test.go b/ui/metrics/time_test.go
new file mode 100644
index 0000000..d73080a
--- /dev/null
+++ b/ui/metrics/time_test.go
@@ -0,0 +1,42 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrics
+
+import (
+ "testing"
+ "time"
+
+ "android/soong/ui/tracer"
+)
+
+func TestEnd(t *testing.T) {
+ startTime := time.Date(2020, time.July, 13, 13, 0, 0, 0, time.UTC)
+ dur := time.Nanosecond * 10
+ initialNow := _now
+ _now = func() time.Time { return startTime.Add(dur) }
+ defer func() { _now = initialNow }()
+
+ timeTracer := &timeTracerImpl{}
+ timeTracer.activeEvents = append(timeTracer.activeEvents, timeEvent{
+ desc: "test",
+ name: "test",
+ start: startTime,
+ })
+
+ perf := timeTracer.End(tracer.Thread(0))
+ if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
+ t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
+ }
+}