diff options
author | Yifan Hong | 2018-10-02 18:55:47 -0500 |
---|---|---|
committer | Yifan Hong | 2018-10-09 19:30:55 -0500 |
commit | 0710a86dc59bed180e134ab92bf62cb44cb078ed (patch) | |
tree | eb052456617e4483e055889522cb80f70d206b1c | |
parent | d34570694d7c9fe5c7bba7756c883c27b3a1590e (diff) | |
download | platform-system-libvintf-0710a86dc59bed180e134ab92bf62cb44cb078ed.tar.gz platform-system-libvintf-0710a86dc59bed180e134ab92bf62cb44cb078ed.tar.xz platform-system-libvintf-0710a86dc59bed180e134ab92bf62cb44cb078ed.zip |
Add dump_hals_for_release.py and analyze_matrix.
Test: ./system/libvintf/analyze_matrix/dump_hals_for_release.py \
--hidl-gen=$(which hidl-gen) \
--analyze-matrix=$(which analyze_matrix) \
--compatibility-matrix \
hardware/interfaces/compatibility_matrices/compatibility_matrix.*.xml
Bug: 116165266
Change-Id: Ic3fa41f8e2dfa6c083ec867534fc39dad469410e
-rw-r--r-- | analyze_matrix/Android.bp | 43 | ||||
-rw-r--r-- | analyze_matrix/analyze_matrix.cpp | 128 | ||||
-rwxr-xr-x | analyze_matrix/dump_hals_for_release.py | 110 |
3 files changed, 281 insertions, 0 deletions
diff --git a/analyze_matrix/Android.bp b/analyze_matrix/Android.bp new file mode 100644 index 0000000..1d8beb1 --- /dev/null +++ b/analyze_matrix/Android.bp | |||
@@ -0,0 +1,43 @@ | |||
1 | // Copyright (C) 2018 The Android Open Source Project | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | cc_binary_host { | ||
16 | name: "analyze_matrix", | ||
17 | defaults: ["libvintf-defaults"], | ||
18 | shared_libs: [ | ||
19 | "libbase", | ||
20 | "libhidl-gen-utils", | ||
21 | "libgflags", | ||
22 | "libvintf", | ||
23 | ], | ||
24 | cpp_std: "experimental", | ||
25 | srcs: [ | ||
26 | "analyze_matrix.cpp", | ||
27 | ], | ||
28 | } | ||
29 | |||
30 | python_binary_host { | ||
31 | name: "dump_hals_for_release", | ||
32 | srcs: [ | ||
33 | "dump_hals_for_release.py", | ||
34 | ], | ||
35 | version: { | ||
36 | py2: { | ||
37 | enabled: true, | ||
38 | }, | ||
39 | py3: { | ||
40 | enabled: false, | ||
41 | }, | ||
42 | }, | ||
43 | } | ||
diff --git a/analyze_matrix/analyze_matrix.cpp b/analyze_matrix/analyze_matrix.cpp new file mode 100644 index 0000000..49134ef --- /dev/null +++ b/analyze_matrix/analyze_matrix.cpp | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <optional> | ||
18 | #include <set> | ||
19 | |||
20 | #include <android-base/logging.h> | ||
21 | #include <gflags/gflags.h> | ||
22 | #include <hidl-util/FqInstance.h> | ||
23 | #include <vintf/FileSystem.h> | ||
24 | #include <vintf/parse_string.h> | ||
25 | #include <vintf/parse_xml.h> | ||
26 | |||
27 | namespace android { | ||
28 | namespace vintf { | ||
29 | |||
30 | namespace { | ||
31 | |||
32 | template <typename T> | ||
33 | std::optional<T> readObject(const std::string& path, const XmlConverter<T>& converter) { | ||
34 | std::string xml; | ||
35 | std::string error; | ||
36 | status_t err = details::FileSystemImpl().fetch(path, &xml, &error); | ||
37 | if (err != OK) { | ||
38 | LOG(ERROR) << "Cannot read '" << path << "': " << error; | ||
39 | return std::nullopt; | ||
40 | } | ||
41 | auto ret = std::make_optional<T>(); | ||
42 | if (!converter(&ret.value(), xml, &error)) { | ||
43 | LOG(ERROR) << "Cannot parse '" << path << "': " << error; | ||
44 | return std::nullopt; | ||
45 | } | ||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | std::optional<std::set<std::string>> getInterfaces(const CompatibilityMatrix& mat) { | ||
50 | auto set = std::make_optional<std::set<std::string>>(); | ||
51 | mat.forEachInstance([&set](const auto& matrixInstance) { | ||
52 | for (auto minorVer = matrixInstance.versionRange().minMinor; | ||
53 | minorVer <= matrixInstance.versionRange().maxMinor; ++minorVer) { | ||
54 | FqInstance fqInstance; | ||
55 | if (!fqInstance.setTo(matrixInstance.package(), matrixInstance.versionRange().majorVer, | ||
56 | minorVer, matrixInstance.interface())) { | ||
57 | LOG(ERROR) << "Matrix not valid; '" << matrixInstance.package() << "@" | ||
58 | << matrixInstance.versionRange().majorVer << "." << minorVer | ||
59 | << "::" << matrixInstance.interface() << "' is not a valid FQName."; | ||
60 | set = std::nullopt; | ||
61 | return false; // break | ||
62 | } | ||
63 | |||
64 | set->insert(fqInstance.string()); | ||
65 | } | ||
66 | return true; // continue | ||
67 | }); | ||
68 | return set; | ||
69 | } | ||
70 | |||
71 | } // namespace | ||
72 | |||
73 | } // namespace vintf | ||
74 | } // namespace android | ||
75 | |||
76 | DEFINE_string(input, "", "Input compatibility matrix file"); | ||
77 | static bool ValidateInput(const char* /* flagname */, const std::string& value) { | ||
78 | return !value.empty(); | ||
79 | } | ||
80 | DEFINE_validator(input, &ValidateInput); | ||
81 | |||
82 | DEFINE_bool(level, false, "Write level (FCM version) of the compatibility matrix."); | ||
83 | DEFINE_bool(interfaces, false, "Write strings like \"android.hardware.foo@1.0::IFoo\"."); | ||
84 | |||
85 | int main(int argc, char** argv) { | ||
86 | using namespace android::vintf; | ||
87 | |||
88 | gflags::ParseCommandLineFlags(&argc, &argv, true /* remove flags */); | ||
89 | |||
90 | auto mat = readObject(FLAGS_input, gCompatibilityMatrixConverter); | ||
91 | if (!mat) { | ||
92 | return 1; | ||
93 | } | ||
94 | |||
95 | bool written = false; | ||
96 | |||
97 | if (FLAGS_level) { | ||
98 | if (mat->level() == Level::UNSPECIFIED) { | ||
99 | LOG(WARNING) << "FCM version is unspecified."; | ||
100 | } | ||
101 | std::cout << mat->level() << std::endl; | ||
102 | |||
103 | written = true; | ||
104 | } | ||
105 | |||
106 | if (FLAGS_interfaces) { | ||
107 | auto pvs = getInterfaces(*mat); | ||
108 | if (!pvs) { | ||
109 | return 1; | ||
110 | } | ||
111 | if (pvs->empty()) { | ||
112 | LOG(WARNING) << "No package and versions are found."; | ||
113 | } | ||
114 | |||
115 | for (const auto& pv : *pvs) { | ||
116 | std::cout << pv << std::endl; | ||
117 | } | ||
118 | |||
119 | written = true; | ||
120 | } | ||
121 | |||
122 | if (!written) { | ||
123 | LOG(ERROR) << "No output format is set."; | ||
124 | return 1; | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
diff --git a/analyze_matrix/dump_hals_for_release.py b/analyze_matrix/dump_hals_for_release.py new file mode 100755 index 0000000..47519c6 --- /dev/null +++ b/analyze_matrix/dump_hals_for_release.py | |||
@@ -0,0 +1,110 @@ | |||
1 | #!/usr/bin/env python | ||
2 | # | ||
3 | # Copyright (C) 2018 The Android Open Source Project | ||
4 | # | ||
5 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | # you may not use this file except in compliance with the License. | ||
7 | # You may obtain a copy of the License at | ||
8 | # | ||
9 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | # | ||
11 | # Unless required by applicable law or agreed to in writing, software | ||
12 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | # See the License for the specific language governing permissions and | ||
15 | # limitations under the License. | ||
16 | # | ||
17 | |||
18 | """ | ||
19 | Dump new HIDL types that are introduced in each FCM version. | ||
20 | """ | ||
21 | |||
22 | from __future__ import print_function | ||
23 | |||
24 | import argparse | ||
25 | import collections | ||
26 | import json | ||
27 | import os | ||
28 | import subprocess | ||
29 | import sys | ||
30 | |||
31 | class Globals: | ||
32 | pass | ||
33 | |||
34 | def call(args): | ||
35 | if Globals.verbose: | ||
36 | print(' '.join(args), file=sys.stderr) | ||
37 | sp = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
38 | out, err = sp.communicate() | ||
39 | return sp.returncode, out.decode(), err.decode() | ||
40 | |||
41 | def check_call(args): | ||
42 | r, o, e = call(args) | ||
43 | assert not r, '`{}` returns {}'.format(' '.join(args), r) | ||
44 | return o, e | ||
45 | |||
46 | def lines(o): | ||
47 | return filter(lambda line: line, (line.strip() for line in o.split())) | ||
48 | |||
49 | def to_level(s): | ||
50 | if s == 'legacy': return 0 | ||
51 | if s == '': return float('inf') | ||
52 | return int(s) | ||
53 | |||
54 | def main(): | ||
55 | parser = argparse.ArgumentParser(description=__doc__) | ||
56 | parser.add_argument('--verbose', help='Verbose mode', action='store_true') | ||
57 | parser.add_argument('--pretty', help='Print pretty JSON', action='store_true') | ||
58 | parser.add_argument('--hidl-gen', help='Location of hidl-gen', required=True) | ||
59 | parser.add_argument('--analyze-matrix', help='Location of analyze_matrix', required=True) | ||
60 | parser.add_argument('--compatibility-matrix', metavar='FILE', | ||
61 | help='Location of framework compatibility matrices', nargs='+', required=True) | ||
62 | parser.add_argument('--package-root', metavar='PACKAGE:PATH', | ||
63 | help='package roots provided to hidl-gen, e.g. android.hardware:hardware/interfaces', | ||
64 | nargs='+') | ||
65 | parser.parse_args(namespace=Globals) | ||
66 | |||
67 | interfaces_for_level = dict() | ||
68 | for matrix_path in Globals.compatibility_matrix: | ||
69 | r, o, _ = call([Globals.analyze_matrix, '--input', matrix_path, '--level']) | ||
70 | if r: | ||
71 | # Not a compatibility matrix, ignore | ||
72 | continue | ||
73 | # stderr may contain warning message if level is empty | ||
74 | level = o.strip() | ||
75 | |||
76 | o, _ = check_call([Globals.analyze_matrix, '--input', matrix_path, '--interfaces']) | ||
77 | # stderr may contain warning message if no interfaces | ||
78 | |||
79 | interfaces = list(lines(o)) | ||
80 | if not interfaces: continue | ||
81 | |||
82 | if level not in interfaces_for_level: | ||
83 | interfaces_for_level[level] = set() | ||
84 | |||
85 | # Put top level interfaces | ||
86 | interfaces_for_level[level].update(interfaces) | ||
87 | |||
88 | # Put interfaces referenced by top level interfaces | ||
89 | args = [Globals.hidl_gen, '-Ldependencies'] | ||
90 | if Globals.package_root: | ||
91 | args.append('-R') | ||
92 | for package_root in Globals.package_root: | ||
93 | args.extend(['-r', package_root]) | ||
94 | args.extend(interfaces) | ||
95 | o, e = check_call(args) | ||
96 | assert not e, '`{}` has written to stderr:\n{}'.format(' '.join(args), e) | ||
97 | interfaces_for_level[level].update(lines(o)) | ||
98 | |||
99 | seen_interfaces = set() | ||
100 | new_interfaces_for_level = collections.OrderedDict() | ||
101 | for level, interfaces in sorted(interfaces_for_level.items(), key=lambda tup: to_level(tup[0])): | ||
102 | new_interfaces_for_level[level] = sorted(interfaces - seen_interfaces) | ||
103 | seen_interfaces.update(interfaces) | ||
104 | |||
105 | print(json.dumps(new_interfaces_for_level, | ||
106 | separators=None if Globals.pretty else (',',':'), | ||
107 | indent=4 if Globals.pretty else None)) | ||
108 | |||
109 | if __name__ == '__main__': | ||
110 | main() | ||