| /* | |
| * 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; | |
| } |