This is needed to be able to launch the viewcompiler from installd. We only support FD-input mode when reading from APKs. For output to an FD, we rely on stdout redirection. Bug: 111895153 Change-Id: I3025d83c60494485bada5f2f4cd67e25354d1d53
173 lines
5.2 KiB
C++
173 lines
5.2 KiB
C++
/*
|
|
* 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;
|
|
}
|