blob: 11ecde27f5cdd5481c618b588da998eb5ccc0c57 [file] [log] [blame]
Eric Holkc4239ac2018-09-05 10:43:31 -07001/*
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 Holk42734572018-12-17 15:46:18 -080019#include "android-base/stringprintf.h"
Eric Holkb377e512018-12-21 17:25:38 -080020#include "apk_layout_compiler.h"
Eric Holkdbc36e22018-09-20 12:03:10 -070021#include "dex_builder.h"
Eric Holk42734572018-12-17 15:46:18 -080022#include "dex_layout_compiler.h"
Eric Holkc4239ac2018-09-05 10:43:31 -070023#include "java_lang_builder.h"
Eric Holk42734572018-12-17 15:46:18 -080024#include "layout_validation.h"
Eric Holk3cbf1762018-12-13 16:04:56 -080025#include "tinyxml_layout_parser.h"
Eric Holkc4239ac2018-09-05 10:43:31 -070026#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 Holkdbc36e22018-09-20 12:03:10 -070036namespace {
37
Eric Holkc4239ac2018-09-05 10:43:31 -070038using namespace tinyxml2;
Eric Holk42734572018-12-17 15:46:18 -080039using android::base::StringPrintf;
40using startop::dex::ClassBuilder;
41using startop::dex::DexBuilder;
42using startop::dex::MethodBuilder;
43using startop::dex::Prototype;
44using startop::dex::TypeDescriptor;
Eric Holkddc89902018-12-13 13:23:43 -080045using namespace startop::util;
Eric Holkc4239ac2018-09-05 10:43:31 -070046using std::string;
47
48constexpr char kStdoutFilename[]{"stdout"};
49
Eric Holkb377e512018-12-21 17:25:38 -080050DEFINE_bool(apk, false, "Compile layouts in an APK");
Eric Holkdbc36e22018-09-20 12:03:10 -070051DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
Eric Holk0e0e7dd2019-01-15 10:03:46 -080052DEFINE_int32(infd, -1, "Read input from the given file descriptor");
Eric Holkc4239ac2018-09-05 10:43:31 -070053DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
Eric Holkdbc36e22018-09-20 12:03:10 -070054DEFINE_string(package, "", "The package name for the generated class (required)");
Eric Holkc4239ac2018-09-05 10:43:31 -070055
Eric Holk42734572018-12-17 15:46:18 -080056template <typename Visitor>
57class XmlVisitorAdapter : public XMLVisitor {
Eric Holkc4239ac2018-09-05 10:43:31 -070058 public:
Eric Holk42734572018-12-17 15:46:18 -080059 explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
Eric Holkc4239ac2018-09-05 10:43:31 -070060
61 bool VisitEnter(const XMLDocument& /*doc*/) override {
Eric Holk42734572018-12-17 15:46:18 -080062 visitor_->VisitStartDocument();
Eric Holkc4239ac2018-09-05 10:43:31 -070063 return true;
64 }
65
66 bool VisitExit(const XMLDocument& /*doc*/) override {
Eric Holk42734572018-12-17 15:46:18 -080067 visitor_->VisitEndDocument();
Eric Holkc4239ac2018-09-05 10:43:31 -070068 return true;
69 }
70
71 bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
Eric Holk42734572018-12-17 15:46:18 -080072 visitor_->VisitStartTag(
73 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
74 element.Name()));
Eric Holkc4239ac2018-09-05 10:43:31 -070075 return true;
76 }
77
78 bool VisitExit(const XMLElement& /*element*/) override {
Eric Holk42734572018-12-17 15:46:18 -080079 visitor_->VisitEndTag();
Eric Holkc4239ac2018-09-05 10:43:31 -070080 return true;
81 }
82
83 private:
Eric Holk42734572018-12-17 15:46:18 -080084 Visitor* visitor_;
Eric Holkc4239ac2018-09-05 10:43:31 -070085};
Eric Holkdbc36e22018-09-20 12:03:10 -070086
Eric Holk42734572018-12-17 15:46:18 -080087template <typename Builder>
88void CompileLayout(XMLDocument* xml, Builder* builder) {
89 startop::LayoutCompilerVisitor visitor{builder};
90 XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
91 xml->Accept(&adapter);
92}
93
Eric Holkc4239ac2018-09-05 10:43:31 -070094} // end namespace
95
96int main(int argc, char** argv) {
97 constexpr size_t kProgramName = 0;
98 constexpr size_t kFileNameParam = 1;
Eric Holk0e0e7dd2019-01-15 10:03:46 -080099 constexpr size_t kNumRequiredArgs = 1;
Eric Holkc4239ac2018-09-05 10:43:31 -0700100
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 Holk0e0e7dd2019-01-15 10:03:46 -0800108 if (argc < kNumRequiredArgs || cmd.is_default) {
Eric Holkc4239ac2018-09-05 10:43:31 -0700109 gflags::ShowUsageWithFlags(argv[kProgramName]);
110 return 1;
111 }
112
Eric Holkb377e512018-12-21 17:25:38 -0800113 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 Holk0e0e7dd2019-01-15 10:03:46 -0800121 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 Holkb377e512018-12-21 17:25:38 -0800134 return 0;
135 }
136
Eric Holk0e0e7dd2019-01-15 10:03:46 -0800137 const char* const filename = argv[kFileNameParam];
Eric Holk42734572018-12-17 15:46:18 -0800138 const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
Eric Holkc4239ac2018-09-05 10:43:31 -0700139
140 XMLDocument xml;
141 xml.LoadFile(filename);
142
Eric Holk3cbf1762018-12-13 16:04:56 -0800143 string message{};
144 if (!startop::CanCompileLayout(xml, &message)) {
145 LOG(ERROR) << "Layout not supported: " << message;
146 return 1;
147 }
148
Eric Holk42734572018-12-17 15:46:18 -0800149 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 Holkc4239ac2018-09-05 10:43:31 -0700161
Eric Holk42734572018-12-17 15:46:18 -0800162 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 Holkc4239ac2018-09-05 10:43:31 -0700171 return 0;
Eric Holkdbc36e22018-09-20 12:03:10 -0700172}