/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "gflags/gflags.h" #include "android-base/stringprintf.h" #include "apk_layout_compiler.h" #include "dex_builder.h" #include "dex_layout_compiler.h" #include "java_lang_builder.h" #include "layout_validation.h" #include "tinyxml_layout_parser.h" #include "util.h" #include "tinyxml2.h" #include <fstream> #include <iostream> #include <sstream> #include <string> #include <vector> namespace { using namespace tinyxml2; using android::base::StringPrintf; using startop::dex::ClassBuilder; using startop::dex::DexBuilder; using startop::dex::MethodBuilder; using startop::dex::Prototype; using startop::dex::TypeDescriptor; using namespace startop::util; using std::string; constexpr char kStdoutFilename[]{"stdout"}; DEFINE_bool(apk, false, "Compile layouts in an APK"); DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); DEFINE_int32(infd, -1, "Read input from the given file descriptor"); DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); DEFINE_string(package, "", "The package name for the generated class (required)"); template <typename Visitor> class XmlVisitorAdapter : public XMLVisitor { public: explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {} bool VisitEnter(const XMLDocument& /*doc*/) override { visitor_->VisitStartDocument(); return true; } bool VisitExit(const XMLDocument& /*doc*/) override { visitor_->VisitEndDocument(); return true; } bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override { visitor_->VisitStartTag( std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes( element.Name())); return true; } bool VisitExit(const XMLElement& /*element*/) override { visitor_->VisitEndTag(); return true; } private: Visitor* visitor_; }; template <typename Builder> void CompileLayout(XMLDocument* xml, Builder* builder) { startop::LayoutCompilerVisitor visitor{builder}; XmlVisitorAdapter<decltype(visitor)> adapter{&visitor}; xml->Accept(&adapter); } } // end namespace int main(int argc, char** argv) { constexpr size_t kProgramName = 0; constexpr size_t kFileNameParam = 1; constexpr size_t kNumRequiredArgs = 1; gflags::SetUsageMessage( "Compile XML layout files into equivalent Java language code\n" "\n" " example usage: viewcompiler layout.xml --package com.example.androidapp"); gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); if (argc < kNumRequiredArgs || cmd.is_default) { gflags::ShowUsageWithFlags(argv[kProgramName]); return 1; } const bool is_stdout = FLAGS_out == kStdoutFilename; std::ofstream outfile; if (!is_stdout) { outfile.open(FLAGS_out); } if (FLAGS_apk) { const startop::CompilationTarget target = FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage; if (FLAGS_infd >= 0) { startop::CompileApkLayoutsFd( android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile); } else { if (argc < 2) { gflags::ShowUsageWithFlags(argv[kProgramName]); return 1; } const char* const filename = argv[kFileNameParam]; startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile); } return 0; } const char* const filename = argv[kFileNameParam]; const string layout_name = startop::util::FindLayoutNameFromFilename(filename); XMLDocument xml; xml.LoadFile(filename); string message{}; if (!startop::CanCompileLayout(xml, &message)) { LOG(ERROR) << "Layout not supported: " << message; return 1; } if (FLAGS_dex) { DexBuilder dex_file; string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str()); ClassBuilder compiled_view{dex_file.MakeClass(class_name)}; MethodBuilder method{compiled_view.CreateMethod( layout_name, Prototype{TypeDescriptor::FromClassname("android.view.View"), TypeDescriptor::FromClassname("android.content.Context"), TypeDescriptor::Int()})}; startop::DexViewBuilder builder{&method}; CompileLayout(&xml, &builder); method.Encode(); slicer::MemView image{dex_file.CreateImage()}; (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size()); } else { // Generate Java language output. JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile}; CompileLayout(&xml, &builder); } return 0; }