diff options
author | Eric Holk <eholk@google.com> | 2019-01-08 10:28:53 -0800 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-01-08 10:28:53 -0800 |
commit | 72ffe3b785f3f9e22bc0469c6cbadb12fb2532eb (patch) | |
tree | f2879c7426833e52003aba647f60e8ca34ffc7ad /startop/view_compiler | |
parent | 9af5a323daf20e607750093a24faac3c8bd8667e (diff) | |
parent | 0ca2e2c2dde2dfe9610c00592d289cd5e7f5c025 (diff) |
Merge "[view compiler] Compile all layouts in an APK" am: a95eac0c59
am: 0ca2e2c2dd
Change-Id: I9106f0893aef1569b7ba6b9944cc2d7b8081ee94
Diffstat (limited to 'startop/view_compiler')
-rw-r--r-- | startop/view_compiler/Android.bp | 18 | ||||
-rw-r--r-- | startop/view_compiler/apk_layout_compiler.cc | 159 | ||||
-rw-r--r-- | startop/view_compiler/apk_layout_compiler.h | 31 | ||||
-rw-r--r-- | startop/view_compiler/main.cc | 24 |
4 files changed, 225 insertions, 7 deletions
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 82056e9a33fe..2fc3a0dd874e 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -22,17 +22,35 @@ cc_defaults { shared_libs: [ "libbase", "libdexfile", + "libz", "slicer", ], static_libs: [ "libtinyxml2", + "liblog", + "libutils", + "libziparchive", ], + cppflags: ["-std=c++17"], + target: { + android: { + shared_libs: [ + "libandroidfw", + ], + }, + host: { + static_libs: [ + "libandroidfw", + ], + }, + }, } cc_library_host_static { name: "libviewcompiler", defaults: ["viewcompiler_defaults"], srcs: [ + "apk_layout_compiler.cc", "dex_builder.cc", "dex_layout_compiler.cc", "java_lang_builder.cc", diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc new file mode 100644 index 000000000000..e95041ba34a4 --- /dev/null +++ b/startop/view_compiler/apk_layout_compiler.cc @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "apk_layout_compiler.h" +#include "dex_layout_compiler.h" +#include "java_lang_builder.h" +#include "layout_validation.h" +#include "util.h" + +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" + +#include <iostream> +#include <locale> + +#include "android-base/stringprintf.h" + +namespace startop { + +using android::ResXMLParser; +using android::base::StringPrintf; + +class ResXmlVisitorAdapter { + public: + ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {} + + template <typename Visitor> + void Accept(Visitor* visitor) { + size_t depth{0}; + do { + switch (parser_->next()) { + case ResXMLParser::START_DOCUMENT: + depth++; + visitor->VisitStartDocument(); + break; + case ResXMLParser::END_DOCUMENT: + depth--; + visitor->VisitEndDocument(); + break; + case ResXMLParser::START_TAG: { + depth++; + size_t name_length = 0; + const char16_t* name = parser_->getElementName(&name_length); + visitor->VisitStartTag(std::u16string{name, name_length}); + break; + } + case ResXMLParser::END_TAG: + depth--; + visitor->VisitEndTag(); + break; + default:; + } + } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE); + } + + private: + ResXMLParser* parser_; +}; + +bool CanCompileLayout(ResXMLParser* parser) { + ResXmlVisitorAdapter adapter{parser}; + LayoutValidationVisitor visitor; + adapter.Accept(&visitor); + + return visitor.can_compile(); +} + +void CompileApkLayouts(const std::string& filename, CompilationTarget target, + std::ostream& target_out) { + auto assets = android::ApkAssets::Load(filename); + android::AssetManager2 resources; + resources.SetApkAssets({assets.get()}); + + std::string package_name; + + // TODO: handle multiple packages better + bool first = true; + for (const auto& package : assets->GetLoadedArsc()->GetPackages()) { + CHECK(first); + package_name = package->GetPackageName(); + first = false; + } + + dex::DexBuilder dex_file; + dex::ClassBuilder compiled_view{ + dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))}; + std::vector<dex::MethodBuilder> methods; + + assets->ForEachFile("res/", [&](const android::StringPiece& s, android::FileType) { + if (s == "layout") { + auto path = StringPrintf("res/%s/", s.to_string().c_str()); + assets->ForEachFile(path, [&](const android::StringPiece& layout_file, android::FileType) { + auto layout_path = StringPrintf("%s%s", path.c_str(), layout_file.to_string().c_str()); + android::ApkAssetsCookie cookie = android::kInvalidCookie; + auto asset = resources.OpenNonAsset(layout_path, android::Asset::ACCESS_RANDOM, &cookie); + CHECK(asset); + CHECK(android::kInvalidCookie != cookie); + const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie); + CHECK(nullptr != dynamic_ref_table); + android::ResXMLTree xml_tree{dynamic_ref_table}; + xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true), + asset->getLength(), + /*copy_data=*/true); + android::ResXMLParser parser{xml_tree}; + parser.restart(); + if (CanCompileLayout(&parser)) { + parser.restart(); + const std::string layout_name = startop::util::FindLayoutNameFromFilename(layout_path); + ResXmlVisitorAdapter adapter{&parser}; + switch (target) { + case CompilationTarget::kDex: { + methods.push_back(compiled_view.CreateMethod( + layout_name, + dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"), + dex::TypeDescriptor::FromClassname("android.content.Context"), + dex::TypeDescriptor::Int()})); + DexViewBuilder builder(&methods.back()); + builder.Start(); + LayoutCompilerVisitor visitor{&builder}; + adapter.Accept(&visitor); + builder.Finish(); + methods.back().Encode(); + break; + } + case CompilationTarget::kJavaLanguage: { + JavaLangViewBuilder builder{package_name, layout_name, target_out}; + builder.Start(); + LayoutCompilerVisitor visitor{&builder}; + adapter.Accept(&visitor); + builder.Finish(); + break; + } + } + } + }); + } + }); + + if (target == CompilationTarget::kDex) { + slicer::MemView image{dex_file.CreateImage()}; + target_out.write(image.ptr<const char>(), image.size()); + } +} + +} // namespace startop diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h new file mode 100644 index 000000000000..c85ddd65ac1b --- /dev/null +++ b/startop/view_compiler/apk_layout_compiler.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef APK_LAYOUT_COMPILER_H_ +#define APK_LAYOUT_COMPILER_H_ + +#include <string> + +namespace startop { + +enum class CompilationTarget { kJavaLanguage, kDex }; + +void CompileApkLayouts(const std::string& filename, CompilationTarget target, + std::ostream& target_out); + +} // namespace startop + +#endif // APK_LAYOUT_COMPILER_H_
\ No newline at end of file diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc index ae00187e1908..871a421cee2d 100644 --- a/startop/view_compiler/main.cc +++ b/startop/view_compiler/main.cc @@ -17,6 +17,7 @@ #include "gflags/gflags.h" #include "android-base/stringprintf.h" +#include "apk_layout_compiler.h" #include "dex_builder.h" #include "dex_layout_compiler.h" #include "java_lang_builder.h" @@ -46,6 +47,7 @@ using std::string; constexpr char kStdoutFilename[]{"stdout"}; +DEFINE_bool(apk, false, "Compile layouts in an APK"); DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); DEFINE_string(package, "", "The package name for the generated class (required)"); @@ -108,6 +110,21 @@ int main(int argc, char** argv) { } const char* const filename = argv[kFileNameParam]; + const bool is_stdout = FLAGS_out == kStdoutFilename; + + std::ofstream outfile; + if (!is_stdout) { + outfile.open(FLAGS_out); + } + + if (FLAGS_apk) { + startop::CompileApkLayouts( + filename, + FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage, + is_stdout ? std::cout : outfile); + return 0; + } + const string layout_name = startop::util::FindLayoutNameFromFilename(filename); XMLDocument xml; @@ -119,13 +136,6 @@ int main(int argc, char** argv) { return 1; } - const bool is_stdout = FLAGS_out == kStdoutFilename; - - std::ofstream outfile; - if (!is_stdout) { - outfile.open(FLAGS_out); - } - if (FLAGS_dex) { DexBuilder dex_file; string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str()); |