Merge changes from topic "put-dep-in-apex"

* changes:
  Add jsonmodify tool
  Put dependency in apex_manifest.json
This commit is contained in:
Treehugger Robot 2019-08-06 00:21:11 +00:00 committed by Gerrit Code Review
commit d62b4af8b7
5 changed files with 293 additions and 15 deletions

View File

@ -199,6 +199,13 @@ func LastUniqueStrings(list []string) []string {
return list[totalSkip:]
}
// SortedUniqueStrings returns what the name says
func SortedUniqueStrings(list []string) []string {
unique := FirstUniqueStrings(list)
sort.Strings(unique)
return unique
}
// checkCalledFromInit panics if a Go package's init function is not on the
// call stack.
func checkCalledFromInit() {

View File

@ -46,6 +46,14 @@ var (
Description: "fs_config ${out}",
}, "ro_paths", "exec_paths")
injectApexDependency = pctx.StaticRule("injectApexDependency", blueprint.RuleParams{
Command: `rm -f $out && ${jsonmodify} $in ` +
`-a provideNativeLibs ${provideNativeLibs} ` +
`-a requireNativeLibs ${requireNativeLibs} -o $out`,
CommandDeps: []string{"${jsonmodify}"},
Description: "Inject dependency into ${out}",
}, "provideNativeLibs", "requireNativeLibs")
// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
// against the binary policy using sefcontext_compiler -p <policy>.
@ -143,6 +151,7 @@ func init() {
pctx.HostBinToolVariable("soong_zip", "soong_zip")
pctx.HostBinToolVariable("zip2zip", "zip2zip")
pctx.HostBinToolVariable("zipalign", "zipalign")
pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
android.RegisterModuleType("apex", apexBundleFactory)
android.RegisterModuleType("apex_test", testApexBundleFactory)
@ -431,6 +440,9 @@ type apexBundle struct {
flattened bool
testApex bool
// intermediate path for apex_manifest.json
manifestOut android.WritablePath
}
func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@ -755,6 +767,10 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
// native lib dependencies
var provideNativeLibs []string
var requireNativeLibs []string
// Check if "uses" requirements are met with dependent apexBundles
var providedNativeSharedLibs []string
useVendor := proptools.Bool(a.properties.Use_vendor)
@ -787,6 +803,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
switch depTag {
case sharedLibTag:
if cc, ok := child.(*cc.Module); ok {
if cc.HasStubsVariants() {
provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
}
fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
return true
@ -898,6 +917,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) {
a.externalDeps = append(a.externalDeps, cc.Name())
}
requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
// Don't track further
return false
}
@ -958,6 +978,21 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = filesInfo
a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json")
// put dependency({provide|require}NativeLibs) in apex_manifest.json
manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
ctx.Build(pctx, android.BuildParams{
Rule: injectApexDependency,
Input: manifestSrc,
Output: a.manifestOut,
Args: map[string]string{
"provideNativeLibs": strings.Join(provideNativeLibs, " "),
"requireNativeLibs": strings.Join(requireNativeLibs, " "),
},
})
if a.apexTypes.zip() {
a.buildUnflattenedApex(ctx, zipApex)
}
@ -1005,8 +1040,6 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap
a.container_private_key_file = key
}
manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
var abis []string
for _, target := range ctx.MultiTargets() {
if len(target.Arch.Abi) > 0 {
@ -1036,7 +1069,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap
}
}
implicitInputs := append(android.Paths(nil), filesToCopy...)
implicitInputs = append(implicitInputs, manifest)
implicitInputs = append(implicitInputs, a.manifestOut)
outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
@ -1131,7 +1164,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap
"tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
"image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
"copy_commands": strings.Join(copyCommands, " && "),
"manifest": manifest.String(),
"manifest": a.manifestOut.String(),
"file_contexts": fileContexts.String(),
"canned_fs_config": cannedFsConfig.String(),
"key": a.private_key_file.String(),
@ -1169,7 +1202,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap
"tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
"image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
"copy_commands": strings.Join(copyCommands, " && "),
"manifest": manifest.String(),
"manifest": a.manifestOut.String(),
},
})
}
@ -1200,16 +1233,7 @@ func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
if a.installable() {
// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
// with other ordinary files.
manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
// rename to apex_manifest.json
copiedManifest := android.PathForModuleOut(ctx, "apex_manifest.json")
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: manifest,
Output: copiedManifest,
})
a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
a.filesInfo = append(a.filesInfo, apexFile{a.manifestOut, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
// rename to apex_pubkey
copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")

View File

@ -270,6 +270,13 @@ func ensureListNotContains(t *testing.T, result []string, notExpected string) {
}
}
func ensureListEmpty(t *testing.T, result []string) {
t.Helper()
if len(result) > 0 {
t.Errorf("%q is expected to be empty", result)
}
}
// Minimal test
func TestBasicApex(t *testing.T) {
ctx, _ := testApex(t, `
@ -1060,6 +1067,109 @@ func TestHeaderLibsDependency(t *testing.T) {
ensureContains(t, cFlags, "-Imy_include")
}
func TestDependenciesInApexManifest(t *testing.T) {
ctx, _ := testApex(t, `
apex {
name: "myapex_nodep",
key: "myapex.key",
native_shared_libs: ["lib_nodep"],
compile_multilib: "both",
file_contexts: "myapex",
}
apex {
name: "myapex_dep",
key: "myapex.key",
native_shared_libs: ["lib_dep"],
compile_multilib: "both",
file_contexts: "myapex",
}
apex {
name: "myapex_provider",
key: "myapex.key",
native_shared_libs: ["libfoo"],
compile_multilib: "both",
file_contexts: "myapex",
}
apex {
name: "myapex_selfcontained",
key: "myapex.key",
native_shared_libs: ["lib_dep", "libfoo"],
compile_multilib: "both",
file_contexts: "myapex",
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
cc_library {
name: "lib_nodep",
srcs: ["mylib.cpp"],
system_shared_libs: [],
stl: "none",
}
cc_library {
name: "lib_dep",
srcs: ["mylib.cpp"],
shared_libs: ["libfoo"],
system_shared_libs: [],
stl: "none",
}
cc_library {
name: "libfoo",
srcs: ["mytest.cpp"],
stubs: {
versions: ["1"],
},
system_shared_libs: [],
stl: "none",
}
`)
names := func(s string) (ns []string) {
for _, n := range strings.Split(s, " ") {
if len(n) > 0 {
ns = append(ns, n)
}
}
return
}
var injectRule android.TestingBuildParams
var provideNativeLibs, requireNativeLibs []string
injectRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("injectApexDependency")
provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
ensureListEmpty(t, provideNativeLibs)
ensureListEmpty(t, requireNativeLibs)
injectRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("injectApexDependency")
provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
ensureListEmpty(t, provideNativeLibs)
ensureListContains(t, requireNativeLibs, "libfoo.so")
injectRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("injectApexDependency")
provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
ensureListContains(t, provideNativeLibs, "libfoo.so")
ensureListEmpty(t, requireNativeLibs)
injectRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("injectApexDependency")
provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
ensureListContains(t, provideNativeLibs, "libfoo.so")
ensureListEmpty(t, requireNativeLibs)
}
func TestNonTestApex(t *testing.T) {
ctx, _ := testApex(t, `
apex {

View File

@ -69,3 +69,19 @@ python_test_host {
},
test_suites: ["general-tests"],
}
python_binary_host {
name: "jsonmodify",
main: "jsonmodify.py",
srcs: [
"jsonmodify.py",
],
version: {
py2: {
enabled: true,
},
py3: {
enabled: false,
},
}
}

121
scripts/jsonmodify.py Executable file
View File

@ -0,0 +1,121 @@
#!/usr/bin/env python
#
# Copyright (C) 2019 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.
import argparse
import collections
import json
import sys
def follow_path(obj, path):
cur = obj
last_key = None
for key in path.split('.'):
if last_key:
if last_key not in cur:
return None,None
cur = cur[last_key]
last_key = key
if last_key not in cur:
return None,None
return cur, last_key
def ensure_path(obj, path):
cur = obj
last_key = None
for key in path.split('.'):
if last_key:
if last_key not in cur:
cur[last_key] = dict()
cur = cur[last_key]
last_key = key
return cur, last_key
class SetValue(str):
def apply(self, obj, val):
cur, key = ensure_path(obj, self)
cur[key] = val
class Replace(str):
def apply(self, obj, val):
cur, key = follow_path(obj, self)
if cur:
cur[key] = val
class Remove(str):
def apply(self, obj):
cur, key = follow_path(obj, self)
if cur:
del cur[key]
class AppendList(str):
def apply(self, obj, *args):
cur, key = ensure_path(obj, self)
if key not in cur:
cur[key] = list()
if not isinstance(cur[key], list):
raise ValueError(self + " should be a array.")
cur[key].extend(args)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--out',
help='write result to a file. If omitted, print to stdout',
metavar='output',
action='store')
parser.add_argument('input', nargs='?', help='JSON file')
parser.add_argument("-v", "--value", type=SetValue,
help='set value of the key specified by path. If path doesn\'t exist, creates new one.',
metavar=('path', 'value'),
nargs=2, dest='patch', default=[], action='append')
parser.add_argument("-s", "--replace", type=Replace,
help='replace value of the key specified by path. If path doesn\'t exist, no op.',
metavar=('path', 'value'),
nargs=2, dest='patch', action='append')
parser.add_argument("-r", "--remove", type=Remove,
help='remove the key specified by path. If path doesn\'t exist, no op.',
metavar='path',
nargs=1, dest='patch', action='append')
parser.add_argument("-a", "--append_list", type=AppendList,
help='append values to the list specified by path. If path doesn\'t exist, creates new list for it.',
metavar=('path', 'value'),
nargs='+', dest='patch', default=[], action='append')
args = parser.parse_args()
if args.input:
with open(args.input) as f:
obj = json.load(f, object_pairs_hook=collections.OrderedDict)
else:
obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
for p in args.patch:
p[0].apply(obj, *p[1:])
if args.out:
with open(args.out, "w") as f:
json.dump(obj, f, indent=2)
else:
print(json.dumps(obj, indent=2))
if __name__ == '__main__':
main()