Merge "Make override modules compatible with prebuilts."
diff --git a/OWNERS b/OWNERS
index adf2b4c..dbb491d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,3 +11,4 @@
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 47dbc5d..487372b 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -36,6 +36,7 @@
"package_ctx.go",
"path_properties.go",
"paths.go",
+ "phony.go",
"prebuilt.go",
"proto.go",
"register.go",
diff --git a/android/config.go b/android/config.go
index 675660e..de76803 100644
--- a/android/config.go
+++ b/android/config.go
@@ -111,6 +111,10 @@
fs pathtools.FileSystem
mockBpList string
+ // If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
+ // in tests when a path doesn't exist.
+ testAllowNonExistentPaths bool
+
OncePer
}
@@ -230,6 +234,10 @@
buildDir: buildDir,
captureBuild: true,
env: envCopy,
+
+ // Set testAllowNonExistentPaths so that test contexts don't need to specify every path
+ // passed to PathForSource or PathForModuleSrc.
+ testAllowNonExistentPaths: true,
}
config.deviceConfig = &deviceConfig{
config: config,
diff --git a/android/makevars.go b/android/makevars.go
index aba4cce..ff7c8e4 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -17,12 +17,11 @@
import (
"bytes"
"fmt"
- "io/ioutil"
- "os"
"strconv"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@@ -84,6 +83,29 @@
// builder whenever a file matching the pattern as added or removed, without rerunning if a
// file that does not match the pattern is added to a searched directory.
GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+ // Phony creates a phony rule in Make, which will allow additional DistForGoal
+ // dependencies to be added to it. Phony can be called on the same name multiple
+ // times to add additional dependencies.
+ Phony(names string, deps ...Path)
+
+ // DistForGoal creates a rule to copy one or more Paths to the artifacts
+ // directory on the build server when the specified goal is built.
+ DistForGoal(goal string, paths ...Path)
+
+ // DistForGoalWithFilename creates a rule to copy a Path to the artifacts
+ // directory on the build server with the given filename when the specified
+ // goal is built.
+ DistForGoalWithFilename(goal string, path Path, filename string)
+
+ // DistForGoals creates a rule to copy one or more Paths to the artifacts
+ // directory on the build server when any of the specified goals are built.
+ DistForGoals(goals []string, paths ...Path)
+
+ // DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
+ // directory on the build server with the given filename when any of the
+ // specified goals are built.
+ DistForGoalsWithFilename(goals []string, path Path, filename string)
}
var _ PathContext = MakeVarsContext(nil)
@@ -130,9 +152,11 @@
type makeVarsContext struct {
SingletonContext
- config Config
- pctx PackageContext
- vars []makeVarsVariable
+ config Config
+ pctx PackageContext
+ vars []makeVarsVariable
+ phonies []phony
+ dists []dist
}
var _ MakeVarsContext = &makeVarsContext{}
@@ -144,6 +168,16 @@
strict bool
}
+type phony struct {
+ name string
+ deps []string
+}
+
+type dist struct {
+ goals []string
+ paths []string
+}
+
func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
if !ctx.Config().EmbeddedInMake() {
return
@@ -152,11 +186,16 @@
outFile := absolutePath(PathForOutput(ctx,
"make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
+ lateOutFile := absolutePath(PathForOutput(ctx,
+ "late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
+
if ctx.Failed() {
return
}
- vars := []makeVarsVariable{}
+ var vars []makeVarsVariable
+ var dists []dist
+ var phonies []phony
for _, provider := range makeVarsProviders {
mctx := &makeVarsContext{
SingletonContext: ctx,
@@ -166,6 +205,8 @@
provider.call(mctx)
vars = append(vars, mctx.vars...)
+ phonies = append(phonies, mctx.phonies...)
+ dists = append(dists, mctx.dists...)
}
if ctx.Failed() {
@@ -174,17 +215,16 @@
outBytes := s.writeVars(vars)
- if _, err := os.Stat(absolutePath(outFile)); err == nil {
- if data, err := ioutil.ReadFile(absolutePath(outFile)); err == nil {
- if bytes.Equal(data, outBytes) {
- return
- }
- }
- }
-
- if err := ioutil.WriteFile(absolutePath(outFile), outBytes, 0666); err != nil {
+ if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
ctx.Errorf(err.Error())
}
+
+ lateOutBytes := s.writeLate(phonies, dists)
+
+ if err := pathtools.WriteFileIfChanged(lateOutFile, lateOutBytes, 0666); err != nil {
+ ctx.Errorf(err.Error())
+ }
+
}
func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
@@ -263,6 +303,33 @@
fmt.Fprintln(buf, "\nsoong-compare-var :=")
+ fmt.Fprintln(buf)
+
+ return buf.Bytes()
+}
+
+func (s *makeVarsSingleton) writeLate(phonies []phony, dists []dist) []byte {
+ buf := &bytes.Buffer{}
+
+ fmt.Fprint(buf, `# Autogenerated file
+
+# Values written by Soong read after parsing all Android.mk files.
+
+
+`)
+
+ for _, phony := range phonies {
+ fmt.Fprintf(buf, ".PHONY: %s\n", phony.name)
+ fmt.Fprintf(buf, "%s: %s\n", phony.name, strings.Join(phony.deps, "\\\n "))
+ }
+
+ fmt.Fprintln(buf)
+
+ for _, dist := range dists {
+ fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
+ strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
+ }
+
return buf.Bytes()
}
@@ -299,6 +366,17 @@
c.addVariableRaw(name, value, strict, sort)
}
+func (c *makeVarsContext) addPhony(name string, deps []string) {
+ c.phonies = append(c.phonies, phony{name, deps})
+}
+
+func (c *makeVarsContext) addDist(goals []string, paths []string) {
+ c.dists = append(c.dists, dist{
+ goals: goals,
+ paths: paths,
+ })
+}
+
func (c *makeVarsContext) Strict(name, ninjaStr string) {
c.addVariable(name, ninjaStr, true, false)
}
@@ -318,3 +396,23 @@
func (c *makeVarsContext) CheckRaw(name, value string) {
c.addVariableRaw(name, value, false, false)
}
+
+func (c *makeVarsContext) Phony(name string, deps ...Path) {
+ c.addPhony(name, Paths(deps).Strings())
+}
+
+func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
+ c.DistForGoals([]string{goal}, paths...)
+}
+
+func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
+ c.DistForGoalsWithFilename([]string{goal}, path, filename)
+}
+
+func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
+ c.addDist(goals, Paths(paths).Strings())
+}
+
+func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
+ c.addDist(goals, []string{path.String() + ":" + filename})
+}
diff --git a/android/module.go b/android/module.go
index 82321f4..a488c0d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -207,6 +207,10 @@
// Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string,
// and performs more verification.
Build(pctx PackageContext, params BuildParams)
+ // Phony creates a Make-style phony rule, a rule with no commands that can depend on other
+ // phony rules or real files. Phony can be called on the same name multiple times to add
+ // additional dependencies.
+ Phony(phony string, deps ...Path)
PrimaryModule() Module
FinalModule() Module
@@ -722,6 +726,7 @@
installFiles InstallPaths
checkbuildFiles Paths
noticeFiles Paths
+ phonies map[string]Paths
// Used by buildTargetSingleton to create checkbuild and per-directory build targets
// Only set on the final variant of each module
@@ -1093,26 +1098,17 @@
}
if len(allInstalledFiles) > 0 {
- name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-install")
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: name,
- Implicits: allInstalledFiles.Paths(),
- Default: !ctx.Config().EmbeddedInMake(),
- })
- deps = append(deps, name)
- m.installTarget = name
+ name := namespacePrefix + ctx.ModuleName() + "-install"
+ ctx.Phony(name, allInstalledFiles.Paths()...)
+ m.installTarget = PathForPhony(ctx, name)
+ deps = append(deps, m.installTarget)
}
if len(allCheckbuildFiles) > 0 {
- name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+"-checkbuild")
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: name,
- Implicits: allCheckbuildFiles,
- })
- deps = append(deps, name)
- m.checkbuildTarget = name
+ name := namespacePrefix + ctx.ModuleName() + "-checkbuild"
+ ctx.Phony(name, allCheckbuildFiles...)
+ m.checkbuildTarget = PathForPhony(ctx, name)
+ deps = append(deps, m.checkbuildTarget)
}
if len(deps) > 0 {
@@ -1121,12 +1117,7 @@
suffix = "-soong"
}
- name := PathForPhony(ctx, namespacePrefix+ctx.ModuleName()+suffix)
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Outputs: []WritablePath{name},
- Implicits: deps,
- })
+ ctx.Phony(namespacePrefix+ctx.ModuleName()+suffix, deps...)
m.blueprintDir = ctx.ModuleDir()
}
@@ -1313,6 +1304,9 @@
m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
+ for k, v := range ctx.phonies {
+ m.phonies[k] = append(m.phonies[k], v...)
+ }
} else if ctx.Config().AllowMissingDependencies() {
// If the module is not enabled it will not create any build rules, nothing will call
// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -1460,6 +1454,7 @@
installFiles InstallPaths
checkbuildFiles Paths
module Module
+ phonies map[string]Paths
// For tests
buildParams []BuildParams
@@ -1574,6 +1569,11 @@
m.bp.Build(pctx.PackageContext, convertBuildParams(params))
}
+
+func (m *moduleContext) Phony(name string, deps ...Path) {
+ addPhony(m.config, name, deps...)
+}
+
func (m *moduleContext) GetMissingDependencies() []string {
var missingDeps []string
missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...)
@@ -2233,9 +2233,8 @@
func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
var checkbuildDeps Paths
- mmTarget := func(dir string) WritablePath {
- return PathForPhony(ctx,
- "MODULES-IN-"+strings.Replace(filepath.Clean(dir), "/", "-", -1))
+ mmTarget := func(dir string) string {
+ return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
}
modulesInDir := make(map[string]Paths)
@@ -2261,11 +2260,7 @@
}
// Create a top-level checkbuild target that depends on all modules
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: PathForPhony(ctx, "checkbuild"+suffix),
- Implicits: checkbuildDeps,
- })
+ ctx.Phony("checkbuild"+suffix, checkbuildDeps...)
// Make will generate the MODULES-IN-* targets
if ctx.Config().EmbeddedInMake() {
@@ -2289,7 +2284,7 @@
for _, dir := range dirs {
p := parentDir(dir)
if p != "." && p != "/" {
- modulesInDir[p] = append(modulesInDir[p], mmTarget(dir))
+ modulesInDir[p] = append(modulesInDir[p], PathForPhony(ctx, mmTarget(dir)))
}
}
@@ -2297,14 +2292,7 @@
// depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp
// files.
for _, dir := range dirs {
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: mmTarget(dir),
- Implicits: modulesInDir[dir],
- // HACK: checkbuild should be an optional build, but force it
- // enabled for now in standalone builds
- Default: !ctx.Config().EmbeddedInMake(),
- })
+ ctx.Phony(mmTarget(dir), modulesInDir[dir]...)
}
// Create (host|host-cross|target)-<OS> phony rules to build a reduced checkbuild.
@@ -2331,23 +2319,15 @@
continue
}
- name := PathForPhony(ctx, className+"-"+os.Name)
- osClass[className] = append(osClass[className], name)
+ name := className + "-" + os.Name
+ osClass[className] = append(osClass[className], PathForPhony(ctx, name))
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: name,
- Implicits: deps,
- })
+ ctx.Phony(name, deps...)
}
// Wrap those into host|host-cross|target phony rules
for _, class := range SortedStringKeys(osClass) {
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: PathForPhony(ctx, class),
- Implicits: osClass[class],
- })
+ ctx.Phony(class, osClass[class]...)
}
}
diff --git a/android/paths.go b/android/paths.go
index 3ad27ac..bed6f3f 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -405,7 +405,7 @@
p := pathForModuleSrc(ctx, s)
if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
reportPathErrorf(ctx, "%s: %s", p, err.Error())
- } else if !exists {
+ } else if !exists && !ctx.Config().testAllowNonExistentPaths {
reportPathErrorf(ctx, "module source path %q does not exist", p)
}
@@ -798,7 +798,7 @@
}
} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
reportPathErrorf(ctx, "%s: %s", path, err.Error())
- } else if !exists {
+ } else if !exists && !ctx.Config().testAllowNonExistentPaths {
reportPathErrorf(ctx, "source path %q does not exist", path)
}
return path
diff --git a/android/phony.go b/android/phony.go
new file mode 100644
index 0000000..f8e5a44
--- /dev/null
+++ b/android/phony.go
@@ -0,0 +1,75 @@
+// 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 (
+ "sync"
+
+ "github.com/google/blueprint"
+)
+
+var phonyMapOnceKey = NewOnceKey("phony")
+
+type phonyMap map[string]Paths
+
+var phonyMapLock sync.Mutex
+
+func getPhonyMap(config Config) phonyMap {
+ return config.Once(phonyMapOnceKey, func() interface{} {
+ return make(phonyMap)
+ }).(phonyMap)
+}
+
+func addPhony(config Config, name string, deps ...Path) {
+ phonyMap := getPhonyMap(config)
+ phonyMapLock.Lock()
+ defer phonyMapLock.Unlock()
+ phonyMap[name] = append(phonyMap[name], deps...)
+}
+
+type phonySingleton struct {
+ phonyMap phonyMap
+ phonyList []string
+}
+
+var _ SingletonMakeVarsProvider = (*phonySingleton)(nil)
+
+func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
+ p.phonyMap = getPhonyMap(ctx.Config())
+ p.phonyList = SortedStringKeys(p.phonyMap)
+ for _, phony := range p.phonyList {
+ p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
+ }
+
+ if !ctx.Config().EmbeddedInMake() {
+ for _, phony := range p.phonyList {
+ ctx.Build(pctx, BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []WritablePath{PathForPhony(ctx, phony)},
+ Implicits: p.phonyMap[phony],
+ })
+ }
+ }
+}
+
+func (p phonySingleton) MakeVars(ctx MakeVarsContext) {
+ for _, phony := range p.phonyList {
+ ctx.Phony(phony, p.phonyMap[phony]...)
+ }
+}
+
+func phonySingletonFactory() Singleton {
+ return &phonySingleton{}
+}
diff --git a/android/register.go b/android/register.go
index ccfe01e..036a811 100644
--- a/android/register.go
+++ b/android/register.go
@@ -104,6 +104,9 @@
registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
+ // Register phony just before makevars so it can write out its phony rules as Make rules
+ ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(phonySingletonFactory))
+
// Register makevars after other singletons so they can export values through makevars
ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
diff --git a/android/singleton.go b/android/singleton.go
index 568398c..2c51c6c 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -36,6 +36,12 @@
Variable(pctx PackageContext, name, value string)
Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
Build(pctx PackageContext, params BuildParams)
+
+ // Phony creates a Make-style phony rule, a rule with no commands that can depend on other
+ // phony rules or real files. Phony can be called on the same name multiple times to add
+ // additional dependencies.
+ Phony(name string, deps ...Path)
+
RequireNinjaVersion(major, minor, micro int)
// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
@@ -156,6 +162,10 @@
}
+func (s *singletonContextAdaptor) Phony(name string, deps ...Path) {
+ addPhony(s.Config(), name, deps...)
+}
+
func (s *singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
}
diff --git a/android/writedocs.go b/android/writedocs.go
index 7262ad8..9e43e80 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -69,9 +69,5 @@
})
// Add a phony target for building the documentation
- ctx.Build(pctx, BuildParams{
- Rule: blueprint.Phony,
- Output: PathForPhony(ctx, "soong_docs"),
- Input: docsFile,
- })
+ ctx.Phony("soong_docs", docsFile)
}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 52480ea..b3ad610 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -457,6 +457,9 @@
entries.SetString("LOCAL_MODULE_PATH", path)
entries.SetString("LOCAL_MODULE_STEM", stem)
entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
+ if c.parsedCoverageXmlPath.String() != "" {
+ entries.SetString("SOONG_NDK_API_XML", "$(SOONG_NDK_API_XML) "+c.parsedCoverageXmlPath.String())
+ }
})
}
diff --git a/cc/cc.go b/cc/cc.go
index 770391a..f80c229 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -3223,12 +3223,7 @@
})
// TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets
if len(xrefTargets) > 0 {
- ctx.Build(pctx, android.BuildParams{
- Rule: blueprint.Phony,
- Output: android.PathForPhony(ctx, "xref_cxx"),
- Inputs: xrefTargets,
- //Default: true,
- })
+ ctx.Phony("xref_cxx", xrefTargets...)
}
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 1597b88..d79badb 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -25,8 +25,12 @@
"android/soong/android"
)
+func init() {
+ pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser")
+}
+
var (
- toolPath = pctx.SourcePathVariable("toolPath", "build/soong/cc/gen_stub_libs.py")
+ toolPath = pctx.SourcePathVariable("toolPath", "build/soong/cc/scriptlib/gen_stub_libs.py")
genStubSrc = pctx.AndroidStaticRule("genStubSrc",
blueprint.RuleParams{
@@ -35,6 +39,12 @@
CommandDeps: []string{"$toolPath"},
}, "arch", "apiLevel", "apiMap", "flags")
+ parseNdkApiRule = pctx.AndroidStaticRule("parseNdkApiRule",
+ blueprint.RuleParams{
+ Command: "$ndk_api_coverage_parser $in $out --api-map $apiMap",
+ CommandDeps: []string{"$ndk_api_coverage_parser"},
+ }, "apiMap")
+
ndkLibrarySuffix = ".ndk"
ndkPrebuiltSharedLibs = []string{
@@ -111,8 +121,9 @@
properties libraryProperties
- versionScriptPath android.ModuleGenPath
- installPath android.Path
+ versionScriptPath android.ModuleGenPath
+ parsedCoverageXmlPath android.ModuleOutPath
+ installPath android.Path
}
// OMG GO
@@ -308,14 +319,35 @@
return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath
}
+func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
+ apiLevelsJson := android.GetApiLevelsJson(ctx)
+ symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
+ outputFileName := strings.Split(symbolFilePath.Base(), ".")[0]
+ parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFileName+".xml")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: parseNdkApiRule,
+ Description: "parse ndk api symbol file for api coverage: " + symbolFilePath.Rel(),
+ Outputs: []android.WritablePath{parsedApiCoveragePath},
+ Input: symbolFilePath,
+ Args: map[string]string{
+ "apiMap": apiLevelsJson.String(),
+ },
+ })
+ return parsedApiCoveragePath
+}
+
func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
}
- objs, versionScript := compileStubLibrary(ctx, flags, String(c.properties.Symbol_file),
+ symbolFile := String(c.properties.Symbol_file)
+ objs, versionScript := compileStubLibrary(ctx, flags, symbolFile,
c.properties.ApiLevel, "")
c.versionScriptPath = versionScript
+ if c.properties.ApiLevel == "current" && ctx.PrimaryArch() {
+ c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
+ }
return objs
}
diff --git a/cc/scriptlib/Android.bp b/cc/scriptlib/Android.bp
new file mode 100644
index 0000000..ff9a2f0
--- /dev/null
+++ b/cc/scriptlib/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2020 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.
+//
+
+python_test_host {
+ name: "test_ndk_api_coverage_parser",
+ main: "test_ndk_api_coverage_parser.py",
+ srcs: [
+ "test_ndk_api_coverage_parser.py",
+ ],
+}
+
+python_binary_host {
+ name: "ndk_api_coverage_parser",
+ main: "ndk_api_coverage_parser.py",
+ srcs: [
+ "gen_stub_libs.py",
+ "ndk_api_coverage_parser.py",
+ ],
+}
diff --git a/cc/scriptlib/__init__.py b/cc/scriptlib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc/scriptlib/__init__.py
diff --git a/cc/gen_stub_libs.py b/cc/scriptlib/gen_stub_libs.py
similarity index 99%
rename from cc/gen_stub_libs.py
rename to cc/scriptlib/gen_stub_libs.py
index 7deb804..d61dfbb 100755
--- a/cc/gen_stub_libs.py
+++ b/cc/scriptlib/gen_stub_libs.py
@@ -246,6 +246,7 @@
def __eq__(self, other):
return self.name == other.name and set(self.tags) == set(other.tags)
+
class SymbolFileParser(object):
"""Parses NDK symbol files."""
def __init__(self, input_file, api_map, arch, api, llndk, apex):
diff --git a/cc/scriptlib/ndk_api_coverage_parser.py b/cc/scriptlib/ndk_api_coverage_parser.py
new file mode 100755
index 0000000..d74035b
--- /dev/null
+++ b/cc/scriptlib/ndk_api_coverage_parser.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2020 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 xml of NDK libraries used for API coverage analysis."""
+import argparse
+import json
+import os
+import sys
+
+from xml.etree.ElementTree import Element, SubElement, tostring
+from gen_stub_libs import ALL_ARCHITECTURES, FUTURE_API_LEVEL, MultiplyDefinedSymbolError, SymbolFileParser
+
+
+ROOT_ELEMENT_TAG = 'ndk-library'
+SYMBOL_ELEMENT_TAG = 'symbol'
+ARCHITECTURE_ATTRIBUTE_KEY = 'arch'
+DEPRECATED_ATTRIBUTE_KEY = 'is_deprecated'
+PLATFORM_ATTRIBUTE_KEY = 'is_platform'
+NAME_ATTRIBUTE_KEY = 'name'
+VARIABLE_TAG = 'var'
+EXPOSED_TARGET_TAGS = (
+ 'vndk',
+ 'apex',
+ 'llndk',
+)
+API_LEVEL_TAG_PREFIXES = (
+ 'introduced=',
+ 'introduced-',
+)
+
+
+def parse_tags(tags):
+ """Parses tags and save needed tags in the created attributes.
+
+ Return attributes dictionary.
+ """
+ attributes = {}
+ arch = []
+ for tag in tags:
+ if tag.startswith(tuple(API_LEVEL_TAG_PREFIXES)):
+ key, _, value = tag.partition('=')
+ attributes.update({key: value})
+ elif tag in ALL_ARCHITECTURES:
+ arch.append(tag)
+ elif tag in EXPOSED_TARGET_TAGS:
+ attributes.update({tag: 'True'})
+ attributes.update({ARCHITECTURE_ATTRIBUTE_KEY: ','.join(arch)})
+ return attributes
+
+
+class XmlGenerator(object):
+ """Output generator that writes parsed symbol file to a xml file."""
+ def __init__(self, output_file):
+ self.output_file = output_file
+
+ def convertToXml(self, versions):
+ """Writes all symbol data to the output file."""
+ root = Element(ROOT_ELEMENT_TAG)
+ for version in versions:
+ if VARIABLE_TAG in version.tags:
+ continue
+ version_attributes = parse_tags(version.tags)
+ _, _, postfix = version.name.partition('_')
+ is_platform = postfix == 'PRIVATE' or postfix == 'PLATFORM'
+ is_deprecated = postfix == 'DEPRECATED'
+ version_attributes.update({PLATFORM_ATTRIBUTE_KEY: str(is_platform)})
+ version_attributes.update({DEPRECATED_ATTRIBUTE_KEY: str(is_deprecated)})
+ for symbol in version.symbols:
+ if VARIABLE_TAG in symbol.tags:
+ continue
+ attributes = {NAME_ATTRIBUTE_KEY: symbol.name}
+ attributes.update(version_attributes)
+ # If same version tags already exist, it will be overwrite here.
+ attributes.update(parse_tags(symbol.tags))
+ SubElement(root, SYMBOL_ELEMENT_TAG, attributes)
+ return root
+
+ def write_xml_to_file(self, root):
+ """Write xml element root to output_file."""
+ parsed_data = tostring(root)
+ output_file = open(self.output_file, "wb")
+ output_file.write(parsed_data)
+
+ def write(self, versions):
+ root = self.convertToXml(versions)
+ self.write_xml_to_file(root)
+
+
+def parse_args():
+ """Parses and returns command line arguments."""
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument('symbol_file', type=os.path.realpath, help='Path to symbol file.')
+ parser.add_argument(
+ 'output_file', type=os.path.realpath,
+ help='The output parsed api coverage file.')
+ parser.add_argument(
+ '--api-map', type=os.path.realpath, required=True,
+ help='Path to the API level map JSON file.')
+ 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)
+
+ with open(args.symbol_file) as symbol_file:
+ try:
+ versions = SymbolFileParser(symbol_file, api_map, "", FUTURE_API_LEVEL,
+ True, True).parse()
+ except MultiplyDefinedSymbolError as ex:
+ sys.exit('{}: error: {}'.format(args.symbol_file, ex))
+
+ generator = XmlGenerator(args.output_file)
+ generator.write(versions)
+
+if __name__ == '__main__':
+ main()
diff --git a/cc/test_gen_stub_libs.py b/cc/scriptlib/test_gen_stub_libs.py
similarity index 100%
rename from cc/test_gen_stub_libs.py
rename to cc/scriptlib/test_gen_stub_libs.py
diff --git a/cc/scriptlib/test_ndk_api_coverage_parser.py b/cc/scriptlib/test_ndk_api_coverage_parser.py
new file mode 100644
index 0000000..a3c9b70
--- /dev/null
+++ b/cc/scriptlib/test_ndk_api_coverage_parser.py
@@ -0,0 +1,66 @@
+#!/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/testing.go b/cc/testing.go
index edbb24d..479b424 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -467,15 +467,6 @@
}
func GatherRequiredFilesForTest(fs map[string][]byte) {
- fs["prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-arm/usr/lib/crtbegin_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-arm/usr/lib/crtend_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-arm64/usr/lib/crtbegin_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-arm64/usr/lib/crtend_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-x86/usr/lib/crtbegin_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-x86/usr/lib/crtend_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-x86_64/usr/lib64/crtbegin_so.o"] = nil
- fs["prebuilts/ndk/current/platforms/android-27/arch-x86_64/usr/lib64/crtend_so.o"] = nil
}
func TestConfig(buildDir string, os android.OsType, env map[string]string,
@@ -484,20 +475,7 @@
// add some modules that are required by the compiler and/or linker
bp = bp + GatherRequiredDepsForTest(os)
- mockFS := map[string][]byte{
- "foo.c": nil,
- "foo.lds": nil,
- "bar.c": nil,
- "baz.c": nil,
- "baz.o": nil,
- "a.proto": nil,
- "b.aidl": nil,
- "sub/c.aidl": nil,
- "my_include": nil,
- "foo.map.txt": nil,
- "liba.so": nil,
- "libb.a": nil,
- }
+ mockFS := map[string][]byte{}
GatherRequiredFilesForTest(mockFS)
diff --git a/java/app_test.go b/java/app_test.go
index eeba161..e45ba70 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2657,7 +2657,7 @@
}
func TestEmbedNotice(t *testing.T) {
- ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ ctx, _ := testJavaWithFS(t, cc.GatherRequiredDepsForTest(android.Android)+`
android_app {
name: "foo",
srcs: ["a.java"],
@@ -2713,7 +2713,12 @@
srcs: ["b.java"],
notice: "TOOL_NOTICE",
}
- `)
+ `, map[string][]byte{
+ "APP_NOTICE": nil,
+ "GENRULE_NOTICE": nil,
+ "LIB_NOTICE": nil,
+ "TOOL_NOTICE": nil,
+ })
// foo has NOTICE files to process, and embed_notices is true.
foo := ctx.ModuleForTests("foo", "android_common")
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 4c3e112..68cfe9f 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1470,7 +1470,7 @@
FlagWithInput("@", srcJarList).
FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
- if implicitsRsp.String() != "" {
+ if implicitsRsp != nil {
cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
}
diff --git a/java/java.go b/java/java.go
index 5f8ad03..1cdfbf1 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2874,11 +2874,7 @@
})
// TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
if len(xrefTargets) > 0 {
- ctx.Build(pctx, android.BuildParams{
- Rule: blueprint.Phony,
- Output: android.PathForPhony(ctx, "xref_java"),
- Inputs: xrefTargets,
- })
+ ctx.Phony("xref_java", xrefTargets...)
}
}
diff --git a/java/java_test.go b/java/java_test.go
index 8ea34d9..215070e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -142,9 +142,14 @@
return ctx, config
}
+func testJavaWithFS(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
+ t.Helper()
+ return testJavaWithConfig(t, testConfig(nil, bp, fs))
+}
+
func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
t.Helper()
- return testJavaWithConfig(t, testConfig(nil, bp, nil))
+ return testJavaWithFS(t, bp, nil)
}
func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
@@ -740,7 +745,7 @@
for _, test := range table {
t.Run(test.name, func(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx, _ := testJavaWithFS(t, `
java_library {
name: "foo",
srcs: [
@@ -750,7 +755,13 @@
],
`+test.prop+`,
}
- `+test.extra)
+ `+test.extra,
+ map[string][]byte{
+ "java-res/a/a": nil,
+ "java-res/b/b": nil,
+ "java-res2/a": nil,
+ },
+ )
foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
@@ -769,7 +780,7 @@
}
func TestIncludeSrcs(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx, _ := testJavaWithFS(t, `
java_library {
name: "foo",
srcs: [
@@ -790,7 +801,11 @@
java_resource_dirs: ["java-res"],
include_srcs: true,
}
- `)
+ `, map[string][]byte{
+ "java-res/a/a": nil,
+ "java-res/b/b": nil,
+ "java-res2/a": nil,
+ })
// Test a library with include_srcs: true
foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
@@ -832,7 +847,7 @@
}
func TestGeneratedSources(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx, _ := testJavaWithFS(t, `
java_library {
name: "foo",
srcs: [
@@ -847,7 +862,10 @@
tool_files: ["java-res/a"],
out: ["gen.java"],
}
- `)
+ `, map[string][]byte{
+ "a.java": nil,
+ "b.java": nil,
+ })
javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
genrule := ctx.ModuleForTests("gen", "").Rule("generator")
@@ -932,7 +950,7 @@
}
func TestDroiddoc(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx, _ := testJavaWithFS(t, `
droiddoc_exported_dir {
name: "droiddoc-templates-sdk",
path: ".",
@@ -945,7 +963,7 @@
droiddoc {
name: "bar-doc",
srcs: [
- "bar-doc/*.java",
+ "bar-doc/a.java",
"bar-doc/IFoo.aidl",
":bar-doc-aidl-srcs",
],
@@ -963,7 +981,11 @@
todo_file: "libcore-docs-todo.html",
args: "-offlinemode -title \"libcore\"",
}
- `)
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ "bar-doc/b.java": nil,
+ })
barDoc := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc")
var javaSrcs []string
@@ -989,7 +1011,7 @@
droidstubs {
name: "stubs-source-system-modules",
srcs: [
- "bar-doc/*.java",
+ "bar-doc/a.java",
],
sdk_version: "none",
system_modules: "source-system-modules",
@@ -1010,7 +1032,7 @@
droidstubs {
name: "stubs-prebuilt-system-modules",
srcs: [
- "bar-doc/*.java",
+ "bar-doc/a.java",
],
sdk_version: "none",
system_modules: "prebuilt-system-modules",
diff --git a/java/testing.go b/java/testing.go
index f993f56..f34c64a 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -25,35 +25,12 @@
bp += GatherRequiredDepsForTest()
mockFS := map[string][]byte{
- "a.java": nil,
- "b.java": nil,
- "c.java": nil,
- "b.kt": nil,
- "a.jar": nil,
- "b.jar": nil,
- "c.jar": nil,
- "APP_NOTICE": nil,
- "GENRULE_NOTICE": nil,
- "LIB_NOTICE": nil,
- "TOOL_NOTICE": nil,
- "AndroidTest.xml": nil,
- "java-res/a/a": nil,
- "java-res/b/b": nil,
- "java-res2/a": nil,
- "java-fg/a.java": nil,
- "java-fg/b.java": nil,
- "java-fg/c.java": nil,
"api/current.txt": nil,
"api/removed.txt": nil,
"api/system-current.txt": nil,
"api/system-removed.txt": nil,
"api/test-current.txt": nil,
"api/test-removed.txt": nil,
- "framework/aidl/a.aidl": nil,
- "aidl/foo/IFoo.aidl": nil,
- "aidl/bar/IBar.aidl": nil,
- "assets_a/a": nil,
- "assets_b/b": nil,
"prebuilts/sdk/14/public/android.jar": nil,
"prebuilts/sdk/14/public/framework.aidl": nil,
@@ -103,45 +80,6 @@
"prebuilts/sdk/30/test/api/bar-removed.txt": nil,
"prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
"prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"],}`),
-
- "prebuilts/apk/app.apk": nil,
- "prebuilts/apk/app_arm.apk": nil,
- "prebuilts/apk/app_arm64.apk": nil,
- "prebuilts/apk/app_xhdpi.apk": nil,
- "prebuilts/apk/app_xxhdpi.apk": nil,
-
- "prebuilts/apks/app.apks": nil,
-
- // For framework-res, which is an implicit dependency for framework
- "AndroidManifest.xml": nil,
- "build/make/target/product/security/testkey": nil,
-
- "build/soong/scripts/jar-wrapper.sh": nil,
-
- "build/make/core/verify_uses_libraries.sh": nil,
-
- "build/make/core/proguard.flags": nil,
- "build/make/core/proguard_basic_keeps.flags": nil,
-
- "jdk8/jre/lib/jce.jar": nil,
- "jdk8/jre/lib/rt.jar": nil,
- "jdk8/lib/tools.jar": nil,
-
- "bar-doc/a.java": nil,
- "bar-doc/b.java": nil,
- "bar-doc/IFoo.aidl": nil,
- "bar-doc/IBar.aidl": nil,
- "bar-doc/known_oj_tags.txt": nil,
- "external/doclava/templates-sdk": nil,
-
- "cert/new_cert.x509.pem": nil,
- "cert/new_cert.pk8": nil,
- "lineage.bin": nil,
-
- "testdata/data": nil,
-
- "stubs-sources/foo/Foo.java": nil,
- "stubs/sources/foo/Foo.java": nil,
}
cc.GatherRequiredFilesForTest(mockFS)
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index f836ea9..df763c8 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -20,6 +20,7 @@
conscrypt-module-test-exports
conscrypt-module-host-exports
runtime-module-sdk
+ runtime-module-host-exports
)
# We want to create apex modules for all supported architectures.