• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 #include "android-base/stringprintf.h"
20 #include "apk_layout_compiler.h"
21 #include "dex_builder.h"
22 #include "dex_layout_compiler.h"
23 #include "java_lang_builder.h"
24 #include "layout_validation.h"
25 #include "tinyxml_layout_parser.h"
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 
36 namespace {
37 
38 using namespace tinyxml2;
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;
45 using namespace startop::util;
46 using std::string;
47 
48 constexpr char kStdoutFilename[]{"stdout"};
49 
50 DEFINE_bool(apk, false, "Compile layouts in an APK");
51 DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
52 DEFINE_int32(infd, -1, "Read input from the given file descriptor");
53 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
54 DEFINE_string(package, "", "The package name for the generated class (required)");
55 
56 template <typename Visitor>
57 class XmlVisitorAdapter : public XMLVisitor {
58  public:
XmlVisitorAdapter(Visitor * visitor)59   explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
60 
VisitEnter(const XMLDocument &)61   bool VisitEnter(const XMLDocument& /*doc*/) override {
62     visitor_->VisitStartDocument();
63     return true;
64   }
65 
VisitExit(const XMLDocument &)66   bool VisitExit(const XMLDocument& /*doc*/) override {
67     visitor_->VisitEndDocument();
68     return true;
69   }
70 
VisitEnter(const XMLElement & element,const XMLAttribute *)71   bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
72     visitor_->VisitStartTag(
73         std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
74             element.Name()));
75     return true;
76   }
77 
VisitExit(const XMLElement &)78   bool VisitExit(const XMLElement& /*element*/) override {
79     visitor_->VisitEndTag();
80     return true;
81   }
82 
83  private:
84   Visitor* visitor_;
85 };
86 
87 template <typename Builder>
CompileLayout(XMLDocument * xml,Builder * 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 
94 }  // end namespace
95 
main(int argc,char ** argv)96 int main(int argc, char** argv) {
97   constexpr size_t kProgramName = 0;
98   constexpr size_t kFileNameParam = 1;
99   constexpr size_t kNumRequiredArgs = 1;
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");
108   if (argc < kNumRequiredArgs || cmd.is_default) {
109     gflags::ShowUsageWithFlags(argv[kProgramName]);
110     return 1;
111   }
112 
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) {
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     }
134     return 0;
135   }
136 
137   const char* const filename = argv[kFileNameParam];
138   const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
139 
140   XMLDocument xml;
141   xml.LoadFile(filename);
142 
143   string message{};
144   if (!startop::CanCompileLayout(xml, &message)) {
145     LOG(ERROR) << "Layout not supported: " << message;
146     return 1;
147   }
148 
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();
161 
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   }
171   return 0;
172 }
173