diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index e6d94e67c3fc5..7a33de0262e24 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -178,6 +178,7 @@ cc_test_host { "libgmock", ], defaults: ["aapt2_defaults"], + data: ["integration-tests/CompileTest/**/*"], } // ========================================================== diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 1db63350670c9..6fe86107d6c0d 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -100,10 +100,20 @@ static Maybe ExtractResourcePathData(const std::string& path, std::string& filename = parts[parts.size() - 1]; StringPiece name = filename; StringPiece extension; - size_t dot_pos = filename.find('.'); - if (dot_pos != std::string::npos) { - extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1)); - name = name.substr(0, dot_pos); + + const std::string kNinePng = ".9.png"; + if (filename.size() > kNinePng.size() + && std::equal(kNinePng.rbegin(), kNinePng.rend(), filename.rbegin())) { + // Split on .9.png if this extension is present at the end of the file path + name = name.substr(0, filename.size() - kNinePng.size()); + extension = "9.png"; + } else { + // Split on the last period occurrence + size_t dot_pos = filename.rfind('.'); + if (dot_pos != std::string::npos) { + extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1)); + name = name.substr(0, dot_pos); + } } return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(), @@ -799,12 +809,13 @@ int Compile(const std::vector& args, IDiagnostics* diagnostics) { // We use a different extension (not necessary anymore, but avoids altering the existing // build system logic). path_data.extension = "arsc"; + } else if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) { if (*type != ResourceType::kRaw) { if (path_data.extension == "xml") { compile_func = &CompileXml; - } else if ((!options.no_png_crunch && path_data.extension == "png") || - path_data.extension == "9.png") { + } else if ((!options.no_png_crunch && path_data.extension == "png") + || path_data.extension == "9.png") { compile_func = &CompilePng; } } @@ -815,6 +826,17 @@ int Compile(const std::vector& args, IDiagnostics* diagnostics) { continue; } + // Treat periods as a reserved character that should not be present in a file name + // Legacy support for AAPT which did not reserve periods + if (compile_func != &CompileFile && !options.legacy_mode + && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) { + error = true; + context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path + << "' name cannot contain '.' other than for" + << "specifying the extension"); + continue; + } + // Compile the file. const std::string out_path = BuildIntermediateContainerFilename(path_data); error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path); diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h new file mode 100644 index 0000000000000..d95cf1c227327 --- /dev/null +++ b/tools/aapt2/cmd/Compile.h @@ -0,0 +1,14 @@ +#ifndef AAPT2_COMPILE_H +#define AAPT2_COMPILE_H + +#include "androidfw/StringPiece.h" + +#include "Diagnostics.h" + +namespace aapt { + + int Compile(const std::vector& args, IDiagnostics* diagnostics); + +}// namespace aapt + +#endif //AAPT2_COMPILE_H diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp new file mode 100644 index 0000000000000..212f2cf26e0d7 --- /dev/null +++ b/tools/aapt2/cmd/Compile_test.cpp @@ -0,0 +1,96 @@ +/* + * 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 "Compile.h" + +#include "android-base/file.h" +#include "io/StringStream.h" +#include "java/AnnotationProcessor.h" +#include "test/Test.h" + +namespace aapt { + +int TestCompile(std::string path, std::string outDir, bool legacy, StdErrDiagnostics& diag) { + std::vector args; + args.push_back(path); + args.push_back("-o"); + args.push_back(outDir); + args.push_back("-v"); + if (legacy) { + args.push_back("--legacy"); + } + return aapt::Compile(args, &diag); +} + +TEST(CompilerTest, MultiplePeriods) { + StdErrDiagnostics diag; + std::unique_ptr context = test::ContextBuilder().Build(); + const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath()) + + "/integration-tests/CompileTest/res"; + + // Resource files without periods in the file name should not throw errors + const std::string path0 = kResDir + "/values/values.xml"; + const std::string path0_out = kResDir + "/values_values.arsc.flat"; + + remove(path0_out.c_str()); + ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0); + ASSERT_EQ(remove(path0_out.c_str()), 0); + ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(remove(path0_out.c_str()), 0); + + const std::string path1 = kResDir + "/drawable/image.png"; + const std::string path1_out = kResDir + "/drawable_image.png.flat"; + remove(path1_out.c_str()); + ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0); + ASSERT_EQ(remove(path1_out.c_str()), 0); + ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(remove(path1_out.c_str()), 0); + + const std::string path2 = kResDir + "/drawable/image.9.png"; + const std::string path2_out = kResDir + "/drawable_image.9.png.flat"; + remove(path2_out.c_str()); + ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0); + ASSERT_EQ(remove(path2_out.c_str()), 0); + ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(remove(path2_out.c_str()), 0); + + // Resource files with periods in the file name should fail on non-legacy compilations + const std::string path3 = kResDir + "/values/values.all.xml"; + const std::string path3_out = kResDir + "/values_values.all.arsc.flat"; + remove(path3_out.c_str()); + ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0); + ASSERT_NE(remove(path3_out.c_str()), 0); + ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(remove(path3_out.c_str()), 0); + + const std::string path4 = kResDir + "/drawable/image.small.png"; + const std::string path4_out = (kResDir + std::string("/drawable_image.small.png.flat")).c_str(); + remove(path4_out.c_str()); + ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0); + ASSERT_NE(remove(path4_out.c_str()), 0); + ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(remove(path4_out.c_str()), 0); + + const std::string path5 = kResDir + "/drawable/image.small.9.png"; + const std::string path5_out = (kResDir + std::string("/drawable_image.small.9.png.flat")).c_str(); + remove(path5_out.c_str()); + ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0); + ASSERT_NE(remove(path5_out.c_str()), 0); + ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(remove(path5_out.c_str()), 0); +} + +} \ No newline at end of file diff --git a/tools/aapt2/integration-tests/CompileTest/res/drawable/image.9.png b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.9.png new file mode 100644 index 0000000000000..1a3731bbc8b88 Binary files /dev/null and b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.9.png differ diff --git a/tools/aapt2/integration-tests/CompileTest/res/drawable/image.png b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.png new file mode 100644 index 0000000000000..1a3731bbc8b88 Binary files /dev/null and b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.png differ diff --git a/tools/aapt2/integration-tests/CompileTest/res/drawable/image.small.9.png b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.small.9.png new file mode 100644 index 0000000000000..1a3731bbc8b88 Binary files /dev/null and b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.small.9.png differ diff --git a/tools/aapt2/integration-tests/CompileTest/res/drawable/image.small.png b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.small.png new file mode 100644 index 0000000000000..1a3731bbc8b88 Binary files /dev/null and b/tools/aapt2/integration-tests/CompileTest/res/drawable/image.small.png differ diff --git a/tools/aapt2/integration-tests/CompileTest/res/values/values.all.xml b/tools/aapt2/integration-tests/CompileTest/res/values/values.all.xml new file mode 100644 index 0000000000000..62ab6526ef7e9 --- /dev/null +++ b/tools/aapt2/integration-tests/CompileTest/res/values/values.all.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/tools/aapt2/integration-tests/CompileTest/res/values/values.xml b/tools/aapt2/integration-tests/CompileTest/res/values/values.xml new file mode 100644 index 0000000000000..62ab6526ef7e9 --- /dev/null +++ b/tools/aapt2/integration-tests/CompileTest/res/values/values.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file