summaryrefslogtreecommitdiff
path: root/tools/aapt2/java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2/java')
-rw-r--r--tools/aapt2/java/AnnotationProcessor.cpp73
-rw-r--r--tools/aapt2/java/AnnotationProcessor.h77
-rw-r--r--tools/aapt2/java/AnnotationProcessor_test.cpp35
-rw-r--r--tools/aapt2/java/ClassDefinition.cpp66
-rw-r--r--tools/aapt2/java/ClassDefinition.h62
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp241
-rw-r--r--tools/aapt2/java/JavaClassGenerator.h25
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp227
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.cpp25
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.h3
-rw-r--r--tools/aapt2/java/ManifestClassGenerator_test.cpp25
-rw-r--r--tools/aapt2/java/ProguardRules.cpp187
-rw-r--r--tools/aapt2/java/ProguardRules.h70
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp137
14 files changed, 796 insertions, 457 deletions
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 1f83fa098d74..8d91b0098c1f 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -17,11 +17,13 @@
#include "java/AnnotationProcessor.h"
#include <algorithm>
+#include <array>
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/Util.h"
+using ::aapt::text::Printer;
using ::aapt::text::Utf8Iterator;
using ::android::StringPiece;
@@ -41,30 +43,54 @@ StringPiece AnnotationProcessor::ExtractFirstSentence(const StringPiece& comment
return comment;
}
-void AnnotationProcessor::AppendCommentLine(std::string& comment) {
+struct AnnotationRule {
+ enum : uint32_t {
+ kDeprecated = 0x01,
+ kSystemApi = 0x02,
+ kTestApi = 0x04,
+ };
+
+ StringPiece doc_str;
+ uint32_t bit_mask;
+ StringPiece annotation;
+};
+
+static std::array<AnnotationRule, 2> sAnnotationRules = {{
+ {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"},
+ {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"},
+}};
+
+void AnnotationProcessor::AppendCommentLine(std::string comment) {
static const std::string sDeprecated = "@deprecated";
- static const std::string sSystemApi = "@SystemApi";
+ // Treat deprecated specially, since we don't remove it from the source comment.
if (comment.find(sDeprecated) != std::string::npos) {
- annotation_bit_mask_ |= kDeprecated;
+ annotation_bit_mask_ |= AnnotationRule::kDeprecated;
}
- std::string::size_type idx = comment.find(sSystemApi);
- if (idx != std::string::npos) {
- annotation_bit_mask_ |= kSystemApi;
- comment.erase(comment.begin() + idx,
- comment.begin() + idx + sSystemApi.size());
+ for (const AnnotationRule& rule : sAnnotationRules) {
+ std::string::size_type idx = comment.find(rule.doc_str.data());
+ if (idx != std::string::npos) {
+ annotation_bit_mask_ |= rule.bit_mask;
+ comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
+ }
}
- if (util::TrimWhitespace(comment).empty()) {
+ // Check if after removal of annotations the line is empty.
+ const StringPiece trimmed = util::TrimWhitespace(comment);
+ if (trimmed.empty()) {
return;
}
+ // If there was trimming to do, copy the string.
+ if (trimmed.size() != comment.size()) {
+ comment = trimmed.to_string();
+ }
+
if (!has_comments_) {
has_comments_ = true;
comment_ << "/**";
}
-
comment_ << "\n * " << std::move(comment);
}
@@ -73,31 +99,34 @@ void AnnotationProcessor::AppendComment(const StringPiece& comment) {
for (StringPiece line : util::Tokenize(comment, '\n')) {
line = util::TrimWhitespace(line);
if (!line.empty()) {
- std::string lineCopy = line.to_string();
- AppendCommentLine(lineCopy);
+ AppendCommentLine(line.to_string());
}
}
}
-void AnnotationProcessor::AppendNewLine() { comment_ << "\n *"; }
+void AnnotationProcessor::AppendNewLine() {
+ if (has_comments_) {
+ comment_ << "\n *";
+ }
+}
-void AnnotationProcessor::WriteToStream(std::ostream* out,
- const StringPiece& prefix) const {
+void AnnotationProcessor::Print(Printer* printer) const {
if (has_comments_) {
std::string result = comment_.str();
for (StringPiece line : util::Tokenize(result, '\n')) {
- *out << prefix << line << "\n";
+ printer->Println(line);
}
- *out << prefix << " */"
- << "\n";
+ printer->Println(" */");
}
- if (annotation_bit_mask_ & kDeprecated) {
- *out << prefix << "@Deprecated\n";
+ if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
+ printer->Println("@Deprecated");
}
- if (annotation_bit_mask_ & kSystemApi) {
- *out << prefix << "@android.annotation.SystemApi\n";
+ for (const AnnotationRule& rule : sAnnotationRules) {
+ if (annotation_bit_mask_ & rule.bit_mask) {
+ printer->Println(rule.annotation);
+ }
}
}
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index a06eda0f9c5c..ae7bdb0c3ae2 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -22,66 +22,57 @@
#include "androidfw/StringPiece.h"
+#include "text/Printer.h"
+
namespace aapt {
-/**
- * Builds a JavaDoc comment from a set of XML comments.
- * This will also look for instances of @SystemApi and convert them to
- * actual Java annotations.
- *
- * Example:
- *
- * Input XML:
- *
- * <!-- This is meant to be hidden because
- * It is system api. Also it is @deprecated
- * @SystemApi
- * -->
- *
- * Output JavaDoc:
- *
- * /\*
- * * This is meant to be hidden because
- * * It is system api. Also it is @deprecated
- * *\/
- *
- * Output Annotations:
- *
- * @Deprecated
- * @android.annotation.SystemApi
- *
- */
+// Builds a JavaDoc comment from a set of XML comments.
+// This will also look for instances of @SystemApi and convert them to
+// actual Java annotations.
+//
+// Example:
+//
+// Input XML:
+//
+// <!-- This is meant to be hidden because
+// It is system api. Also it is @deprecated
+// @SystemApi
+// -->
+//
+// Output JavaDoc:
+//
+// /**
+// * This is meant to be hidden because
+// * It is system api. Also it is @deprecated
+// */
+//
+// Output Annotations:
+//
+// @Deprecated
+// @android.annotation.SystemApi
class AnnotationProcessor {
public:
+ // Extracts the first sentence of a comment. The algorithm selects the substring starting from
+ // the beginning of the string, and ending at the first '.' character that is followed by a
+ // whitespace character. If these requirements are not met, the whole string is returned.
static android::StringPiece ExtractFirstSentence(const android::StringPiece& comment);
- /**
- * Adds more comments. Since resources can have various values with different
- * configurations,
- * we need to collect all the comments.
- */
+ // Adds more comments. Resources can have value definitions for various configurations, and
+ // each of the definitions may have comments that need to be processed.
void AppendComment(const android::StringPiece& comment);
void AppendNewLine();
- /**
- * Writes the comments and annotations to the stream, with the given prefix
- * before each line.
- */
- void WriteToStream(std::ostream* out, const android::StringPiece& prefix) const;
+ // Writes the comments and annotations to the Printer.
+ void Print(text::Printer* printer) const;
private:
- enum : uint32_t {
- kDeprecated = 0x01,
- kSystemApi = 0x02,
- };
-
std::stringstream comment_;
std::stringstream mAnnotations;
bool has_comments_ = false;
uint32_t annotation_bit_mask_ = 0;
- void AppendCommentLine(std::string& line);
+ void AppendCommentLine(std::string line);
};
} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 9ccac8888426..69f49c8b97c3 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -16,8 +16,12 @@
#include "java/AnnotationProcessor.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+#include "text/Printer.h"
+using ::aapt::io::StringOutputStream;
+using ::aapt::text::Printer;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Not;
@@ -33,9 +37,11 @@ TEST(AnnotationProcessorTest, EmitsDeprecated) {
AnnotationProcessor processor;
processor.AppendComment(comment);
- std::stringstream result;
- processor.WriteToStream(&result, "");
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
}
@@ -44,15 +50,32 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.AppendComment("@SystemApi This is a system API");
- std::stringstream result;
- processor.WriteToStream(&result, "");
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
EXPECT_THAT(annotations, Not(HasSubstr("@SystemApi")));
EXPECT_THAT(annotations, HasSubstr("This is a system API"));
}
+TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) {
+ AnnotationProcessor processor;
+ processor.AppendComment("@TestApi This is a test API");
+
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
+
+ EXPECT_THAT(annotations, HasSubstr("@android.annotation.TestApi"));
+ EXPECT_THAT(annotations, Not(HasSubstr("@TestApi")));
+ EXPECT_THAT(annotations, HasSubstr("This is a test API"));
+}
+
TEST(AnnotationProcessor, ExtractsFirstSentence) {
EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence("This is the only sentence"),
Eq("This is the only sentence"));
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index c139b73db296..f5f5b05491bb 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -18,33 +18,45 @@
#include "androidfw/StringPiece.h"
-using android::StringPiece;
+using ::aapt::text::Printer;
+using ::android::StringPiece;
namespace aapt {
-void ClassMember::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
- processor_.WriteToStream(out, prefix);
+void ClassMember::Print(bool /*final*/, Printer* printer) const {
+ processor_.Print(printer);
}
void MethodDefinition::AppendStatement(const StringPiece& statement) {
statements_.push_back(statement.to_string());
}
-void MethodDefinition::WriteToStream(const StringPiece& prefix, bool final,
- std::ostream* out) const {
- *out << prefix << signature_ << " {\n";
+void MethodDefinition::Print(bool final, Printer* printer) const {
+ printer->Print(signature_).Println(" {");
+ printer->Indent();
for (const auto& statement : statements_) {
- *out << prefix << " " << statement << "\n";
+ printer->Println(statement);
}
- *out << prefix << "}";
+ printer->Undent();
+ printer->Print("}");
}
ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) {
Result result = Result::kAdded;
auto iter = indexed_members_.find(member->GetName());
if (iter != indexed_members_.end()) {
- // Overwrite the entry.
- ordered_members_[iter->second].reset();
+ // Overwrite the entry. Be careful, as the key in indexed_members_ is actually memory owned
+ // by the value at ordered_members_[index]. Since overwriting a value for a key doesn't replace
+ // the key (the initial key inserted into the unordered_map is kept), we must erase and then
+ // insert a new key, whose memory is being kept around. We do all this to avoid using more
+ // memory for each key.
+ size_t index = iter->second;
+
+ // Erase the key + value from the map.
+ indexed_members_.erase(iter);
+
+ // Now clear the memory that was backing the key (now erased).
+ ordered_members_[index].reset();
result = Result::kOverridden;
}
@@ -62,34 +74,32 @@ bool ClassDefinition::empty() const {
return true;
}
-void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
- std::ostream* out) const {
+void ClassDefinition::Print(bool final, Printer* printer) const {
if (empty() && !create_if_empty_) {
return;
}
- ClassMember::WriteToStream(prefix, final, out);
+ ClassMember::Print(final, printer);
- *out << prefix << "public ";
+ printer->Print("public ");
if (qualifier_ == ClassQualifier::kStatic) {
- *out << "static ";
+ printer->Print("static ");
}
- *out << "final class " << name_ << " {\n";
-
- std::string new_prefix = prefix.to_string();
- new_prefix.append(kIndent);
+ printer->Print("final class ").Print(name_).Println(" {");
+ printer->Indent();
for (const std::unique_ptr<ClassMember>& member : ordered_members_) {
// There can be nullptr members when a member is added to the ClassDefinition
// and takes precedence over a previous member with the same name. The overridden member is
// set to nullptr.
if (member != nullptr) {
- member->WriteToStream(new_prefix, final, out);
- *out << "\n";
+ member->Print(final, printer);
+ printer->Println();
}
}
- *out << prefix << "}";
+ printer->Undent();
+ printer->Print("}");
}
constexpr static const char* sWarningHeader =
@@ -100,12 +110,12 @@ constexpr static const char* sWarningHeader =
" * should not be modified by hand.\n"
" */\n\n";
-bool ClassDefinition::WriteJavaFile(const ClassDefinition* def,
- const StringPiece& package, bool final,
- std::ostream* out) {
- *out << sWarningHeader << "package " << package << ";\n\n";
- def->WriteToStream("", final, out);
- return bool(*out);
+void ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
+ bool final, io::OutputStream* out) {
+ Printer printer(out);
+ printer.Print(sWarningHeader).Print("package ").Print(package).Println(";");
+ printer.Println();
+ def->Print(final, &printer);
}
} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 28a3489e71a4..fb11266f1761 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -17,7 +17,6 @@
#ifndef AAPT_JAVA_CLASSDEFINITION_H
#define AAPT_JAVA_CLASSDEFINITION_H
-#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -27,6 +26,7 @@
#include "Resource.h"
#include "java/AnnotationProcessor.h"
+#include "text/Printer.h"
#include "util/Util.h"
namespace aapt {
@@ -47,11 +47,10 @@ class ClassMember {
virtual const std::string& GetName() const = 0;
- // Writes the class member to the out stream. Subclasses should derive this method
+ // Writes the class member to the Printer. Subclasses should derive this method
// to write their own data. Call this base method from the subclass to write out
// this member's comments/annotations.
- virtual void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const;
+ virtual void Print(bool final, text::Printer* printer) const;
private:
AnnotationProcessor processor_;
@@ -71,11 +70,16 @@ class PrimitiveMember : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "") << "int " << name_ << "=" << val_
- << ";";
+ void Print(bool final, text::Printer* printer) const override {
+ using std::to_string;
+
+ ClassMember::Print(final, printer);
+
+ printer->Print("public static ");
+ if (final) {
+ printer->Print("final ");
+ }
+ printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
}
private:
@@ -100,12 +104,14 @@ class PrimitiveMember<std::string> : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
+ void Print(bool final, text::Printer* printer) const override {
+ ClassMember::Print(final, printer);
- *out << prefix << "public static " << (final ? "final " : "") << "String "
- << name_ << "=\"" << val_ << "\";";
+ printer->Print("public static ");
+ if (final) {
+ printer->Print("final ");
+ }
+ printer->Print("String ").Print(name_).Print("=\"").Print(val_).Print("\";");
}
private:
@@ -136,25 +142,27 @@ class PrimitiveArrayMember : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
+ void Print(bool final, text::Printer* printer) const override {
+ ClassMember::Print(final, printer);
- *out << prefix << "public static final int[] " << name_ << "={";
+ printer->Print("public static final int[] ").Print(name_).Print("={");
+ printer->Indent();
const auto begin = elements_.begin();
const auto end = elements_.end();
for (auto current = begin; current != end; ++current) {
if (std::distance(begin, current) % kAttribsPerLine == 0) {
- *out << "\n" << prefix << kIndent << kIndent;
+ printer->Println();
}
- *out << *current;
+ printer->Print(to_string(*current));
if (std::distance(current, end) > 1) {
- *out << ", ";
+ printer->Print(", ");
}
}
- *out << "\n" << prefix << kIndent << "};";
+ printer->Println();
+ printer->Undent();
+ printer->Print("};");
}
private:
@@ -187,8 +195,7 @@ class MethodDefinition : public ClassMember {
return false;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override;
+ void Print(bool final, text::Printer* printer) const override;
private:
DISALLOW_COPY_AND_ASSIGN(MethodDefinition);
@@ -201,8 +208,8 @@ enum class ClassQualifier { kNone, kStatic };
class ClassDefinition : public ClassMember {
public:
- static bool WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
- bool final, std::ostream* out);
+ static void WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
+ bool final, io::OutputStream* out);
ClassDefinition(const android::StringPiece& name, ClassQualifier qualifier, bool createIfEmpty)
: name_(name.to_string()), qualifier_(qualifier), create_if_empty_(createIfEmpty) {}
@@ -220,8 +227,7 @@ class ClassDefinition : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override;
+ void Print(bool final, text::Printer* printer) const override;
private:
DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 44fa0f19a0e5..6b07b1e96261 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -37,8 +37,10 @@
#include "java/ClassDefinition.h"
#include "process/SymbolTable.h"
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -61,7 +63,7 @@ static bool IsValidSymbol(const StringPiece& symbol) {
// Java symbols can not contain . or -, but those are valid in a resource name.
// Replace those with '_'.
-static std::string TransformToFieldName(const StringPiece& symbol) {
+std::string JavaClassGenerator::TransformToFieldName(const StringPiece& symbol) {
std::string output = symbol.to_string();
for (char& c : output) {
if (c == '.' || c == '-') {
@@ -89,9 +91,9 @@ static std::string TransformNestedAttr(const ResourceNameRef& attr_name,
// the package.
if (!attr_name.package.empty() &&
package_name_to_generate != attr_name.package) {
- output += "_" + TransformToFieldName(attr_name.package);
+ output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.package);
}
- output += "_" + TransformToFieldName(attr_name.entry);
+ output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.entry);
return output;
}
@@ -189,14 +191,14 @@ JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
const JavaClassGeneratorOptions& options)
: context_(context), table_(table), options_(options) {}
-bool JavaClassGenerator::SkipSymbol(SymbolState state) {
+bool JavaClassGenerator::SkipSymbol(Visibility::Level level) {
switch (options_.types) {
case JavaClassGeneratorOptions::SymbolTypes::kAll:
return false;
case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
- return state == SymbolState::kUndefined;
+ return level == Visibility::Level::kUndefined;
case JavaClassGeneratorOptions::SymbolTypes::kPublic:
- return state != SymbolState::kPublic;
+ return level != Visibility::Level::kPublic;
}
return true;
}
@@ -230,7 +232,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
const StringPiece& package_name_to_generate,
ClassDefinition* out_class_def,
MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt) {
+ Printer* r_txt_printer) {
const std::string array_field_name = TransformToFieldName(name.entry);
std::unique_ptr<ResourceArrayMember> array_def =
util::make_unique<ResourceArrayMember>(array_field_name);
@@ -270,7 +272,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
// Build the JavaDoc comment for the Styleable array. This has references to child attributes
// and what possible values can be used for them.
const size_t attr_count = sorted_attributes.size();
- if (attr_count > 0) {
+ if (out_class_def != nullptr && attr_count > 0) {
std::stringstream styleable_comment;
if (!styleable.GetComment().empty()) {
styleable_comment << styleable.GetComment() << "\n";
@@ -323,8 +325,8 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
}
- if (out_r_txt != nullptr) {
- *out_r_txt << "int[] styleable " << array_field_name << " {";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Print("int[] styleable ").Print(array_field_name).Print(" {");
}
// Add the ResourceIds to the array member.
@@ -332,16 +334,16 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
array_def->AddElement(id);
- if (out_r_txt != nullptr) {
+ if (r_txt_printer != nullptr) {
if (i != 0) {
- *out_r_txt << ",";
+ r_txt_printer->Print(",");
}
- *out_r_txt << " " << id;
+ r_txt_printer->Print(" ").Print(id.to_string());
}
}
- if (out_r_txt != nullptr) {
- *out_r_txt << " }\n";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Println(" }");
}
// Add the Styleable array to the Styleable class.
@@ -354,54 +356,56 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
continue;
}
- StringPiece comment = styleable_attr.attr_ref->GetComment();
- if (styleable_attr.symbol.value().attribute && comment.empty()) {
- comment = styleable_attr.symbol.value().attribute->GetComment();
- }
+ if (out_class_def != nullptr) {
+ StringPiece comment = styleable_attr.attr_ref->GetComment();
+ if (styleable_attr.symbol.value().attribute && comment.empty()) {
+ comment = styleable_attr.symbol.value().attribute->GetComment();
+ }
- if (comment.contains("@removed")) {
- // Removed attributes are public but hidden from the documentation, so
- // don't emit them as part of the class documentation.
- continue;
- }
+ if (comment.contains("@removed")) {
+ // Removed attributes are public but hidden from the documentation, so
+ // don't emit them as part of the class documentation.
+ continue;
+ }
- const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
+ const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
- StringPiece package_name = attr_name.package;
- if (package_name.empty()) {
- package_name = context_->GetCompilationPackage();
- }
+ StringPiece package_name = attr_name.package;
+ if (package_name.empty()) {
+ package_name = context_->GetCompilationPackage();
+ }
- std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
- sorted_attributes[i].field_name, static_cast<uint32_t>(i));
+ std::unique_ptr<IntMember> index_member =
+ util::make_unique<IntMember>(sorted_attributes[i].field_name, static_cast<uint32_t>(i));
+
+ AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
+
+ if (!comment.empty()) {
+ attr_processor->AppendComment("<p>\n@attr description");
+ attr_processor->AppendComment(comment);
+ } else {
+ std::stringstream default_comment;
+ default_comment << "<p>This symbol is the offset where the "
+ << "{@link " << package_name << ".R.attr#"
+ << TransformToFieldName(attr_name.entry) << "}\n"
+ << "attribute's value can be found in the "
+ << "{@link #" << array_field_name << "} array.";
+ attr_processor->AppendComment(default_comment.str());
+ }
- AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
+ attr_processor->AppendNewLine();
+ AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
+ attr_processor->AppendNewLine();
+ attr_processor->AppendComment(
+ StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
- if (!comment.empty()) {
- attr_processor->AppendComment("<p>\n@attr description");
- attr_processor->AppendComment(comment);
- } else {
- std::stringstream default_comment;
- default_comment << "<p>This symbol is the offset where the "
- << "{@link " << package_name << ".R.attr#"
- << TransformToFieldName(attr_name.entry) << "}\n"
- << "attribute's value can be found in the "
- << "{@link #" << array_field_name << "} array.";
- attr_processor->AppendComment(default_comment.str());
+ out_class_def->AddMember(std::move(index_member));
}
- attr_processor->AppendNewLine();
- AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
- attr_processor->AppendNewLine();
- attr_processor->AppendComment(
- StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
-
- if (out_r_txt != nullptr) {
- *out_r_txt << StringPrintf("int styleable %s %d\n", sorted_attributes[i].field_name.data(),
- (int)i);
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Println(
+ StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
}
-
- out_class_def->AddMember(std::move(index_member));
}
// If there is a rewrite method to generate, add the statements that rewrite package IDs
@@ -422,46 +426,55 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
const ResourceEntry& entry, ClassDefinition* out_class_def,
MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt) {
+ text::Printer* r_txt_printer) {
ResourceId real_id = id;
if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
id.package_id() > kAppPackageId) {
+ // Workaround for feature splits using package IDs > 0x7F.
+ // See b/37498913.
real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
}
const std::string field_name = TransformToFieldName(name.entry);
- std::unique_ptr<ResourceMember> resource_member =
- util::make_unique<ResourceMember>(field_name, real_id);
+ if (out_class_def != nullptr) {
+ std::unique_ptr<ResourceMember> resource_member =
+ util::make_unique<ResourceMember>(field_name, real_id);
- // Build the comments and annotations for this entry.
- AnnotationProcessor* processor = resource_member->GetCommentBuilder();
+ // Build the comments and annotations for this entry.
+ AnnotationProcessor* processor = resource_member->GetCommentBuilder();
- // Add the comments from any <public> tags.
- if (entry.symbol_status.state != SymbolState::kUndefined) {
- processor->AppendComment(entry.symbol_status.comment);
- }
+ // Add the comments from any <public> tags.
+ if (entry.visibility.level != Visibility::Level::kUndefined) {
+ processor->AppendComment(entry.visibility.comment);
+ }
- // Add the comments from all configurations of this entry.
- for (const auto& config_value : entry.values) {
- processor->AppendComment(config_value->value->GetComment());
- }
+ // Add the comments from all configurations of this entry.
+ for (const auto& config_value : entry.values) {
+ processor->AppendComment(config_value->value->GetComment());
+ }
- // If this is an Attribute, append the format Javadoc.
- if (!entry.values.empty()) {
- if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
- // We list out the available values for the given attribute.
- AddAttributeFormatDoc(processor, attr);
+ // If this is an Attribute, append the format Javadoc.
+ if (!entry.values.empty()) {
+ if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
+ // We list out the available values for the given attribute.
+ AddAttributeFormatDoc(processor, attr);
+ }
}
- }
- out_class_def->AddMember(std::move(resource_member));
+ out_class_def->AddMember(std::move(resource_member));
+ }
- if (out_r_txt != nullptr) {
- *out_r_txt << "int " << name.type << " " << field_name << " " << real_id << "\n";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Print("int ")
+ .Print(to_string(name.type))
+ .Print(" ")
+ .Print(field_name)
+ .Print(" ")
+ .Println(real_id.to_string());
}
if (out_rewrite_method != nullptr) {
- const StringPiece& type_str = ToString(name.type);
+ const StringPiece& type_str = to_string(name.type);
out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
type_str.data(), field_name.data(),
type_str.data(), field_name.data()));
@@ -471,7 +484,7 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
const StringPiece& package_name_to_generate,
const ResourceEntry& entry) {
- if (SkipSymbol(entry.symbol_status.state)) {
+ if (SkipSymbol(entry.visibility.level)) {
return {};
}
@@ -480,7 +493,7 @@ Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& packa
if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
// The entry name was mangled, and we successfully unmangled it.
// Check that we want to emit this symbol.
- if (package_name != unmangled_package) {
+ if (package_name_to_generate != unmangled_package) {
// Skip the entry if it doesn't belong to the package we're writing.
return {};
}
@@ -497,7 +510,7 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
const ResourceTableType& type,
ClassDefinition* out_type_class_def,
MethodDefinition* out_rewrite_method_def,
- std::ostream* out_r_txt) {
+ Printer* r_txt_printer) {
for (const auto& entry : type.entries) {
const Maybe<std::string> unmangled_name =
UnmangleResource(package.name, package_name_to_generate, *entry);
@@ -532,18 +545,18 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
static_cast<const Styleable*>(entry->values.front()->value.get());
ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
- out_rewrite_method_def, out_r_txt);
+ out_rewrite_method_def, r_txt_printer);
} else {
ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
- out_r_txt);
+ r_txt_printer);
}
}
return true;
}
-bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out,
- std::ostream* out_r_txt) {
- return Generate(package_name_to_generate, package_name_to_generate, out);
+bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, OutputStream* out,
+ OutputStream* out_r_txt) {
+ return Generate(package_name_to_generate, package_name_to_generate, out, out_r_txt);
}
static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
@@ -556,13 +569,18 @@ static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations
}
bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
- const StringPiece& out_package_name, std::ostream* out,
- std::ostream* out_r_txt) {
+ const StringPiece& out_package_name, OutputStream* out,
+ OutputStream* out_r_txt) {
ClassDefinition r_class("R", ClassQualifier::kNone, true);
std::unique_ptr<MethodDefinition> rewrite_method;
+ std::unique_ptr<Printer> r_txt_printer;
+ if (out_r_txt != nullptr) {
+ r_txt_printer = util::make_unique<Printer>(out_r_txt);
+ }
+
// Generate an onResourcesLoaded() callback if requested.
- if (options_.rewrite_callback_options) {
+ if (out != nullptr && options_.rewrite_callback_options) {
rewrite_method =
util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
for (const std::string& package_to_callback :
@@ -579,15 +597,18 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
continue;
}
- // Stay consistent with AAPT and generate an empty type class if the R class
- // is public.
+ // Stay consistent with AAPT and generate an empty type class if the R class is public.
const bool force_creation_if_empty =
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
- std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
- ToString(type->type), ClassQualifier::kStatic, force_creation_if_empty);
+ std::unique_ptr<ClassDefinition> class_def;
+ if (out != nullptr) {
+ class_def = util::make_unique<ClassDefinition>(
+ to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
+ }
+
if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
- rewrite_method.get(), out_r_txt)) {
+ rewrite_method.get(), r_txt_printer.get())) {
return false;
}
@@ -596,22 +617,23 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
if (priv_type) {
if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
- rewrite_method.get(), out_r_txt)) {
+ rewrite_method.get(), r_txt_printer.get())) {
return false;
}
}
}
- if (type->type == ResourceType::kStyleable &&
+ if (out != nullptr && type->type == ResourceType::kStyleable &&
options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
// When generating a public R class, we don't want Styleable to be part
// of the API. It is only emitted for documentation purposes.
class_def->GetCommentBuilder()->AppendComment("@doconly");
}
- AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
-
- r_class.AddMember(std::move(class_def));
+ if (out != nullptr) {
+ AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
+ r_class.AddMember(std::move(class_def));
+ }
}
}
@@ -619,23 +641,10 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
r_class.AddMember(std::move(rewrite_method));
}
- AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
-
- if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
- return false;
- }
-
- out->flush();
-
- if (out_r_txt != nullptr) {
- out_r_txt->flush();
-
- if (!*out_r_txt) {
- error_ = android::base::SystemErrorCodeToString(errno);
- return false;
- }
+ if (out != nullptr) {
+ AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
+ ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
}
-
return true;
}
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 18746ffc5a0a..853120b3cb98 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -17,15 +17,16 @@
#ifndef AAPT_JAVA_CLASS_GENERATOR_H
#define AAPT_JAVA_CLASS_GENERATOR_H
-#include <ostream>
#include <string>
#include "androidfw/StringPiece.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
+#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "text/Printer.h"
namespace aapt {
@@ -69,17 +70,19 @@ class JavaClassGenerator {
// All symbols technically belong to a single package, but linked libraries will
// have their names mangled, denoting that they came from a different package.
// We need to generate these symbols in a separate file. Returns true on success.
- bool Generate(const android::StringPiece& package_name_to_generate, std::ostream* out,
- std::ostream* out_r_txt = nullptr);
+ bool Generate(const android::StringPiece& package_name_to_generate, io::OutputStream* out,
+ io::OutputStream* out_r_txt = nullptr);
bool Generate(const android::StringPiece& package_name_to_generate,
- const android::StringPiece& output_package_name, std::ostream* out,
- std::ostream* out_r_txt = nullptr);
+ const android::StringPiece& output_package_name, io::OutputStream* out,
+ io::OutputStream* out_r_txt = nullptr);
- const std::string& getError() const;
+ const std::string& GetError() const;
+
+ static std::string TransformToFieldName(const android::StringPiece& symbol);
private:
- bool SkipSymbol(SymbolState state);
+ bool SkipSymbol(Visibility::Level state);
bool SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol);
// Returns the unmangled resource entry name if the unmangled package is the same as
@@ -91,13 +94,13 @@ class JavaClassGenerator {
bool ProcessType(const android::StringPiece& package_name_to_generate,
const ResourceTablePackage& package, const ResourceTableType& type,
ClassDefinition* out_type_class_def, MethodDefinition* out_rewrite_method_def,
- std::ostream* out_r_txt);
+ text::Printer* r_txt_printer);
// Writes a resource to the R.java file, optionally writing out a rewrite rule for its package
// ID if `out_rewrite_method` is not nullptr.
void ProcessResource(const ResourceNameRef& name, const ResourceId& id,
const ResourceEntry& entry, ClassDefinition* out_class_def,
- MethodDefinition* out_rewrite_method, std::ostream* out_r_txt);
+ MethodDefinition* out_rewrite_method, text::Printer* r_txt_printer);
// Writes a styleable resource to the R.java file, optionally writing out a rewrite rule for
// its package ID if `out_rewrite_method` is not nullptr.
@@ -106,7 +109,7 @@ class JavaClassGenerator {
const Styleable& styleable,
const android::StringPiece& package_name_to_generate,
ClassDefinition* out_class_def, MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt);
+ text::Printer* r_txt_printer);
IAaptContext* context_;
ResourceTable* table_;
@@ -114,7 +117,7 @@ class JavaClassGenerator {
std::string error_;
};
-inline const std::string& JavaClassGenerator::getError() const {
+inline const std::string& JavaClassGenerator::GetError() const {
return error_;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 84bf04134ad9..e449546f9399 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -16,15 +16,18 @@
#include "java/JavaClassGenerator.h"
-#include <sstream>
#include <string>
+#include "io/StringStream.h"
#include "test/Test.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::aapt::io::StringOutputStream;
+using ::android::StringPiece;
+using ::testing::HasSubstr;
using ::testing::Lt;
using ::testing::Ne;
+using ::testing::Not;
namespace aapt {
@@ -43,7 +46,8 @@ TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string result;
+ StringOutputStream out(&result);
EXPECT_FALSE(generator.Generate("android", &out));
}
@@ -53,35 +57,28 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
.SetPackageId("android", 0x01)
.AddSimple("android:id/hey-man", ResourceId(0x01020000))
.AddValue("android:attr/cool.attr", ResourceId(0x01010000),
- test::AttributeBuilder(false).Build())
- .AddValue(
- "android:styleable/hey.dude", ResourceId(0x01030000),
- test::StyleableBuilder()
- .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
- .Build())
+ test::AttributeBuilder().Build())
+ .AddValue("android:styleable/hey.dude", ResourceId(0x01030000),
+ test::StyleableBuilder()
+ .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
+ .Build())
.Build();
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string output = out.str();
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_man=0x01020000;"));
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int[] hey_dude={"));
-
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_dude_cool_attr=0;"));
+ EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
+ EXPECT_THAT(output, HasSubstr("public static final int hey_dude_cool_attr=0;"));
}
TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
@@ -94,20 +91,20 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
+ out.Flush();
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_EQ(std::string::npos, output.find("two"));
- EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
+ EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, Not(HasSubstr("two")));
+ EXPECT_THAT(output, Not(HasSubstr("com_foo$two")));
}
TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
@@ -120,18 +117,18 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("public static final class attr"));
- EXPECT_EQ(std::string::npos,
- output.find("public static final class ^attr-private"));
+ EXPECT_THAT(output, HasSubstr("public static final class attr"));
+ EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
}
TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
@@ -142,16 +139,13 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
.AddSimple("android:id/one", ResourceId(0x01020000))
.AddSimple("android:id/two", ResourceId(0x01020001))
.AddSimple("android:id/three", ResourceId(0x01020002))
- .SetSymbolState("android:id/one", ResourceId(0x01020000),
- SymbolState::kPublic)
- .SetSymbolState("android:id/two", ResourceId(0x01020001),
- SymbolState::kPrivate)
+ .SetSymbolState("android:id/one", ResourceId(0x01020000), Visibility::Level::kPublic)
+ .SetSymbolState("android:id/two", ResourceId(0x01020001), Visibility::Level::kPrivate)
.Build();
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
@@ -159,40 +153,40 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_EQ(std::string::npos, output.find("two"));
- EXPECT_EQ(std::string::npos, output.find("three"));
+ out.Flush();
+
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, Not(HasSubstr("two")));
+ EXPECT_THAT(output, Not(HasSubstr("three")));
}
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int two=0x01020001;"));
- EXPECT_EQ(std::string::npos, output.find("three"));
+ out.Flush();
+
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
+ EXPECT_THAT(output, Not(HasSubstr("three")));
}
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int one=0x01020000;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int two=0x01020001;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int three=0x01020002;"));
+ out.Flush();
+
+ EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
+ EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
}
}
@@ -235,10 +229,8 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
test::ResourceTableBuilder()
.SetPackageId("android", 0x01)
.SetPackageId("com.lib", 0x02)
- .AddValue("android:attr/bar", ResourceId(0x01010000),
- test::AttributeBuilder(false).Build())
- .AddValue("com.lib:attr/bar", ResourceId(0x02010000),
- test::AttributeBuilder(false).Build())
+ .AddValue("android:attr/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+ .AddValue("com.lib:attr/bar", ResourceId(0x02010000), test::AttributeBuilder().Build())
.AddValue("android:styleable/foo", ResourceId(0x01030000),
test::StyleableBuilder()
.AddItem("android:attr/bar", ResourceId(0x01010000))
@@ -248,18 +240,18 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("int foo_bar="));
- EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
+ EXPECT_THAT(output, HasSubstr("int foo_bar="));
+ EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
}
TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
@@ -273,35 +265,34 @@ TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ out.Flush();
- const char* expectedText =
+ const char* expected_text =
R"EOF(/**
* This is a comment
* @deprecated
*/
@Deprecated
public static final int foo=0x01010000;)EOF";
-
- EXPECT_NE(std::string::npos, actual.find(expectedText));
+ EXPECT_THAT(output, HasSubstr(expected_text));
}
TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
- Attribute attr(false);
+ Attribute attr;
attr.SetComment(StringPiece("This is an attribute"));
Styleable styleable;
- styleable.entries.push_back(
- Reference(test::ParseNameOrDie("android:attr/one")));
+ styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
styleable.SetComment(StringPiece("This is a styleable"));
std::unique_ptr<ResourceTable> table =
@@ -314,21 +305,22 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGeneratorOptions options;
options.use_final = false;
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ out.Flush();
- EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
- EXPECT_NE(std::string::npos, actual.find("attr description"));
- EXPECT_NE(std::string::npos, actual.find(attr.GetComment().data()));
- EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data()));
+ EXPECT_THAT(output, HasSubstr("attr name android:one"));
+ EXPECT_THAT(output, HasSubstr("attr description"));
+ EXPECT_THAT(output, HasSubstr(attr.GetComment()));
+ EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
}
TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
@@ -355,9 +347,11 @@ TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
JavaClassGeneratorOptions options;
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
std::string::size_type actionbar_pos = output.find("int[] ActionBar");
ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
@@ -379,7 +373,7 @@ TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
}
TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
- Attribute attr(false);
+ Attribute attr;
attr.SetComment(StringPiece("removed"));
std::unique_ptr<ResourceTable> table =
@@ -390,33 +384,34 @@ TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
- .AddSymbolSource(
- util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGeneratorOptions options;
options.use_final = false;
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string actual = out.str();
+ out.Flush();
- EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
- EXPECT_EQ(std::string::npos, actual.find("@attr description"));
+ EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
+ EXPECT_THAT(output, Not(HasSubstr("@attr description")));
// We should find @removed only in the attribute javadoc and not anywhere else
- // (i.e. the class
- // javadoc).
- const size_t pos = actual.find("removed");
- EXPECT_NE(std::string::npos, pos);
- EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
+ // (i.e. the class javadoc).
+ const std::string kRemoved("removed");
+ ASSERT_THAT(output, HasSubstr(kRemoved));
+ std::string after_first_match = output.substr(output.find(kRemoved) + kRemoved.size());
+ EXPECT_THAT(after_first_match, Not(HasSubstr(kRemoved)));
}
TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("android", 0x00)
- .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>(false))
+ .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>())
.AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
.AddValue(
"android:style/foo", ResourceId(0x00030000),
@@ -430,19 +425,17 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary)
JavaClassGeneratorOptions options;
options.use_final = false;
- options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
- {"com.foo", "com.boo"},
- };
+ options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string actual = out.str();
-
- EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
- EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
- EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded"));
}
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 8981e0718be0..c4b36176aa71 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -25,7 +25,7 @@
#include "util/Maybe.h"
#include "xml/XmlDom.h"
-using android::StringPiece;
+using ::android::StringPiece;
using ::aapt::text::IsJavaIdentifier;
namespace aapt {
@@ -50,17 +50,16 @@ static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source
return result;
}
-static bool WriteSymbol(const Source& source, IDiagnostics* diag,
- xml::Element* el, ClassDefinition* class_def) {
+static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
+ ClassDefinition* class_def) {
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
if (!attr) {
- diag->Error(DiagMessage(source) << "<" << el->name
- << "> must define 'android:name'");
+ diag->Error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
return false;
}
- Maybe<StringPiece> result = ExtractJavaIdentifier(
- diag, source.WithLine(el->line_number), attr->value);
+ Maybe<StringPiece> result =
+ ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
if (!result) {
return false;
}
@@ -76,8 +75,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag,
return true;
}
-std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
- xml::XmlResource* res) {
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
xml::Element* el = xml::FindRootElement(res->root.get());
if (!el) {
diag->Error(DiagMessage(res->file.source) << "no root tag defined");
@@ -85,8 +83,7 @@ std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
}
if (el->name != "manifest" && !el->namespace_uri.empty()) {
- diag->Error(DiagMessage(res->file.source)
- << "no <manifest> root tag defined");
+ diag->Error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
return {};
}
@@ -100,11 +97,9 @@ std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
for (xml::Element* child_el : children) {
if (child_el->namespace_uri.empty()) {
if (child_el->name == "permission") {
- error |= !WriteSymbol(res->file.source, diag, child_el,
- permission_class.get());
+ error |= !WriteSymbol(res->file.source, diag, child_el, permission_class.get());
} else if (child_el->name == "permission-group") {
- error |= !WriteSymbol(res->file.source, diag, child_el,
- permission_group_class.get());
+ error |= !WriteSymbol(res->file.source, diag, child_el, permission_group_class.get());
}
}
}
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index b12202a8d137..3f6645facaa2 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -23,8 +23,7 @@
namespace aapt {
-std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
- xml::XmlResource* res);
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index 44b6a1ffd5ae..f4e10ab2e584 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -16,8 +16,10 @@
#include "java/ManifestClassGenerator.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
@@ -84,6 +86,8 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
@hide
@SystemApi -->
<permission android:name="android.permission.SECRET" />
+ <!-- @TestApi This is a test only permission. -->
+ <permission android:name="android.permission.TEST_ONLY" />
</manifest>)");
std::string actual;
@@ -110,6 +114,13 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
@android.annotation.SystemApi
public static final String SECRET="android.permission.SECRET";)";
EXPECT_THAT(actual, HasSubstr(expected_secret));
+
+ const char* expected_test = R"( /**
+ * This is a test only permission.
+ */
+ @android.annotation.TestApi
+ public static final String TEST_ONLY="android.permission.TEST_ONLY";)";
+ EXPECT_THAT(actual, HasSubstr(expected_test));
}
// This is bad but part of public API behaviour so we need to preserve it.
@@ -118,13 +129,16 @@ TEST(ManifestClassGeneratorTest, LastSeenPermissionWithSameLeafNameTakesPreceden
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission android:name="android.permission.ACCESS_INTERNET" />
- <permission android:name="com.android.aapt.test.ACCESS_INTERNET" />
+ <permission android:name="com.android.sample.ACCESS_INTERNET" />
+ <permission android:name="com.android.permission.UNRELATED_PERMISSION" />
+ <permission android:name="com.android.aapt.test.ACCESS_INTERNET" /> -->
</manifest>)");
std::string actual;
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
EXPECT_THAT(actual, HasSubstr("ACCESS_INTERNET=\"com.android.aapt.test.ACCESS_INTERNET\";"));
EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";")));
+ EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"com.android.sample.ACCESS_INTERNET\";")));
}
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,
@@ -135,12 +149,9 @@ static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xm
return ::testing::AssertionFailure() << "manifest_class == nullptr";
}
- std::stringstream out;
- if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out)) {
- return ::testing::AssertionFailure() << "failed to write java file";
- }
-
- *out_str = out.str();
+ StringOutputStream out(out_str);
+ manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out);
+ out.Flush();
return ::testing::AssertionSuccess();
}
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 10c46101123c..ffcef8966654 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -20,10 +20,18 @@
#include <string>
#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+#include "JavaClassGenerator.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+#include "text/Printer.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+
namespace aapt {
namespace proguard {
@@ -31,7 +39,7 @@ class BaseVisitor : public xml::Visitor {
public:
using xml::Visitor::Visit;
- BaseVisitor(const Source& source, KeepSet* keep_set) : source_(source), keep_set_(keep_set) {
+ BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : file_(file), keep_set_(keep_set) {
}
void Visit(xml::Element* node) override {
@@ -52,27 +60,47 @@ class BaseVisitor : public xml::Visitor {
for (const auto& child : node->children) {
child->Accept(this);
}
+
+ for (const auto& attr : node->attributes) {
+ if (attr.compiled_value) {
+ auto ref = ValueCast<Reference>(attr.compiled_value.get());
+ if (ref) {
+ AddReference(node->line_number, ref);
+ }
+ }
+ }
}
protected:
- void AddClass(size_t line_number, const std::string& class_name) {
- keep_set_->AddClass(Source(source_.path, line_number), class_name);
+ ResourceFile file_;
+ KeepSet* keep_set_;
+
+ virtual void AddClass(size_t line_number, const std::string& class_name) {
+ keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, class_name);
}
void AddMethod(size_t line_number, const std::string& method_name) {
- keep_set_->AddMethod(Source(source_.path, line_number), method_name);
+ keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)}, method_name);
+ }
+
+ void AddReference(size_t line_number, Reference* ref) {
+ if (ref && ref->name) {
+ ResourceName ref_name = ref->name.value();
+ if (ref_name.package.empty()) {
+ ref_name = ResourceName(file_.name.package, ref_name.type, ref_name.entry);
+ }
+ keep_set_->AddReference({file_.name, file_.source.WithLine(line_number)}, ref_name);
+ }
}
private:
DISALLOW_COPY_AND_ASSIGN(BaseVisitor);
- Source source_;
- KeepSet* keep_set_;
};
class LayoutVisitor : public BaseVisitor {
public:
- LayoutVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
}
void Visit(xml::Element* node) override {
@@ -110,7 +138,7 @@ class LayoutVisitor : public BaseVisitor {
class MenuVisitor : public BaseVisitor {
public:
- MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ MenuVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
}
void Visit(xml::Element* node) override {
@@ -136,7 +164,7 @@ class MenuVisitor : public BaseVisitor {
class XmlResourceVisitor : public BaseVisitor {
public:
- XmlResourceVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ XmlResourceVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
}
void Visit(xml::Element* node) override {
@@ -163,7 +191,7 @@ class XmlResourceVisitor : public BaseVisitor {
class TransitionVisitor : public BaseVisitor {
public:
- TransitionVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ TransitionVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
}
void Visit(xml::Element* node) override {
@@ -185,8 +213,9 @@ class TransitionVisitor : public BaseVisitor {
class ManifestVisitor : public BaseVisitor {
public:
- ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
- : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
+ ManifestVisitor(const ResourceFile& file, KeepSet* keep_set, bool main_dex_only)
+ : BaseVisitor(file, keep_set), main_dex_only_(main_dex_only) {
+ }
void Visit(xml::Element* node) override {
if (node->namespace_uri.empty()) {
@@ -241,6 +270,10 @@ class ManifestVisitor : public BaseVisitor {
BaseVisitor::Visit(node);
}
+ virtual void AddClass(size_t line_number, const std::string& class_name) override {
+ keep_set_->AddManifestClass({file_.name, file_.source.WithLine(line_number)}, class_name);
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);
@@ -249,9 +282,8 @@ class ManifestVisitor : public BaseVisitor {
std::string default_process_;
};
-bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keep_set,
- bool main_dex_only) {
- ManifestVisitor visitor(source, keep_set, main_dex_only);
+bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set, bool main_dex_only) {
+ ManifestVisitor visitor(res->file, keep_set, main_dex_only);
if (res->root) {
res->root->Accept(&visitor);
return true;
@@ -259,55 +291,150 @@ bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res
return false;
}
-bool CollectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keep_set) {
+bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) {
if (!res->root) {
return false;
}
switch (res->file.name.type) {
case ResourceType::kLayout: {
- LayoutVisitor visitor(source, keep_set);
+ LayoutVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
break;
}
case ResourceType::kXml: {
- XmlResourceVisitor visitor(source, keep_set);
+ XmlResourceVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
break;
}
case ResourceType::kTransition: {
- TransitionVisitor visitor(source, keep_set);
+ TransitionVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
break;
}
case ResourceType::kMenu: {
- MenuVisitor visitor(source, keep_set);
+ MenuVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
break;
}
- default:
+ default: {
+ BaseVisitor visitor(res->file, keep_set);
+ res->root->Accept(&visitor);
break;
+ }
+ }
+ return true;
+}
+
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+ Printer printer(out);
+ for (const auto& entry : keep_set.manifest_class_set_) {
+ for (const UsageLocation& location : entry.second) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ }
+
+ for (const auto& entry : keep_set.conditional_class_set_) {
+ std::set<UsageLocation> locations;
+ bool can_be_conditional = true;
+ for (const UsageLocation& location : entry.second) {
+ can_be_conditional &= CollectLocations(location, keep_set, &locations);
+ }
+
+ if (keep_set.conditional_keep_rules_ && can_be_conditional) {
+ for (const UsageLocation& location : locations) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ printer.Print("-if class **.R$layout { int ")
+ .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
+ .Println("; }");
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ }
+ } else {
+ for (const UsageLocation& location : entry.second) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ }
+ printer.Println();
+ }
+
+ for (const auto& entry : keep_set.method_set_) {
+ for (const UsageLocation& location : entry.second) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
+ printer.Print("-keepclassmembers class * { *** ").Print(entry.first).Println("(...); }");
+ printer.Println();
+ }
+}
+
+bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
+ std::set<UsageLocation>* locations) {
+ locations->insert(location);
+
+ // TODO: allow for more reference types if we can determine its safe.
+ if (location.name.type != ResourceType::kLayout) {
+ return false;
+ }
+
+ for (const auto& entry : keep_set.reference_set_) {
+ if (entry.first == location.name) {
+ for (auto& refLocation : entry.second) {
+ // Don't get stuck in loops
+ if (locations->find(refLocation) != locations->end()) {
+ return false;
+ }
+ if (!CollectLocations(refLocation, keep_set, locations)) {
+ return false;
+ }
+ }
+ }
}
+
return true;
}
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
- for (const auto& entry : keep_set.keep_set_) {
- for (const Source& source : entry.second) {
- *out << "# Referenced at " << source << "\n";
+class ReferenceVisitor : public ValueVisitor {
+ public:
+ using ValueVisitor::Visit;
+
+ ReferenceVisitor(aapt::IAaptContext* context, ResourceName from, KeepSet* keep_set)
+ : context_(context), from_(from), keep_set_(keep_set) {
+ }
+
+ void Visit(Reference* reference) override {
+ if (reference->name) {
+ ResourceName reference_name = reference->name.value();
+ if (reference_name.package.empty()) {
+ reference_name = ResourceName(context_->GetCompilationPackage(), reference_name.type,
+ reference_name.entry);
+ }
+ keep_set_->AddReference({from_, reference->GetSource()}, reference_name);
}
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
}
- for (const auto& entry : keep_set.keep_method_set_) {
- for (const Source& source : entry.second) {
- *out << "# Referenced at " << source << "\n";
+ private:
+ aapt::IAaptContext* context_;
+ ResourceName from_;
+ KeepSet* keep_set_;
+};
+
+bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
+ KeepSet* keep_set) {
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ for (auto& config_value : entry->values) {
+ ResourceName from(pkg->name, type->type, entry->name);
+ ReferenceVisitor visitor(context, from, keep_set);
+ config_value->value->Accept(&visitor);
+ }
+ }
}
- *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
}
return true;
}
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 3c349bab1217..46827ee7cf93 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -22,37 +22,83 @@
#include <set>
#include <string>
+#include "androidfw/StringPiece.h"
+
#include "Resource.h"
+#include "ResourceTable.h"
#include "Source.h"
+#include "ValueVisitor.h"
+#include "io/Io.h"
+#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
namespace aapt {
namespace proguard {
+struct UsageLocation {
+ ResourceName name;
+ Source source;
+};
+
class KeepSet {
public:
- inline void AddClass(const Source& source, const std::string& class_name) {
- keep_set_[class_name].insert(source);
+ KeepSet() = default;
+
+ KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) {
}
- inline void AddMethod(const Source& source, const std::string& method_name) {
- keep_method_set_[method_name].insert(source);
+ inline void AddManifestClass(const UsageLocation& file, const std::string& class_name) {
+ manifest_class_set_[class_name].insert(file);
+ }
+
+ inline void AddConditionalClass(const UsageLocation& file, const std::string& class_name) {
+ conditional_class_set_[class_name].insert(file);
+ }
+
+ inline void AddMethod(const UsageLocation& file, const std::string& method_name) {
+ method_set_[method_name].insert(file);
+ }
+
+ inline void AddReference(const UsageLocation& file, const ResourceName& resource_name) {
+ reference_set_[resource_name].insert(file);
}
private:
- friend bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+ friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+
+ friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
+ std::set<UsageLocation>* locations);
- std::map<std::string, std::set<Source>> keep_set_;
- std::map<std::string, std::set<Source>> keep_method_set_;
+ bool conditional_keep_rules_ = false;
+ std::map<std::string, std::set<UsageLocation>> manifest_class_set_;
+ std::map<std::string, std::set<UsageLocation>> method_set_;
+ std::map<std::string, std::set<UsageLocation>> conditional_class_set_;
+ std::map<ResourceName, std::set<UsageLocation>> reference_set_;
};
-bool CollectProguardRulesForManifest(const Source& source,
- xml::XmlResource* res, KeepSet* keep_set,
+bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set,
bool main_dex_only = false);
-bool CollectProguardRules(const Source& source, xml::XmlResource* res,
- KeepSet* keep_set);
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
+
+bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
+
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+
+bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
+ std::set<UsageLocation>* locations);
+
+//
+// UsageLocation implementation.
+//
+
+inline bool operator==(const UsageLocation& lhs, const UsageLocation& rhs) {
+ return lhs.name == rhs.name;
+}
+
+inline int operator<(const UsageLocation& lhs, const UsageLocation& rhs) {
+ return lhs.name.compare(rhs.name);
+}
} // namespace proguard
} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 900b07339715..37d1a5fbaeb8 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -15,14 +15,25 @@
*/
#include "java/ProguardRules.h"
+#include "link/Linkers.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
namespace aapt {
+std::string GetKeepSetString(const proguard::KeepSet& set) {
+ std::string out;
+ StringOutputStream sout(&out);
+ proguard::WriteKeepSet(set, &sout);
+ sout.Flush();
+ return out;
+}
+
TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
@@ -31,12 +42,10 @@ TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -47,12 +56,10 @@ TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -65,16 +72,110 @@ TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
}
+TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.foo.Bar />
+ </View>)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+
+ std::string actual = GetKeepSetString(set);
+
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+}
+
+TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
+ std::unique_ptr<xml::XmlResource> bar_layout = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.foo.Bar />
+ </View>)");
+ bar_layout->file.name = test::ParseNameOrDie("com.foo:layout/bar");
+
+ ResourceTable table;
+ StdErrDiagnostics errDiagnostics;
+ table.AddResource(bar_layout->file.name, ConfigDescription::DefaultConfig(), "",
+ util::make_unique<FileReference>(), &errDiagnostics);
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.foo")
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(&table))
+ .Build();
+
+ std::unique_ptr<xml::XmlResource> foo_layout = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android">
+ <include layout="@layout/bar" />
+ </View>)");
+ foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
+
+ XmlReferenceLinker xml_linker;
+ ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
+ ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
+
+ proguard::KeepSet set = proguard::KeepSet(true);
+ ASSERT_TRUE(proguard::CollectProguardRules(bar_layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(foo_layout.get(), &set));
+
+ std::string actual = GetKeepSetString(set);
+
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("int foo"));
+ EXPECT_THAT(actual, HasSubstr("int bar"));
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+}
+
+TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.foo.Bar />
+ </View>)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set = proguard::KeepSet(true);
+ set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+
+ std::string actual = GetKeepSetString(set);
+
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("int foo"));
+ EXPECT_THAT(actual, HasSubstr("int bar"));
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+}
+
+TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.foo.Bar />
+ </View>)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set = proguard::KeepSet(true);
+ set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+
+ std::string actual = GetKeepSetString(set);
+
+ EXPECT_THAT(actual, Not(HasSubstr("-if")));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+}
+
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
@@ -83,12 +184,10 @@ TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("bar_method"));
}
@@ -104,12 +203,10 @@ TEST(ProguardRulesTest, MenuRulesAreEmitted) {
menu->file.name = test::ParseNameOrDie("menu/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules({}, menu.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(menu.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("on_click"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));