blob: 871a421cee2d63cb47e679efcb42a8ba1b071e52 [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 Holkc4239ac2018-09-05 10:43:31 -070052DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
Eric Holkdbc36e22018-09-20 12:03:10 -070053DEFINE_string(package, "", "The package name for the generated class (required)");
Eric Holkc4239ac2018-09-05 10:43:31 -070054
Eric Holk42734572018-12-17 15:46:18 -080055template <typename Visitor>
56class XmlVisitorAdapter : public XMLVisitor {
Eric Holkc4239ac2018-09-05 10:43:31 -070057 public:
Eric Holk42734572018-12-17 15:46:18 -080058 explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
Eric Holkc4239ac2018-09-05 10:43:31 -070059
60 bool VisitEnter(const XMLDocument& /*doc*/) override {
Eric Holk42734572018-12-17 15:46:18 -080061 visitor_->VisitStartDocument();
Eric Holkc4239ac2018-09-05 10:43:31 -070062 return true;
63 }
64
65 bool VisitExit(const XMLDocument& /*doc*/) override {
Eric Holk42734572018-12-17 15:46:18 -080066 visitor_->VisitEndDocument();
Eric Holkc4239ac2018-09-05 10:43:31 -070067 return true;
68 }
69
70 bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
Eric Holk42734572018-12-17 15:46:18 -080071 visitor_->VisitStartTag(
72 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
73 element.Name()));
Eric Holkc4239ac2018-09-05 10:43:31 -070074 return true;
75 }
76
77 bool VisitExit(const XMLElement& /*element*/) override {
Eric Holk42734572018-12-17 15:46:18 -080078 visitor_->VisitEndTag();
Eric Holkc4239ac2018-09-05 10:43:31 -070079 return true;
80 }
81
82 private:
Eric Holk42734572018-12-17 15:46:18 -080083 Visitor* visitor_;
Eric Holkc4239ac2018-09-05 10:43:31 -070084};
Eric Holkdbc36e22018-09-20 12:03:10 -070085
Eric Holk42734572018-12-17 15:46:18 -080086template <typename Builder>
87void CompileLayout(XMLDocument* xml, Builder* builder) {
88 startop::LayoutCompilerVisitor visitor{builder};
89 XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
90 xml->Accept(&adapter);
91}
92
Eric Holkc4239ac2018-09-05 10:43:31 -070093} // end namespace
94
95int main(int argc, char** argv) {
96 constexpr size_t kProgramName = 0;
97 constexpr size_t kFileNameParam = 1;
98 constexpr size_t kNumRequiredArgs = 2;
99
100 gflags::SetUsageMessage(
101 "Compile XML layout files into equivalent Java language code\n"
102 "\n"
103 " example usage: viewcompiler layout.xml --package com.example.androidapp");
104 gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
105
106 gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
107 if (argc != kNumRequiredArgs || cmd.is_default) {
108 gflags::ShowUsageWithFlags(argv[kProgramName]);
109 return 1;
110 }
111
112 const char* const filename = argv[kFileNameParam];
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) {
121 startop::CompileApkLayouts(
122 filename,
123 FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage,
124 is_stdout ? std::cout : outfile);
125 return 0;
126 }
127
Eric Holk42734572018-12-17 15:46:18 -0800128 const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
Eric Holkc4239ac2018-09-05 10:43:31 -0700129
130 XMLDocument xml;
131 xml.LoadFile(filename);
132
Eric Holk3cbf1762018-12-13 16:04:56 -0800133 string message{};
134 if (!startop::CanCompileLayout(xml, &message)) {
135 LOG(ERROR) << "Layout not supported: " << message;
136 return 1;
137 }
138
Eric Holk42734572018-12-17 15:46:18 -0800139 if (FLAGS_dex) {
140 DexBuilder dex_file;
141 string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
142 ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
143 MethodBuilder method{compiled_view.CreateMethod(
144 layout_name,
145 Prototype{TypeDescriptor::FromClassname("android.view.View"),
146 TypeDescriptor::FromClassname("android.content.Context"),
147 TypeDescriptor::Int()})};
148 startop::DexViewBuilder builder{&method};
149 CompileLayout(&xml, &builder);
150 method.Encode();
Eric Holkc4239ac2018-09-05 10:43:31 -0700151
Eric Holk42734572018-12-17 15:46:18 -0800152 slicer::MemView image{dex_file.CreateImage()};
153
154 (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
155 } else {
156 // Generate Java language output.
157 JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
158
159 CompileLayout(&xml, &builder);
160 }
Eric Holkc4239ac2018-09-05 10:43:31 -0700161 return 0;
Eric Holkdbc36e22018-09-20 12:03:10 -0700162}