Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 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 "gflags/gflags.h" |
| 18 | |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 19 | #include "android-base/stringprintf.h" |
Eric Holk | b377e51 | 2018-12-21 17:25:38 -0800 | [diff] [blame] | 20 | #include "apk_layout_compiler.h" |
Eric Holk | dbc36e2 | 2018-09-20 12:03:10 -0700 | [diff] [blame] | 21 | #include "dex_builder.h" |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 22 | #include "dex_layout_compiler.h" |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 23 | #include "java_lang_builder.h" |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 24 | #include "layout_validation.h" |
Eric Holk | 3cbf176 | 2018-12-13 16:04:56 -0800 | [diff] [blame] | 25 | #include "tinyxml_layout_parser.h" |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 26 | #include "util.h" |
| 27 | |
| 28 | #include "tinyxml2.h" |
| 29 | |
| 30 | #include <fstream> |
| 31 | #include <iostream> |
| 32 | #include <sstream> |
| 33 | #include <string> |
| 34 | #include <vector> |
| 35 | |
Eric Holk | dbc36e2 | 2018-09-20 12:03:10 -0700 | [diff] [blame] | 36 | namespace { |
| 37 | |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 38 | using namespace tinyxml2; |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 39 | using android::base::StringPrintf; |
| 40 | using startop::dex::ClassBuilder; |
| 41 | using startop::dex::DexBuilder; |
| 42 | using startop::dex::MethodBuilder; |
| 43 | using startop::dex::Prototype; |
| 44 | using startop::dex::TypeDescriptor; |
Eric Holk | ddc8990 | 2018-12-13 13:23:43 -0800 | [diff] [blame] | 45 | using namespace startop::util; |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 46 | using std::string; |
| 47 | |
| 48 | constexpr char kStdoutFilename[]{"stdout"}; |
| 49 | |
Eric Holk | b377e51 | 2018-12-21 17:25:38 -0800 | [diff] [blame] | 50 | DEFINE_bool(apk, false, "Compile layouts in an APK"); |
Eric Holk | dbc36e2 | 2018-09-20 12:03:10 -0700 | [diff] [blame] | 51 | DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); |
Eric Holk | 0e0e7dd | 2019-01-15 10:03:46 -0800 | [diff] [blame] | 52 | DEFINE_int32(infd, -1, "Read input from the given file descriptor"); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 53 | DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); |
Eric Holk | dbc36e2 | 2018-09-20 12:03:10 -0700 | [diff] [blame] | 54 | DEFINE_string(package, "", "The package name for the generated class (required)"); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 55 | |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 56 | template <typename Visitor> |
| 57 | class XmlVisitorAdapter : public XMLVisitor { |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 58 | public: |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 59 | explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {} |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 60 | |
| 61 | bool VisitEnter(const XMLDocument& /*doc*/) override { |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 62 | visitor_->VisitStartDocument(); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 63 | return true; |
| 64 | } |
| 65 | |
| 66 | bool VisitExit(const XMLDocument& /*doc*/) override { |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 67 | visitor_->VisitEndDocument(); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 68 | return true; |
| 69 | } |
| 70 | |
| 71 | bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override { |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 72 | visitor_->VisitStartTag( |
| 73 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes( |
| 74 | element.Name())); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 75 | return true; |
| 76 | } |
| 77 | |
| 78 | bool VisitExit(const XMLElement& /*element*/) override { |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 79 | visitor_->VisitEndTag(); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 80 | return true; |
| 81 | } |
| 82 | |
| 83 | private: |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 84 | Visitor* visitor_; |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 85 | }; |
Eric Holk | dbc36e2 | 2018-09-20 12:03:10 -0700 | [diff] [blame] | 86 | |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 87 | template <typename Builder> |
| 88 | void CompileLayout(XMLDocument* xml, Builder* builder) { |
| 89 | startop::LayoutCompilerVisitor visitor{builder}; |
| 90 | XmlVisitorAdapter<decltype(visitor)> adapter{&visitor}; |
| 91 | xml->Accept(&adapter); |
| 92 | } |
| 93 | |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 94 | } // end namespace |
| 95 | |
| 96 | int main(int argc, char** argv) { |
| 97 | constexpr size_t kProgramName = 0; |
| 98 | constexpr size_t kFileNameParam = 1; |
Eric Holk | 0e0e7dd | 2019-01-15 10:03:46 -0800 | [diff] [blame] | 99 | constexpr size_t kNumRequiredArgs = 1; |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 100 | |
| 101 | gflags::SetUsageMessage( |
| 102 | "Compile XML layout files into equivalent Java language code\n" |
| 103 | "\n" |
| 104 | " example usage: viewcompiler layout.xml --package com.example.androidapp"); |
| 105 | gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); |
| 106 | |
| 107 | gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); |
Eric Holk | 0e0e7dd | 2019-01-15 10:03:46 -0800 | [diff] [blame] | 108 | if (argc < kNumRequiredArgs || cmd.is_default) { |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 109 | gflags::ShowUsageWithFlags(argv[kProgramName]); |
| 110 | return 1; |
| 111 | } |
| 112 | |
Eric Holk | b377e51 | 2018-12-21 17:25:38 -0800 | [diff] [blame] | 113 | const bool is_stdout = FLAGS_out == kStdoutFilename; |
| 114 | |
| 115 | std::ofstream outfile; |
| 116 | if (!is_stdout) { |
| 117 | outfile.open(FLAGS_out); |
| 118 | } |
| 119 | |
| 120 | if (FLAGS_apk) { |
Eric Holk | 0e0e7dd | 2019-01-15 10:03:46 -0800 | [diff] [blame] | 121 | const startop::CompilationTarget target = |
| 122 | FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage; |
| 123 | if (FLAGS_infd >= 0) { |
| 124 | startop::CompileApkLayoutsFd( |
| 125 | android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile); |
| 126 | } else { |
| 127 | if (argc < 2) { |
| 128 | gflags::ShowUsageWithFlags(argv[kProgramName]); |
| 129 | return 1; |
| 130 | } |
| 131 | const char* const filename = argv[kFileNameParam]; |
| 132 | startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile); |
| 133 | } |
Eric Holk | b377e51 | 2018-12-21 17:25:38 -0800 | [diff] [blame] | 134 | return 0; |
| 135 | } |
| 136 | |
Eric Holk | 0e0e7dd | 2019-01-15 10:03:46 -0800 | [diff] [blame] | 137 | const char* const filename = argv[kFileNameParam]; |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 138 | const string layout_name = startop::util::FindLayoutNameFromFilename(filename); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 139 | |
| 140 | XMLDocument xml; |
| 141 | xml.LoadFile(filename); |
| 142 | |
Eric Holk | 3cbf176 | 2018-12-13 16:04:56 -0800 | [diff] [blame] | 143 | string message{}; |
| 144 | if (!startop::CanCompileLayout(xml, &message)) { |
| 145 | LOG(ERROR) << "Layout not supported: " << message; |
| 146 | return 1; |
| 147 | } |
| 148 | |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 149 | if (FLAGS_dex) { |
| 150 | DexBuilder dex_file; |
| 151 | string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str()); |
| 152 | ClassBuilder compiled_view{dex_file.MakeClass(class_name)}; |
| 153 | MethodBuilder method{compiled_view.CreateMethod( |
| 154 | layout_name, |
| 155 | Prototype{TypeDescriptor::FromClassname("android.view.View"), |
| 156 | TypeDescriptor::FromClassname("android.content.Context"), |
| 157 | TypeDescriptor::Int()})}; |
| 158 | startop::DexViewBuilder builder{&method}; |
| 159 | CompileLayout(&xml, &builder); |
| 160 | method.Encode(); |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 161 | |
Eric Holk | 4273457 | 2018-12-17 15:46:18 -0800 | [diff] [blame] | 162 | slicer::MemView image{dex_file.CreateImage()}; |
| 163 | |
| 164 | (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size()); |
| 165 | } else { |
| 166 | // Generate Java language output. |
| 167 | JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile}; |
| 168 | |
| 169 | CompileLayout(&xml, &builder); |
| 170 | } |
Eric Holk | c4239ac | 2018-09-05 10:43:31 -0700 | [diff] [blame] | 171 | return 0; |
Eric Holk | dbc36e2 | 2018-09-20 12:03:10 -0700 | [diff] [blame] | 172 | } |