blob: ae00187e1908d0224116cb8bf01a11c0dc0b4507 [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 Holkdbc36e22018-09-20 12:03:10 -070020#include "dex_builder.h"
Eric Holk42734572018-12-17 15:46:18 -080021#include "dex_layout_compiler.h"
Eric Holkc4239ac2018-09-05 10:43:31 -070022#include "java_lang_builder.h"
Eric Holk42734572018-12-17 15:46:18 -080023#include "layout_validation.h"
Eric Holk3cbf1762018-12-13 16:04:56 -080024#include "tinyxml_layout_parser.h"
Eric Holkc4239ac2018-09-05 10:43:31 -070025#include "util.h"
26
27#include "tinyxml2.h"
28
29#include <fstream>
30#include <iostream>
31#include <sstream>
32#include <string>
33#include <vector>
34
Eric Holkdbc36e22018-09-20 12:03:10 -070035namespace {
36
Eric Holkc4239ac2018-09-05 10:43:31 -070037using namespace tinyxml2;
Eric Holk42734572018-12-17 15:46:18 -080038using android::base::StringPrintf;
39using startop::dex::ClassBuilder;
40using startop::dex::DexBuilder;
41using startop::dex::MethodBuilder;
42using startop::dex::Prototype;
43using startop::dex::TypeDescriptor;
Eric Holkddc89902018-12-13 13:23:43 -080044using namespace startop::util;
Eric Holkc4239ac2018-09-05 10:43:31 -070045using std::string;
46
47constexpr char kStdoutFilename[]{"stdout"};
48
Eric Holkdbc36e22018-09-20 12:03:10 -070049DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
Eric Holkc4239ac2018-09-05 10:43:31 -070050DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
Eric Holkdbc36e22018-09-20 12:03:10 -070051DEFINE_string(package, "", "The package name for the generated class (required)");
Eric Holkc4239ac2018-09-05 10:43:31 -070052
Eric Holk42734572018-12-17 15:46:18 -080053template <typename Visitor>
54class XmlVisitorAdapter : public XMLVisitor {
Eric Holkc4239ac2018-09-05 10:43:31 -070055 public:
Eric Holk42734572018-12-17 15:46:18 -080056 explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
Eric Holkc4239ac2018-09-05 10:43:31 -070057
58 bool VisitEnter(const XMLDocument& /*doc*/) override {
Eric Holk42734572018-12-17 15:46:18 -080059 visitor_->VisitStartDocument();
Eric Holkc4239ac2018-09-05 10:43:31 -070060 return true;
61 }
62
63 bool VisitExit(const XMLDocument& /*doc*/) override {
Eric Holk42734572018-12-17 15:46:18 -080064 visitor_->VisitEndDocument();
Eric Holkc4239ac2018-09-05 10:43:31 -070065 return true;
66 }
67
68 bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
Eric Holk42734572018-12-17 15:46:18 -080069 visitor_->VisitStartTag(
70 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
71 element.Name()));
Eric Holkc4239ac2018-09-05 10:43:31 -070072 return true;
73 }
74
75 bool VisitExit(const XMLElement& /*element*/) override {
Eric Holk42734572018-12-17 15:46:18 -080076 visitor_->VisitEndTag();
Eric Holkc4239ac2018-09-05 10:43:31 -070077 return true;
78 }
79
80 private:
Eric Holk42734572018-12-17 15:46:18 -080081 Visitor* visitor_;
Eric Holkc4239ac2018-09-05 10:43:31 -070082};
Eric Holkdbc36e22018-09-20 12:03:10 -070083
Eric Holk42734572018-12-17 15:46:18 -080084template <typename Builder>
85void CompileLayout(XMLDocument* xml, Builder* builder) {
86 startop::LayoutCompilerVisitor visitor{builder};
87 XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
88 xml->Accept(&adapter);
89}
90
Eric Holkc4239ac2018-09-05 10:43:31 -070091} // end namespace
92
93int main(int argc, char** argv) {
94 constexpr size_t kProgramName = 0;
95 constexpr size_t kFileNameParam = 1;
96 constexpr size_t kNumRequiredArgs = 2;
97
98 gflags::SetUsageMessage(
99 "Compile XML layout files into equivalent Java language code\n"
100 "\n"
101 " example usage: viewcompiler layout.xml --package com.example.androidapp");
102 gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
103
104 gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
105 if (argc != kNumRequiredArgs || cmd.is_default) {
106 gflags::ShowUsageWithFlags(argv[kProgramName]);
107 return 1;
108 }
109
110 const char* const filename = argv[kFileNameParam];
Eric Holk42734572018-12-17 15:46:18 -0800111 const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
Eric Holkc4239ac2018-09-05 10:43:31 -0700112
113 XMLDocument xml;
114 xml.LoadFile(filename);
115
Eric Holk3cbf1762018-12-13 16:04:56 -0800116 string message{};
117 if (!startop::CanCompileLayout(xml, &message)) {
118 LOG(ERROR) << "Layout not supported: " << message;
119 return 1;
120 }
121
Eric Holk42734572018-12-17 15:46:18 -0800122 const bool is_stdout = FLAGS_out == kStdoutFilename;
123
Eric Holkc4239ac2018-09-05 10:43:31 -0700124 std::ofstream outfile;
Eric Holk42734572018-12-17 15:46:18 -0800125 if (!is_stdout) {
Eric Holkc4239ac2018-09-05 10:43:31 -0700126 outfile.open(FLAGS_out);
127 }
Eric Holkc4239ac2018-09-05 10:43:31 -0700128
Eric Holk42734572018-12-17 15:46:18 -0800129 if (FLAGS_dex) {
130 DexBuilder dex_file;
131 string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
132 ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
133 MethodBuilder method{compiled_view.CreateMethod(
134 layout_name,
135 Prototype{TypeDescriptor::FromClassname("android.view.View"),
136 TypeDescriptor::FromClassname("android.content.Context"),
137 TypeDescriptor::Int()})};
138 startop::DexViewBuilder builder{&method};
139 CompileLayout(&xml, &builder);
140 method.Encode();
Eric Holkc4239ac2018-09-05 10:43:31 -0700141
Eric Holk42734572018-12-17 15:46:18 -0800142 slicer::MemView image{dex_file.CreateImage()};
143
144 (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
145 } else {
146 // Generate Java language output.
147 JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
148
149 CompileLayout(&xml, &builder);
150 }
Eric Holkc4239ac2018-09-05 10:43:31 -0700151 return 0;
Eric Holkdbc36e22018-09-20 12:03:10 -0700152}