Home | History | Annotate | Download | only in view_compiler
      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:
     59   explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
     60 
     61   bool VisitEnter(const XMLDocument& /*doc*/) override {
     62     visitor_->VisitStartDocument();
     63     return true;
     64   }
     65 
     66   bool VisitExit(const XMLDocument& /*doc*/) override {
     67     visitor_->VisitEndDocument();
     68     return true;
     69   }
     70 
     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 
     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>
     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 
     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