| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2015 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 17 | #include "Link.h" | 
|  | 18 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 19 | #include <sys/stat.h> | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 20 | #include <cinttypes> | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 21 |  | 
| Mohamed Heikal | d3c5fb6 | 2018-01-12 11:37:26 -0500 | [diff] [blame] | 22 | #include <algorithm> | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 23 | #include <queue> | 
|  | 24 | #include <unordered_map> | 
|  | 25 | #include <vector> | 
|  | 26 |  | 
|  | 27 | #include "android-base/errors.h" | 
|  | 28 | #include "android-base/file.h" | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 29 | #include "android-base/stringprintf.h" | 
| Adam Lesinski | d5083f6 | 2017-01-16 15:07:21 -0800 | [diff] [blame] | 30 | #include "androidfw/StringPiece.h" | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 31 |  | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 32 | #include "AppInfo.h" | 
|  | 33 | #include "Debug.h" | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 34 | #include "LoadedApk.h" | 
| Adam Lesinski | 6a00817 | 2016-02-02 17:02:58 -0800 | [diff] [blame] | 35 | #include "Locale.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 36 | #include "NameMangler.h" | 
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 37 | #include "ResourceUtils.h" | 
| Adam Lesinski | d3ffa844 | 2017-09-28 13:34:35 -0700 | [diff] [blame] | 38 | #include "ResourceValues.h" | 
|  | 39 | #include "ValueVisitor.h" | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 40 | #include "cmd/Util.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 41 | #include "compile/IdAssigner.h" | 
| Adam Lesinski | 2427dce | 2017-11-30 15:10:28 -0800 | [diff] [blame] | 42 | #include "compile/XmlIdCollector.h" | 
| Adam Lesinski | 6a00817 | 2016-02-02 17:02:58 -0800 | [diff] [blame] | 43 | #include "filter/ConfigFilter.h" | 
| Adam Lesinski | 4670805 | 2017-09-29 14:49:15 -0700 | [diff] [blame] | 44 | #include "format/Archive.h" | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 45 | #include "format/Container.h" | 
| Adam Lesinski | 4670805 | 2017-09-29 14:49:15 -0700 | [diff] [blame] | 46 | #include "format/binary/TableFlattener.h" | 
|  | 47 | #include "format/binary/XmlFlattener.h" | 
|  | 48 | #include "format/proto/ProtoDeserialize.h" | 
|  | 49 | #include "format/proto/ProtoSerialize.h" | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 50 | #include "io/BigBufferStream.h" | 
|  | 51 | #include "io/FileStream.h" | 
| Adam Lesinski | a40e972 | 2015-11-24 19:11:46 -0800 | [diff] [blame] | 52 | #include "io/FileSystem.h" | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 53 | #include "io/Util.h" | 
| Adam Lesinski | a40e972 | 2015-11-24 19:11:46 -0800 | [diff] [blame] | 54 | #include "io/ZipArchive.h" | 
| Adam Lesinski | ca5638f | 2015-10-21 14:42:43 -0700 | [diff] [blame] | 55 | #include "java/JavaClassGenerator.h" | 
|  | 56 | #include "java/ManifestClassGenerator.h" | 
|  | 57 | #include "java/ProguardRules.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 58 | #include "link/Linkers.h" | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 59 | #include "link/ManifestFixer.h" | 
| Adam Lesinski | 34a1687 | 2018-02-23 16:18:10 -0800 | [diff] [blame] | 60 | #include "link/NoDefaultResourceRemover.h" | 
| Adam Lesinski | 467f171 | 2015-11-16 17:35:44 -0800 | [diff] [blame] | 61 | #include "link/ReferenceLinker.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 62 | #include "link/TableMerger.h" | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 63 | #include "link/XmlCompatVersioner.h" | 
| Adam Lesinski | d48944a | 2017-02-21 14:22:30 -0800 | [diff] [blame] | 64 | #include "optimize/ResourceDeduper.h" | 
|  | 65 | #include "optimize/VersionCollapser.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 66 | #include "process/IResourceTableConsumer.h" | 
|  | 67 | #include "process/SymbolTable.h" | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 68 | #include "split/TableSplitter.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 69 | #include "util/Files.h" | 
| Adam Lesinski | 467f171 | 2015-11-16 17:35:44 -0800 | [diff] [blame] | 70 | #include "xml/XmlDom.h" | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 71 |  | 
| Adam Lesinski | efeb7af | 2017-08-02 14:57:43 -0700 | [diff] [blame] | 72 | using ::aapt::io::FileInputStream; | 
|  | 73 | using ::android::StringPiece; | 
|  | 74 | using ::android::base::StringPrintf; | 
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 75 |  | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 76 | namespace aapt { | 
|  | 77 |  | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 78 | class LinkContext : public IAaptContext { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 79 | public: | 
| Chris Warrington | 820d72a | 2017-04-27 15:27:01 +0100 | [diff] [blame] | 80 | LinkContext(IDiagnostics* diagnostics) | 
|  | 81 | : diagnostics_(diagnostics), name_mangler_({}), symbols_(&name_mangler_) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 82 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 83 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 84 | PackageType GetPackageType() override { | 
|  | 85 | return package_type_; | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | void SetPackageType(PackageType type) { | 
|  | 89 | package_type_ = type; | 
|  | 90 | } | 
|  | 91 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 92 | IDiagnostics* GetDiagnostics() override { | 
| Chris Warrington | 820d72a | 2017-04-27 15:27:01 +0100 | [diff] [blame] | 93 | return diagnostics_; | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 94 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 95 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 96 | NameMangler* GetNameMangler() override { | 
|  | 97 | return &name_mangler_; | 
|  | 98 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 99 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 100 | void SetNameManglerPolicy(const NameManglerPolicy& policy) { | 
|  | 101 | name_mangler_ = NameMangler(policy); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 102 | } | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 103 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 104 | const std::string& GetCompilationPackage() override { | 
|  | 105 | return compilation_package_; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 106 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 107 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 108 | void SetCompilationPackage(const StringPiece& package_name) { | 
| Adam Lesinski | d5083f6 | 2017-01-16 15:07:21 -0800 | [diff] [blame] | 109 | compilation_package_ = package_name.to_string(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 110 | } | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 111 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 112 | uint8_t GetPackageId() override { | 
|  | 113 | return package_id_; | 
|  | 114 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 115 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 116 | void SetPackageId(uint8_t id) { | 
|  | 117 | package_id_ = id; | 
|  | 118 | } | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 119 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 120 | SymbolTable* GetExternalSymbols() override { | 
|  | 121 | return &symbols_; | 
|  | 122 | } | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 123 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 124 | bool IsVerbose() override { | 
|  | 125 | return verbose_; | 
|  | 126 | } | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 127 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 128 | void SetVerbose(bool val) { | 
|  | 129 | verbose_ = val; | 
|  | 130 | } | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 131 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 132 | int GetMinSdkVersion() override { | 
|  | 133 | return min_sdk_version_; | 
|  | 134 | } | 
| Adam Lesinski | fb6312f | 2016-06-28 14:40:32 -0700 | [diff] [blame] | 135 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 136 | void SetMinSdkVersion(int minSdk) { | 
|  | 137 | min_sdk_version_ = minSdk; | 
|  | 138 | } | 
| Adam Lesinski | fb6312f | 2016-06-28 14:40:32 -0700 | [diff] [blame] | 139 |  | 
| Chris Warrington | 481f027 | 2018-02-06 14:03:39 +0000 | [diff] [blame] | 140 | bool IsAutoNamespace() override { | 
|  | 141 | return auto_namespace_; | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | void SetAutoNamespace(bool val) { | 
|  | 145 | auto_namespace_ = val; | 
|  | 146 | } | 
|  | 147 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 148 | private: | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 149 | DISALLOW_COPY_AND_ASSIGN(LinkContext); | 
|  | 150 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 151 | PackageType package_type_ = PackageType::kApp; | 
| Chris Warrington | 820d72a | 2017-04-27 15:27:01 +0100 | [diff] [blame] | 152 | IDiagnostics* diagnostics_; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 153 | NameMangler name_mangler_; | 
|  | 154 | std::string compilation_package_; | 
|  | 155 | uint8_t package_id_ = 0x0; | 
|  | 156 | SymbolTable symbols_; | 
|  | 157 | bool verbose_ = false; | 
|  | 158 | int min_sdk_version_ = 0; | 
| Chris Warrington | 481f027 | 2018-02-06 14:03:39 +0000 | [diff] [blame] | 159 | bool auto_namespace_ = false; | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 160 | }; | 
|  | 161 |  | 
| Adam Lesinski | 1e4b0e5 | 2017-04-27 15:01:10 -0700 | [diff] [blame] | 162 | // A custom delegate that generates compatible pre-O IDs for use with feature splits. | 
|  | 163 | // Feature splits use package IDs > 7f, which in Java (since Java doesn't have unsigned ints) | 
|  | 164 | // is interpreted as a negative number. Some verification was wrongly assuming negative values | 
|  | 165 | // were invalid. | 
|  | 166 | // | 
|  | 167 | // This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE, | 
|  | 168 | // where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such | 
|  | 169 | // an overlap exists. | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 170 | // | 
|  | 171 | // See b/37498913. | 
| Adam Lesinski | 1e4b0e5 | 2017-04-27 15:01:10 -0700 | [diff] [blame] | 172 | class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate { | 
|  | 173 | public: | 
|  | 174 | FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) { | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | virtual ~FeatureSplitSymbolTableDelegate() = default; | 
|  | 178 |  | 
|  | 179 | virtual std::unique_ptr<SymbolTable::Symbol> FindByName( | 
|  | 180 | const ResourceName& name, | 
|  | 181 | const std::vector<std::unique_ptr<ISymbolSource>>& sources) override { | 
|  | 182 | std::unique_ptr<SymbolTable::Symbol> symbol = | 
|  | 183 | DefaultSymbolTableDelegate::FindByName(name, sources); | 
|  | 184 | if (symbol == nullptr) { | 
|  | 185 | return {}; | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 | // Check to see if this is an 'id' with the target package. | 
|  | 189 | if (name.type == ResourceType::kId && symbol->id) { | 
|  | 190 | ResourceId* id = &symbol->id.value(); | 
|  | 191 | if (id->package_id() > kAppPackageId) { | 
|  | 192 | // Rewrite the resource ID to be compatible pre-O. | 
|  | 193 | ResourceId rewritten_id(kAppPackageId, id->package_id(), id->entry_id()); | 
|  | 194 |  | 
|  | 195 | // Check that this doesn't overlap another resource. | 
|  | 196 | if (DefaultSymbolTableDelegate::FindById(rewritten_id, sources) != nullptr) { | 
|  | 197 | // The ID overlaps, so log a message (since this is a weird failure) and fail. | 
|  | 198 | context_->GetDiagnostics()->Error(DiagMessage() << "Failed to rewrite " << name | 
|  | 199 | << " for pre-O feature split support"); | 
|  | 200 | return {}; | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | if (context_->IsVerbose()) { | 
|  | 204 | context_->GetDiagnostics()->Note(DiagMessage() << "rewriting " << name << " (" << *id | 
|  | 205 | << ") -> (" << rewritten_id << ")"); | 
|  | 206 | } | 
|  | 207 |  | 
|  | 208 | *id = rewritten_id; | 
|  | 209 | } | 
|  | 210 | } | 
|  | 211 | return symbol; | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 | private: | 
|  | 215 | DISALLOW_COPY_AND_ASSIGN(FeatureSplitSymbolTableDelegate); | 
|  | 216 |  | 
|  | 217 | IAaptContext* context_; | 
|  | 218 | }; | 
|  | 219 |  | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 220 | static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res, | 
|  | 221 | const StringPiece& path, bool keep_raw_values, bool utf16, | 
|  | 222 | OutputFormat format, IArchiveWriter* writer) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 223 | if (context->IsVerbose()) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 224 | context->GetDiagnostics()->Note(DiagMessage(path) << "writing to archive (keep_raw_values=" | 
|  | 225 | << (keep_raw_values ? "true" : "false") | 
|  | 226 | << ")"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 227 | } | 
|  | 228 |  | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 229 | switch (format) { | 
|  | 230 | case OutputFormat::kApk: { | 
|  | 231 | BigBuffer buffer(1024); | 
|  | 232 | XmlFlattenerOptions options = {}; | 
|  | 233 | options.keep_raw_values = keep_raw_values; | 
|  | 234 | options.use_utf16 = utf16; | 
|  | 235 | XmlFlattener flattener(&buffer, options); | 
|  | 236 | if (!flattener.Consume(context, &xml_res)) { | 
|  | 237 | return false; | 
|  | 238 | } | 
|  | 239 |  | 
|  | 240 | io::BigBufferInputStream input_stream(&buffer); | 
|  | 241 | return io::CopyInputStreamToArchive(context, &input_stream, path.to_string(), | 
|  | 242 | ArchiveEntry::kCompress, writer); | 
|  | 243 | } break; | 
|  | 244 |  | 
|  | 245 | case OutputFormat::kProto: { | 
|  | 246 | pb::XmlNode pb_node; | 
|  | 247 | SerializeXmlResourceToPb(xml_res, &pb_node); | 
|  | 248 | return io::CopyProtoToArchive(context, &pb_node, path.to_string(), ArchiveEntry::kCompress, | 
|  | 249 | writer); | 
|  | 250 | } break; | 
|  | 251 | } | 
|  | 252 | return false; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 253 | } | 
|  | 254 |  | 
| Adam Lesinski | efeb7af | 2017-08-02 14:57:43 -0700 | [diff] [blame] | 255 | // Inflates an XML file from the source path. | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 256 | static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, IDiagnostics* diag) { | 
| Adam Lesinski | efeb7af | 2017-08-02 14:57:43 -0700 | [diff] [blame] | 257 | FileInputStream fin(path); | 
|  | 258 | if (fin.HadError()) { | 
|  | 259 | diag->Error(DiagMessage(path) << "failed to load XML file: " << fin.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 260 | return {}; | 
|  | 261 | } | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 262 | return xml::Inflate(&fin, diag, Source(path)); | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 263 | } | 
|  | 264 |  | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 265 | struct ResourceFileFlattenerOptions { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 266 | bool no_auto_version = false; | 
|  | 267 | bool no_version_vectors = false; | 
| Yuichi Araki | 4d35cca | 2017-01-18 20:42:17 +0900 | [diff] [blame] | 268 | bool no_version_transitions = false; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 269 | bool no_xml_namespaces = false; | 
|  | 270 | bool keep_raw_values = false; | 
|  | 271 | bool do_not_compress_anything = false; | 
|  | 272 | bool update_proguard_spec = false; | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 273 | OutputFormat output_format = OutputFormat::kApk; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 274 | std::unordered_set<std::string> extensions_to_not_compress; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 275 | }; | 
|  | 276 |  | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 277 | // A sampling of public framework resource IDs. | 
|  | 278 | struct R { | 
|  | 279 | struct attr { | 
|  | 280 | enum : uint32_t { | 
|  | 281 | paddingLeft = 0x010100d6u, | 
|  | 282 | paddingRight = 0x010100d8u, | 
|  | 283 | paddingHorizontal = 0x0101053du, | 
|  | 284 |  | 
|  | 285 | paddingTop = 0x010100d7u, | 
|  | 286 | paddingBottom = 0x010100d9u, | 
|  | 287 | paddingVertical = 0x0101053eu, | 
|  | 288 |  | 
|  | 289 | layout_marginLeft = 0x010100f7u, | 
|  | 290 | layout_marginRight = 0x010100f9u, | 
|  | 291 | layout_marginHorizontal = 0x0101053bu, | 
|  | 292 |  | 
|  | 293 | layout_marginTop = 0x010100f8u, | 
|  | 294 | layout_marginBottom = 0x010100fau, | 
|  | 295 | layout_marginVertical = 0x0101053cu, | 
|  | 296 | }; | 
|  | 297 | }; | 
|  | 298 | }; | 
|  | 299 |  | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 300 | class ResourceFileFlattener { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 301 | public: | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 302 | ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 303 | proguard::KeepSet* keep_set); | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 304 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 305 | bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer); | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 306 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 307 | private: | 
|  | 308 | struct FileOperation { | 
|  | 309 | ConfigDescription config; | 
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 310 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 311 | // The entry this file came from. | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 312 | ResourceEntry* entry; | 
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 313 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 314 | // The file to copy as-is. | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 315 | io::IFile* file_to_copy; | 
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 316 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 317 | // The XML to process and flatten. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 318 | std::unique_ptr<xml::XmlResource> xml_to_flatten; | 
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 319 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 320 | // The destination to write this file to. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 321 | std::string dst_path; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 322 | }; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 323 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 324 | uint32_t GetCompressionFlags(const StringPiece& str); | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 325 |  | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 326 | std::vector<std::unique_ptr<xml::XmlResource>> LinkAndVersionXmlFile(ResourceTable* table, | 
|  | 327 | FileOperation* file_op); | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 328 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 329 | ResourceFileFlattenerOptions options_; | 
|  | 330 | IAaptContext* context_; | 
|  | 331 | proguard::KeepSet* keep_set_; | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 332 | XmlCompatVersioner::Rules rules_; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 333 | }; | 
|  | 334 |  | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 335 | ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions& options, | 
|  | 336 | IAaptContext* context, proguard::KeepSet* keep_set) | 
|  | 337 | : options_(options), context_(context), keep_set_(keep_set) { | 
|  | 338 | SymbolTable* symm = context_->GetExternalSymbols(); | 
|  | 339 |  | 
|  | 340 | // Build up the rules for degrading newer attributes to older ones. | 
|  | 341 | // NOTE(adamlesinski): These rules are hardcoded right now, but they should be | 
|  | 342 | // generated from the attribute definitions themselves (b/62028956). | 
|  | 343 | if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingHorizontal)) { | 
|  | 344 | std::vector<ReplacementAttr> replacements{ | 
| Adam Lesinski | 73bff1e | 2017-12-08 16:06:10 -0800 | [diff] [blame] | 345 | {"paddingLeft", R::attr::paddingLeft, Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
|  | 346 | {"paddingRight", R::attr::paddingRight, Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 347 | }; | 
|  | 348 | rules_[R::attr::paddingHorizontal] = | 
|  | 349 | util::make_unique<DegradeToManyRule>(std::move(replacements)); | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingVertical)) { | 
|  | 353 | std::vector<ReplacementAttr> replacements{ | 
| Adam Lesinski | 73bff1e | 2017-12-08 16:06:10 -0800 | [diff] [blame] | 354 | {"paddingTop", R::attr::paddingTop, Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
|  | 355 | {"paddingBottom", R::attr::paddingBottom, Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 356 | }; | 
|  | 357 | rules_[R::attr::paddingVertical] = | 
|  | 358 | util::make_unique<DegradeToManyRule>(std::move(replacements)); | 
|  | 359 | } | 
|  | 360 |  | 
|  | 361 | if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginHorizontal)) { | 
|  | 362 | std::vector<ReplacementAttr> replacements{ | 
|  | 363 | {"layout_marginLeft", R::attr::layout_marginLeft, | 
| Adam Lesinski | 73bff1e | 2017-12-08 16:06:10 -0800 | [diff] [blame] | 364 | Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 365 | {"layout_marginRight", R::attr::layout_marginRight, | 
| Adam Lesinski | 73bff1e | 2017-12-08 16:06:10 -0800 | [diff] [blame] | 366 | Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 367 | }; | 
|  | 368 | rules_[R::attr::layout_marginHorizontal] = | 
|  | 369 | util::make_unique<DegradeToManyRule>(std::move(replacements)); | 
|  | 370 | } | 
|  | 371 |  | 
|  | 372 | if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginVertical)) { | 
|  | 373 | std::vector<ReplacementAttr> replacements{ | 
|  | 374 | {"layout_marginTop", R::attr::layout_marginTop, | 
| Adam Lesinski | 73bff1e | 2017-12-08 16:06:10 -0800 | [diff] [blame] | 375 | Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 376 | {"layout_marginBottom", R::attr::layout_marginBottom, | 
| Adam Lesinski | 73bff1e | 2017-12-08 16:06:10 -0800 | [diff] [blame] | 377 | Attribute(android::ResTable_map::TYPE_DIMENSION)}, | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 378 | }; | 
|  | 379 | rules_[R::attr::layout_marginVertical] = | 
|  | 380 | util::make_unique<DegradeToManyRule>(std::move(replacements)); | 
|  | 381 | } | 
|  | 382 | } | 
|  | 383 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 384 | uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) { | 
|  | 385 | if (options_.do_not_compress_anything) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 386 | return 0; | 
|  | 387 | } | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 388 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 389 | for (const std::string& extension : options_.extensions_to_not_compress) { | 
|  | 390 | if (util::EndsWith(str, extension)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 391 | return 0; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 392 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 393 | } | 
|  | 394 | return ArchiveEntry::kCompress; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 395 | } | 
|  | 396 |  | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 397 | static bool IsTransitionElement(const std::string& name) { | 
|  | 398 | return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" || | 
|  | 399 | name == "changeImageTransform" || name == "changeTransform" || | 
|  | 400 | name == "changeClipBounds" || name == "autoTransition" || name == "recolor" || | 
|  | 401 | name == "changeScroll" || name == "transitionSet" || name == "transition" || | 
|  | 402 | name == "transitionManager"; | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | static bool IsVectorElement(const std::string& name) { | 
|  | 406 | return name == "vector" || name == "animated-vector" || name == "pathInterpolator" || | 
| Nick Butcher | e78a816 | 2018-01-09 15:24:21 +0000 | [diff] [blame] | 407 | name == "objectAnimator" || name == "gradient" || name == "animated-selector"; | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 408 | } | 
|  | 409 |  | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 410 | template <typename T> | 
|  | 411 | std::vector<T> make_singleton_vec(T&& val) { | 
|  | 412 | std::vector<T> vec; | 
|  | 413 | vec.emplace_back(std::forward<T>(val)); | 
|  | 414 | return vec; | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVersionXmlFile( | 
|  | 418 | ResourceTable* table, FileOperation* file_op) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 419 | xml::XmlResource* doc = file_op->xml_to_flatten.get(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 420 | const Source& src = doc->file.source; | 
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 421 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 422 | if (context_->IsVerbose()) { | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 423 | context_->GetDiagnostics()->Note(DiagMessage() | 
|  | 424 | << "linking " << src.path << " (" << doc->file.name << ")"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 425 | } | 
|  | 426 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 427 | // First, strip out any tools namespace attributes. AAPT stripped them out early, which means | 
|  | 428 | // that existing projects have out-of-date references which pass compilation. | 
|  | 429 | xml::StripAndroidStudioAttributes(doc->root.get()); | 
|  | 430 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 431 | XmlReferenceLinker xml_linker; | 
|  | 432 | if (!xml_linker.Consume(context_, doc)) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 433 | return {}; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 434 | } | 
|  | 435 |  | 
| Ryan Mitchell | 9a2f6e6 | 2018-05-23 14:23:18 -0700 | [diff] [blame] | 436 | if (options_.update_proguard_spec && !proguard::CollectProguardRules(context_, doc, keep_set_)) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 437 | return {}; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 438 | } | 
|  | 439 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 440 | if (options_.no_xml_namespaces) { | 
|  | 441 | XmlNamespaceRemover namespace_remover; | 
|  | 442 | if (!namespace_remover.Consume(context_, doc)) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 443 | return {}; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 444 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 445 | } | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 446 |  | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 447 | if (options_.no_auto_version) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 448 | return make_singleton_vec(std::move(file_op->xml_to_flatten)); | 
|  | 449 | } | 
| Alexandria Cornwall | a7cc3f1 | 2016-08-16 13:33:32 -0700 | [diff] [blame] | 450 |  | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 451 | if (options_.no_version_vectors || options_.no_version_transitions) { | 
|  | 452 | // Skip this if it is a vector or animated-vector. | 
| Adam Lesinski | 6b37299 | 2017-08-09 10:54:23 -0700 | [diff] [blame] | 453 | xml::Element* el = doc->root.get(); | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 454 | if (el && el->namespace_uri.empty()) { | 
|  | 455 | if ((options_.no_version_vectors && IsVectorElement(el->name)) || | 
|  | 456 | (options_.no_version_transitions && IsTransitionElement(el->name))) { | 
|  | 457 | return make_singleton_vec(std::move(file_op->xml_to_flatten)); | 
|  | 458 | } | 
|  | 459 | } | 
|  | 460 | } | 
|  | 461 |  | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 462 | const ConfigDescription& config = file_op->config; | 
|  | 463 | ResourceEntry* entry = file_op->entry; | 
|  | 464 |  | 
|  | 465 | XmlCompatVersioner xml_compat_versioner(&rules_); | 
|  | 466 | const util::Range<ApiVersion> api_range{config.sdkVersion, | 
|  | 467 | FindNextApiVersionForConfig(entry, config)}; | 
|  | 468 | return xml_compat_versioner.Process(context_, doc, api_range); | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 469 | } | 
|  | 470 |  | 
| Adam Lesinski | a65bbdf | 2018-02-15 12:39:44 -0800 | [diff] [blame] | 471 | ResourceFile::Type XmlFileTypeForOutputFormat(OutputFormat format) { | 
|  | 472 | switch (format) { | 
|  | 473 | case OutputFormat::kApk: | 
|  | 474 | return ResourceFile::Type::kBinaryXml; | 
|  | 475 | case OutputFormat::kProto: | 
|  | 476 | return ResourceFile::Type::kProtoXml; | 
|  | 477 | } | 
|  | 478 | LOG_ALWAYS_FATAL("unreachable"); | 
|  | 479 | return ResourceFile::Type::kUnknown; | 
|  | 480 | } | 
|  | 481 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 482 | bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archive_writer) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 483 | bool error = false; | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 484 | std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 485 |  | 
| Adam Koski | dc21dea | 2017-07-21 10:55:27 -0700 | [diff] [blame] | 486 | proguard::CollectResourceReferences(context_, table, keep_set_); | 
|  | 487 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 488 | for (auto& pkg : table->packages) { | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 489 | CHECK(!pkg->name.empty()) << "Packages must have names when being linked"; | 
|  | 490 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 491 | for (auto& type : pkg->types) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 492 | // Sort by config and name, so that we get better locality in the zip file. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 493 | config_sorted_files.clear(); | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 494 | std::queue<FileOperation> file_operations; | 
| Adam Lesinski | 355f285 | 2016-02-13 20:26:45 -0800 | [diff] [blame] | 495 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 496 | // Populate the queue with all files in the ResourceTable. | 
|  | 497 | for (auto& entry : type->entries) { | 
| Adam Lesinski | bb94f32 | 2017-07-12 07:41:55 -0700 | [diff] [blame] | 498 | for (auto& config_value : entry->values) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 499 | // WARNING! Do not insert or remove any resources while executing in this scope. It will | 
|  | 500 | // corrupt the iteration order. | 
|  | 501 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 502 | FileReference* file_ref = ValueCast<FileReference>(config_value->value.get()); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 503 | if (!file_ref) { | 
| Adam Lesinski | bf0bd0f | 2016-06-01 15:31:50 -0700 | [diff] [blame] | 504 | continue; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 505 | } | 
| Adam Lesinski | bf0bd0f | 2016-06-01 15:31:50 -0700 | [diff] [blame] | 506 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 507 | io::IFile* file = file_ref->file; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 508 | if (!file) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 509 | context_->GetDiagnostics()->Error(DiagMessage(file_ref->GetSource()) | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 510 | << "file not found"); | 
| Adam Lesinski | bf0bd0f | 2016-06-01 15:31:50 -0700 | [diff] [blame] | 511 | return false; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 512 | } | 
|  | 513 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 514 | FileOperation file_op; | 
|  | 515 | file_op.entry = entry.get(); | 
|  | 516 | file_op.dst_path = *file_ref->path; | 
|  | 517 | file_op.config = config_value->config; | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 518 | file_op.file_to_copy = file; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 519 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 520 | if (type->type != ResourceType::kRaw && | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 521 | (file_ref->type == ResourceFile::Type::kBinaryXml || | 
|  | 522 | file_ref->type == ResourceFile::Type::kProtoXml)) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 523 | std::unique_ptr<io::IData> data = file->OpenAsData(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 524 | if (!data) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 525 | context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 526 | << "failed to open file"); | 
|  | 527 | return false; | 
|  | 528 | } | 
|  | 529 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 530 | if (file_ref->type == ResourceFile::Type::kProtoXml) { | 
|  | 531 | pb::XmlNode pb_xml_node; | 
|  | 532 | if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) { | 
|  | 533 | context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 534 | << "failed to parse proto XML"); | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 535 | return false; | 
|  | 536 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 537 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 538 | std::string error; | 
|  | 539 | file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error); | 
|  | 540 | if (file_op.xml_to_flatten == nullptr) { | 
|  | 541 | context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 542 | << "failed to deserialize proto XML: " << error); | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 543 | return false; | 
|  | 544 | } | 
|  | 545 | } else { | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 546 | std::string error_str; | 
|  | 547 | file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), &error_str); | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 548 | if (file_op.xml_to_flatten == nullptr) { | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 549 | context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) | 
|  | 550 | << "failed to parse binary XML: " << error_str); | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 551 | return false; | 
|  | 552 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 553 | } | 
|  | 554 |  | 
| Adam Lesinski | a65bbdf | 2018-02-15 12:39:44 -0800 | [diff] [blame] | 555 | // Update the type that this file will be written as. | 
|  | 556 | file_ref->type = XmlFileTypeForOutputFormat(options_.output_format); | 
|  | 557 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 558 | file_op.xml_to_flatten->file.config = config_value->config; | 
|  | 559 | file_op.xml_to_flatten->file.source = file_ref->GetSource(); | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 560 | file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 561 | } | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 562 |  | 
|  | 563 | // NOTE(adamlesinski): Explicitly construct a StringPiece here, or | 
|  | 564 | // else we end up copying the string in the std::make_pair() method, | 
|  | 565 | // then creating a StringPiece from the copy, which would cause us | 
|  | 566 | // to end up referencing garbage in the map. | 
|  | 567 | const StringPiece entry_name(entry->name); | 
|  | 568 | config_sorted_files[std::make_pair(config_value->config, entry_name)] = | 
|  | 569 | std::move(file_op); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 570 | } | 
|  | 571 | } | 
|  | 572 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 573 | // Now flatten the sorted values. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 574 | for (auto& map_entry : config_sorted_files) { | 
|  | 575 | const ConfigDescription& config = map_entry.first.first; | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 576 | FileOperation& file_op = map_entry.second; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 577 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 578 | if (file_op.xml_to_flatten) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 579 | std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs = | 
|  | 580 | LinkAndVersionXmlFile(table, &file_op); | 
| Adam Lesinski | c0a5e1e | 2017-08-07 11:56:32 -0700 | [diff] [blame] | 581 | if (versioned_docs.empty()) { | 
|  | 582 | error = true; | 
|  | 583 | continue; | 
|  | 584 | } | 
|  | 585 |  | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 586 | for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) { | 
|  | 587 | std::string dst_path = file_op.dst_path; | 
|  | 588 | if (doc->file.config != file_op.config) { | 
|  | 589 | // Only add the new versioned configurations. | 
|  | 590 | if (context_->IsVerbose()) { | 
|  | 591 | context_->GetDiagnostics()->Note(DiagMessage(doc->file.source) | 
|  | 592 | << "auto-versioning resource from config '" | 
|  | 593 | << config << "' -> '" << doc->file.config << "'"); | 
|  | 594 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 595 |  | 
| Adam Lesinski | a65bbdf | 2018-02-15 12:39:44 -0800 | [diff] [blame] | 596 | const ResourceFile& file = doc->file; | 
|  | 597 | dst_path = ResourceUtils::BuildResourceFileName(file, context_->GetNameMangler()); | 
|  | 598 |  | 
|  | 599 | std::unique_ptr<FileReference> file_ref = | 
|  | 600 | util::make_unique<FileReference>(table->string_pool.MakeRef(dst_path)); | 
|  | 601 | file_ref->SetSource(doc->file.source); | 
|  | 602 | // Update the output format of this XML file. | 
|  | 603 | file_ref->type = XmlFileTypeForOutputFormat(options_.output_format); | 
|  | 604 | if (!table->AddResourceMangled(file.name, file.config, {}, std::move(file_ref), | 
|  | 605 | context_->GetDiagnostics())) { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 606 | return false; | 
|  | 607 | } | 
|  | 608 | } | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 609 |  | 
|  | 610 | error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values, | 
|  | 611 | false /*utf16*/, options_.output_format, archive_writer); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 612 | } | 
|  | 613 | } else { | 
| Adam Lesinski | c744ae8 | 2017-05-17 19:28:38 -0700 | [diff] [blame] | 614 | error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path, | 
|  | 615 | GetCompressionFlags(file_op.dst_path), archive_writer); | 
| Adam Lesinski | bf0bd0f | 2016-06-01 15:31:50 -0700 | [diff] [blame] | 616 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 617 | } | 
| Adam Lesinski | bf0bd0f | 2016-06-01 15:31:50 -0700 | [diff] [blame] | 618 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 619 | } | 
|  | 620 | return !error; | 
|  | 621 | } | 
|  | 622 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 623 | static bool WriteStableIdMapToPath(IDiagnostics* diag, | 
|  | 624 | const std::unordered_map<ResourceName, ResourceId>& id_map, | 
|  | 625 | const std::string& id_map_path) { | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 626 | io::FileOutputStream fout(id_map_path); | 
|  | 627 | if (fout.HadError()) { | 
|  | 628 | diag->Error(DiagMessage(id_map_path) << "failed to open: " << fout.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 629 | return false; | 
|  | 630 | } | 
|  | 631 |  | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 632 | text::Printer printer(&fout); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 633 | for (const auto& entry : id_map) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 634 | const ResourceName& name = entry.first; | 
|  | 635 | const ResourceId& id = entry.second; | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 636 | printer.Print(name.to_string()); | 
|  | 637 | printer.Print(" = "); | 
|  | 638 | printer.Println(id.to_string()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 639 | } | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 640 | fout.Flush(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 641 |  | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 642 | if (fout.HadError()) { | 
|  | 643 | diag->Error(DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 644 | return false; | 
|  | 645 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 646 | return true; | 
|  | 647 | } | 
|  | 648 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 649 | static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path, | 
|  | 650 | std::unordered_map<ResourceName, ResourceId>* out_id_map) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 651 | std::string content; | 
| Adam Lesinski | 2354b56 | 2017-05-26 16:31:38 -0700 | [diff] [blame] | 652 | if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 653 | diag->Error(DiagMessage(path) << "failed reading stable ID file"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 654 | return false; | 
|  | 655 | } | 
|  | 656 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 657 | out_id_map->clear(); | 
|  | 658 | size_t line_no = 0; | 
|  | 659 | for (StringPiece line : util::Tokenize(content, '\n')) { | 
|  | 660 | line_no++; | 
|  | 661 | line = util::TrimWhitespace(line); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 662 | if (line.empty()) { | 
|  | 663 | continue; | 
|  | 664 | } | 
|  | 665 |  | 
|  | 666 | auto iter = std::find(line.begin(), line.end(), '='); | 
|  | 667 | if (iter == line.end()) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 668 | diag->Error(DiagMessage(Source(path, line_no)) << "missing '='"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 669 | return false; | 
|  | 670 | } | 
|  | 671 |  | 
|  | 672 | ResourceNameRef name; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 673 | StringPiece res_name_str = | 
|  | 674 | util::TrimWhitespace(line.substr(0, std::distance(line.begin(), iter))); | 
|  | 675 | if (!ResourceUtils::ParseResourceName(res_name_str, &name)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 676 | diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource name '" << res_name_str | 
|  | 677 | << "'"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 678 | return false; | 
|  | 679 | } | 
|  | 680 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 681 | const size_t res_id_start_idx = std::distance(line.begin(), iter) + 1; | 
|  | 682 | const size_t res_id_str_len = line.size() - res_id_start_idx; | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 683 | StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 684 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 685 | Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str); | 
|  | 686 | if (!maybe_id) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 687 | diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '" << res_id_str | 
|  | 688 | << "'"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 689 | return false; | 
|  | 690 | } | 
|  | 691 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 692 | (*out_id_map)[name.ToResourceName()] = maybe_id.value(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 693 | } | 
|  | 694 | return true; | 
| Adam Lesinski | bf0bd0f | 2016-06-01 15:31:50 -0700 | [diff] [blame] | 695 | } | 
|  | 696 |  | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 697 | static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) { | 
|  | 698 | using namespace android; | 
|  | 699 |  | 
|  | 700 | // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so | 
|  | 701 | // we're looking for the first attribute resource in the system package. | 
|  | 702 | const ResTable& table = assets.getResources(true); | 
|  | 703 | Res_value val; | 
|  | 704 | ssize_t idx = table.getResource(0x01010000, &val, true); | 
|  | 705 | if (idx != NO_ERROR) { | 
|  | 706 | // Try as a bag. | 
|  | 707 | const ResTable::bag_entry* entry; | 
|  | 708 | ssize_t cnt = table.lockBag(0x01010000, &entry); | 
|  | 709 | if (cnt >= 0) { | 
|  | 710 | idx = entry->stringBlock; | 
|  | 711 | } | 
|  | 712 | table.unlockBag(entry); | 
|  | 713 | } | 
|  | 714 |  | 
|  | 715 | if (idx < 0) { | 
|  | 716 | return 0; | 
|  | 717 | } | 
|  | 718 | return table.getTableCookie(idx); | 
|  | 719 | } | 
|  | 720 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 721 | class Linker { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 722 | public: | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 723 | Linker(LinkContext* context, const LinkOptions& options) | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 724 | : options_(options), | 
|  | 725 | context_(context), | 
|  | 726 | final_table_(), | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 727 | file_collection_(util::make_unique<io::FileCollection>()) { | 
|  | 728 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 729 |  | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 730 | void ExtractCompileSdkVersions(android::AssetManager* assets) { | 
|  | 731 | using namespace android; | 
|  | 732 |  | 
|  | 733 | int32_t cookie = FindFrameworkAssetManagerCookie(*assets); | 
|  | 734 | if (cookie == 0) { | 
|  | 735 | // No Framework assets loaded. Not a failure. | 
|  | 736 | return; | 
|  | 737 | } | 
|  | 738 |  | 
|  | 739 | std::unique_ptr<Asset> manifest( | 
|  | 740 | assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER)); | 
|  | 741 | if (manifest == nullptr) { | 
|  | 742 | // No errors. | 
|  | 743 | return; | 
|  | 744 | } | 
|  | 745 |  | 
|  | 746 | std::string error; | 
|  | 747 | std::unique_ptr<xml::XmlResource> manifest_xml = | 
|  | 748 | xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error); | 
|  | 749 | if (manifest_xml == nullptr) { | 
|  | 750 | // No errors. | 
|  | 751 | return; | 
|  | 752 | } | 
|  | 753 |  | 
| Todd Kennedy | 9f6dec1 | 2018-04-20 12:29:29 -0700 | [diff] [blame] | 754 | if (!options_.manifest_fixer_options.compile_sdk_version) { | 
|  | 755 | xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode"); | 
|  | 756 | if (attr != nullptr) { | 
|  | 757 | Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version; | 
|  | 758 | if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) { | 
|  | 759 | switch (prim->value.dataType) { | 
|  | 760 | case Res_value::TYPE_INT_DEC: | 
|  | 761 | compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data)); | 
|  | 762 | break; | 
|  | 763 | case Res_value::TYPE_INT_HEX: | 
|  | 764 | compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data); | 
|  | 765 | break; | 
|  | 766 | default: | 
|  | 767 | break; | 
|  | 768 | } | 
|  | 769 | } else if (String* str = ValueCast<String>(attr->compiled_value.get())) { | 
|  | 770 | compile_sdk_version = *str->value; | 
|  | 771 | } else { | 
|  | 772 | compile_sdk_version = attr->value; | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 773 | } | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 774 | } | 
|  | 775 | } | 
|  | 776 |  | 
| Todd Kennedy | 9f6dec1 | 2018-04-20 12:29:29 -0700 | [diff] [blame] | 777 | if (!options_.manifest_fixer_options.compile_sdk_version_codename) { | 
|  | 778 | xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName"); | 
|  | 779 | if (attr != nullptr) { | 
|  | 780 | Maybe<std::string>& compile_sdk_version_codename = | 
|  | 781 | options_.manifest_fixer_options.compile_sdk_version_codename; | 
|  | 782 | if (String* str = ValueCast<String>(attr->compiled_value.get())) { | 
|  | 783 | compile_sdk_version_codename = *str->value; | 
|  | 784 | } else { | 
|  | 785 | compile_sdk_version_codename = attr->value; | 
|  | 786 | } | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 787 | } | 
|  | 788 | } | 
|  | 789 | } | 
|  | 790 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 791 | // Creates a SymbolTable that loads symbols from the various APKs. | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 792 | // Pre-condition: context_->GetCompilationPackage() needs to be set. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 793 | bool LoadSymbolsFromIncludePaths() { | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 794 | auto asset_source = util::make_unique<AssetManagerSymbolSource>(); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 795 | for (const std::string& path : options_.include_paths) { | 
|  | 796 | if (context_->IsVerbose()) { | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 797 | context_->GetDiagnostics()->Note(DiagMessage() << "including " << path); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 798 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 799 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 800 | std::string error; | 
|  | 801 | auto zip_collection = io::ZipFileCollection::Create(path, &error); | 
|  | 802 | if (zip_collection == nullptr) { | 
|  | 803 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to open APK: " << error); | 
|  | 804 | return false; | 
|  | 805 | } | 
|  | 806 |  | 
|  | 807 | if (zip_collection->FindFile(kProtoResourceTablePath) != nullptr) { | 
|  | 808 | // Load this as a static library include. | 
|  | 809 | std::unique_ptr<LoadedApk> static_apk = LoadedApk::LoadProtoApkFromFileCollection( | 
|  | 810 | Source(path), std::move(zip_collection), context_->GetDiagnostics()); | 
|  | 811 | if (static_apk == nullptr) { | 
|  | 812 | return false; | 
|  | 813 | } | 
|  | 814 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 815 | if (context_->GetPackageType() != PackageType::kStaticLib) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 816 | // Can't include static libraries when not building a static library (they have no IDs | 
|  | 817 | // assigned). | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 818 | context_->GetDiagnostics()->Error( | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 819 | DiagMessage(path) << "can't include static library when not building a static lib"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 820 | return false; | 
|  | 821 | } | 
|  | 822 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 823 | ResourceTable* table = static_apk->GetResourceTable(); | 
|  | 824 |  | 
|  | 825 | // If we are using --no-static-lib-packages, we need to rename the package of this table to | 
|  | 826 | // our compilation package. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 827 | if (options_.no_static_lib_packages) { | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 828 | // Since package names can differ, and multiple packages can exist in a ResourceTable, | 
|  | 829 | // we place the requirement that all static libraries are built with the package | 
|  | 830 | // ID 0x7f. So if one is not found, this is an error. | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 831 | if (ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId)) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 832 | pkg->name = context_->GetCompilationPackage(); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 833 | } else { | 
|  | 834 | context_->GetDiagnostics()->Error(DiagMessage(path) | 
|  | 835 | << "no package with ID 0x7f found in static library"); | 
|  | 836 | return false; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 837 | } | 
|  | 838 | } | 
|  | 839 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 840 | context_->GetExternalSymbols()->AppendSource( | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 841 | util::make_unique<ResourceTableSymbolSource>(table)); | 
|  | 842 | static_library_includes_.push_back(std::move(static_apk)); | 
|  | 843 | } else { | 
|  | 844 | if (!asset_source->AddAssetPath(path)) { | 
|  | 845 | context_->GetDiagnostics()->Error(DiagMessage() | 
|  | 846 | << "failed to load include path " << path); | 
|  | 847 | return false; | 
|  | 848 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 849 | } | 
|  | 850 | } | 
|  | 851 |  | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 852 | // Capture the shared libraries so that the final resource table can be properly flattened | 
|  | 853 | // with support for shared libraries. | 
|  | 854 | for (auto& entry : asset_source->GetAssignedPackageIds()) { | 
| Todd Kennedy | 3251299 | 2018-04-25 16:45:59 -0700 | [diff] [blame] | 855 | if (entry.first == kAppPackageId) { | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 856 | // Capture the included base feature package. | 
|  | 857 | included_feature_base_ = entry.second; | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 858 | } else if (entry.first == kFrameworkPackageId) { | 
|  | 859 | // Try to embed which version of the framework we're compiling against. | 
|  | 860 | // First check if we should use compileSdkVersion at all. Otherwise compilation may fail | 
|  | 861 | // when linking our synthesized 'android:compileSdkVersion' attribute. | 
|  | 862 | std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName( | 
|  | 863 | ResourceName("android", ResourceType::kAttr, "compileSdkVersion")); | 
|  | 864 | if (symbol != nullptr && symbol->is_public) { | 
|  | 865 | // The symbol is present and public, extract the android:versionName and | 
|  | 866 | // android:versionCode from the framework AndroidManifest.xml. | 
|  | 867 | ExtractCompileSdkVersions(asset_source->GetAssetManager()); | 
|  | 868 | } | 
| Todd Kennedy | 3251299 | 2018-04-25 16:45:59 -0700 | [diff] [blame] | 869 | } else if (asset_source->IsPackageDynamic(entry.first)) { | 
|  | 870 | final_table_.included_packages_[entry.first] = entry.second; | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 871 | } | 
|  | 872 | } | 
|  | 873 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 874 | context_->GetExternalSymbols()->AppendSource(std::move(asset_source)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 875 | return true; | 
|  | 876 | } | 
|  | 877 |  | 
| Adam Lesinski | b0c47ef | 2017-03-06 20:05:57 -0800 | [diff] [blame] | 878 | Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 879 | // Make sure the first element is <manifest> with package attribute. | 
| Adam Lesinski | b0c47ef | 2017-03-06 20:05:57 -0800 | [diff] [blame] | 880 | xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get()); | 
|  | 881 | if (manifest_el == nullptr) { | 
|  | 882 | return {}; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 883 | } | 
| Adam Lesinski | b0c47ef | 2017-03-06 20:05:57 -0800 | [diff] [blame] | 884 |  | 
|  | 885 | AppInfo app_info; | 
|  | 886 |  | 
|  | 887 | if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { | 
|  | 888 | diag->Error(DiagMessage(xml_res->file.source) << "root tag must be <manifest>"); | 
|  | 889 | return {}; | 
|  | 890 | } | 
|  | 891 |  | 
|  | 892 | xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); | 
|  | 893 | if (!package_attr) { | 
|  | 894 | diag->Error(DiagMessage(xml_res->file.source) | 
|  | 895 | << "<manifest> must have a 'package' attribute"); | 
|  | 896 | return {}; | 
|  | 897 | } | 
|  | 898 | app_info.package = package_attr->value; | 
|  | 899 |  | 
|  | 900 | if (xml::Attribute* version_code_attr = | 
|  | 901 | manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { | 
|  | 902 | Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value); | 
|  | 903 | if (!maybe_code) { | 
|  | 904 | diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) | 
|  | 905 | << "invalid android:versionCode '" << version_code_attr->value << "'"); | 
|  | 906 | return {}; | 
|  | 907 | } | 
|  | 908 | app_info.version_code = maybe_code.value(); | 
|  | 909 | } | 
|  | 910 |  | 
| Ryan Mitchell | 5fa2bb1 | 2018-07-12 11:24:51 -0700 | [diff] [blame] | 911 | if (xml::Attribute* version_code_major_attr = | 
|  | 912 | manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) { | 
|  | 913 | Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value); | 
|  | 914 | if (!maybe_code) { | 
|  | 915 | diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) | 
|  | 916 | << "invalid android:versionCodeMajor '" | 
|  | 917 | << version_code_major_attr->value << "'"); | 
|  | 918 | return {}; | 
|  | 919 | } | 
|  | 920 | app_info.version_code_major = maybe_code.value(); | 
|  | 921 | } | 
|  | 922 |  | 
| Adam Lesinski | b0c47ef | 2017-03-06 20:05:57 -0800 | [diff] [blame] | 923 | if (xml::Attribute* revision_code_attr = | 
|  | 924 | manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { | 
|  | 925 | Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value); | 
|  | 926 | if (!maybe_code) { | 
|  | 927 | diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) | 
|  | 928 | << "invalid android:revisionCode '" << revision_code_attr->value << "'"); | 
|  | 929 | return {}; | 
|  | 930 | } | 
|  | 931 | app_info.revision_code = maybe_code.value(); | 
|  | 932 | } | 
|  | 933 |  | 
|  | 934 | if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { | 
|  | 935 | if (!split_name_attr->value.empty()) { | 
|  | 936 | app_info.split_name = split_name_attr->value; | 
|  | 937 | } | 
|  | 938 | } | 
|  | 939 |  | 
|  | 940 | if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { | 
|  | 941 | if (xml::Attribute* min_sdk = | 
|  | 942 | uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 943 | app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value); | 
| Adam Lesinski | b0c47ef | 2017-03-06 20:05:57 -0800 | [diff] [blame] | 944 | } | 
|  | 945 | } | 
|  | 946 | return app_info; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 947 | } | 
|  | 948 |  | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 949 | // Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked. | 
|  | 950 | // Postcondition: ResourceTable has only one package left. All others are | 
|  | 951 | // stripped, or there is an error and false is returned. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 952 | bool VerifyNoExternalPackages() { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 953 | auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 954 | return context_->GetCompilationPackage() != pkg->name || !pkg->id || | 
|  | 955 | pkg->id.value() != context_->GetPackageId(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 956 | }; | 
|  | 957 |  | 
|  | 958 | bool error = false; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 959 | for (const auto& package : final_table_.packages) { | 
|  | 960 | if (is_ext_package_func(package)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 961 | // We have a package that is not related to the one we're building! | 
|  | 962 | for (const auto& type : package->types) { | 
|  | 963 | for (const auto& entry : type->entries) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 964 | ResourceNameRef res_name(package->name, type->type, entry->name); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 965 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 966 | for (const auto& config_value : entry->values) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 967 | // Special case the occurrence of an ID that is being generated | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 968 | // for the 'android' package. This is due to legacy reasons. | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 969 | if (ValueCast<Id>(config_value->value.get()) && package->name == "android") { | 
|  | 970 | context_->GetDiagnostics()->Warn(DiagMessage(config_value->value->GetSource()) | 
|  | 971 | << "generated id '" << res_name | 
|  | 972 | << "' for external package '" << package->name | 
|  | 973 | << "'"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 974 | } else { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 975 | context_->GetDiagnostics()->Error(DiagMessage(config_value->value->GetSource()) | 
|  | 976 | << "defined resource '" << res_name | 
|  | 977 | << "' for external package '" << package->name | 
|  | 978 | << "'"); | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 979 | error = true; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 980 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 981 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 982 | } | 
|  | 983 | } | 
|  | 984 | } | 
|  | 985 | } | 
|  | 986 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 987 | auto new_end_iter = std::remove_if(final_table_.packages.begin(), final_table_.packages.end(), | 
|  | 988 | is_ext_package_func); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 989 | final_table_.packages.erase(new_end_iter, final_table_.packages.end()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 990 | return !error; | 
|  | 991 | } | 
|  | 992 |  | 
|  | 993 | /** | 
|  | 994 | * Returns true if no IDs have been set, false otherwise. | 
|  | 995 | */ | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 996 | bool VerifyNoIdsSet() { | 
|  | 997 | for (const auto& package : final_table_.packages) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 998 | for (const auto& type : package->types) { | 
|  | 999 | if (type->id) { | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1000 | context_->GetDiagnostics()->Error(DiagMessage() << "type " << type->type << " has ID " | 
|  | 1001 | << StringPrintf("%02x", type->id.value()) | 
|  | 1002 | << " assigned"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1003 | return false; | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 1004 | } | 
|  | 1005 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1006 | for (const auto& entry : type->entries) { | 
|  | 1007 | if (entry->id) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1008 | ResourceNameRef res_name(package->name, type->type, entry->name); | 
|  | 1009 | context_->GetDiagnostics()->Error( | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1010 | DiagMessage() << "entry " << res_name << " has ID " | 
|  | 1011 | << StringPrintf("%02x", entry->id.value()) << " assigned"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1012 | return false; | 
|  | 1013 | } | 
|  | 1014 | } | 
|  | 1015 | } | 
|  | 1016 | } | 
|  | 1017 | return true; | 
|  | 1018 | } | 
|  | 1019 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1020 | std::unique_ptr<IArchiveWriter> MakeArchiveWriter(const StringPiece& out) { | 
|  | 1021 | if (options_.output_to_directory) { | 
|  | 1022 | return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1023 | } else { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1024 | return CreateZipFileArchiveWriter(context_->GetDiagnostics(), out); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1025 | } | 
|  | 1026 | } | 
|  | 1027 |  | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 1028 | bool FlattenTable(ResourceTable* table, OutputFormat format, IArchiveWriter* writer) { | 
|  | 1029 | switch (format) { | 
|  | 1030 | case OutputFormat::kApk: { | 
|  | 1031 | BigBuffer buffer(1024); | 
|  | 1032 | TableFlattener flattener(options_.table_flattener_options, &buffer); | 
|  | 1033 | if (!flattener.Consume(context_, table)) { | 
|  | 1034 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to flatten resource table"); | 
|  | 1035 | return false; | 
|  | 1036 | } | 
|  | 1037 |  | 
|  | 1038 | io::BigBufferInputStream input_stream(&buffer); | 
|  | 1039 | return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath, | 
|  | 1040 | ArchiveEntry::kAlign, writer); | 
|  | 1041 | } break; | 
|  | 1042 |  | 
|  | 1043 | case OutputFormat::kProto: { | 
|  | 1044 | pb::ResourceTable pb_table; | 
| Ryan Mitchell | a15c2a8 | 2018-03-26 11:05:31 -0700 | [diff] [blame] | 1045 | SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics()); | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 1046 | return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath, | 
|  | 1047 | ArchiveEntry::kCompress, writer); | 
|  | 1048 | } break; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1049 | } | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 1050 | return false; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1051 | } | 
|  | 1052 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1053 | bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate, | 
| Adam Lesinski | 418763f | 2017-04-11 17:36:53 -0700 | [diff] [blame] | 1054 | const StringPiece& out_package, const JavaClassGeneratorOptions& java_options, | 
| Chih-Hung Hsieh | 4dc5812 | 2017-08-03 16:28:10 -0700 | [diff] [blame] | 1055 | const Maybe<std::string>& out_text_symbols_path = {}) { | 
| Izabela Orlowska | 23a6e1e | 2017-12-05 14:52:07 +0000 | [diff] [blame] | 1056 | if (!options_.generate_java_class_path && !out_text_symbols_path) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1057 | return true; | 
|  | 1058 | } | 
|  | 1059 |  | 
| Izabela Orlowska | 23a6e1e | 2017-12-05 14:52:07 +0000 | [diff] [blame] | 1060 | std::string out_path; | 
|  | 1061 | std::unique_ptr<io::FileOutputStream> fout; | 
|  | 1062 | if (options_.generate_java_class_path) { | 
|  | 1063 | out_path = options_.generate_java_class_path.value(); | 
|  | 1064 | file::AppendPath(&out_path, file::PackageToPath(out_package)); | 
|  | 1065 | if (!file::mkdirs(out_path)) { | 
|  | 1066 | context_->GetDiagnostics()->Error(DiagMessage() | 
|  | 1067 | << "failed to create directory '" << out_path << "'"); | 
|  | 1068 | return false; | 
|  | 1069 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1070 |  | 
| Izabela Orlowska | 23a6e1e | 2017-12-05 14:52:07 +0000 | [diff] [blame] | 1071 | file::AppendPath(&out_path, "R.java"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1072 |  | 
| Izabela Orlowska | 23a6e1e | 2017-12-05 14:52:07 +0000 | [diff] [blame] | 1073 | fout = util::make_unique<io::FileOutputStream>(out_path); | 
|  | 1074 | if (fout->HadError()) { | 
|  | 1075 | context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path | 
|  | 1076 | << "': " << fout->GetError()); | 
|  | 1077 | return false; | 
|  | 1078 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1079 | } | 
|  | 1080 |  | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1081 | std::unique_ptr<io::FileOutputStream> fout_text; | 
| Adam Lesinski | 418763f | 2017-04-11 17:36:53 -0700 | [diff] [blame] | 1082 | if (out_text_symbols_path) { | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1083 | fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value()); | 
|  | 1084 | if (fout_text->HadError()) { | 
|  | 1085 | context_->GetDiagnostics()->Error(DiagMessage() | 
|  | 1086 | << "failed writing to '" << out_text_symbols_path.value() | 
|  | 1087 | << "': " << fout_text->GetError()); | 
| Adam Lesinski | 418763f | 2017-04-11 17:36:53 -0700 | [diff] [blame] | 1088 | return false; | 
|  | 1089 | } | 
|  | 1090 | } | 
|  | 1091 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1092 | JavaClassGenerator generator(context_, table, java_options); | 
| Izabela Orlowska | 23a6e1e | 2017-12-05 14:52:07 +0000 | [diff] [blame] | 1093 | if (!generator.Generate(package_name_to_generate, out_package, fout.get(), fout_text.get())) { | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1094 | context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1095 | return false; | 
|  | 1096 | } | 
|  | 1097 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1098 | return true; | 
|  | 1099 | } | 
|  | 1100 |  | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 1101 | bool GenerateJavaClasses() { | 
|  | 1102 | // The set of packages whose R class to call in the main classes onResourcesLoaded callback. | 
|  | 1103 | std::vector<std::string> packages_to_callback; | 
|  | 1104 |  | 
|  | 1105 | JavaClassGeneratorOptions template_options; | 
|  | 1106 | template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll; | 
|  | 1107 | template_options.javadoc_annotations = options_.javadoc_annotations; | 
|  | 1108 |  | 
|  | 1109 | if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) { | 
|  | 1110 | template_options.use_final = false; | 
|  | 1111 | } | 
|  | 1112 |  | 
|  | 1113 | if (context_->GetPackageType() == PackageType::kSharedLib) { | 
|  | 1114 | template_options.use_final = false; | 
|  | 1115 | template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{}; | 
|  | 1116 | } | 
|  | 1117 |  | 
|  | 1118 | const StringPiece actual_package = context_->GetCompilationPackage(); | 
|  | 1119 | StringPiece output_package = context_->GetCompilationPackage(); | 
|  | 1120 | if (options_.custom_java_package) { | 
|  | 1121 | // Override the output java package to the custom one. | 
|  | 1122 | output_package = options_.custom_java_package.value(); | 
|  | 1123 | } | 
|  | 1124 |  | 
|  | 1125 | // Generate the private symbols if required. | 
|  | 1126 | if (options_.private_symbols) { | 
|  | 1127 | packages_to_callback.push_back(options_.private_symbols.value()); | 
|  | 1128 |  | 
|  | 1129 | // If we defined a private symbols package, we only emit Public symbols | 
|  | 1130 | // to the original package, and private and public symbols to the private package. | 
|  | 1131 | JavaClassGeneratorOptions options = template_options; | 
|  | 1132 | options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate; | 
|  | 1133 | if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(), | 
|  | 1134 | options)) { | 
|  | 1135 | return false; | 
|  | 1136 | } | 
|  | 1137 | } | 
|  | 1138 |  | 
|  | 1139 | // Generate copies of the original package R class but with different package names. | 
|  | 1140 | // This is to support non-namespaced builds. | 
|  | 1141 | for (const std::string& extra_package : options_.extra_java_packages) { | 
|  | 1142 | packages_to_callback.push_back(extra_package); | 
|  | 1143 |  | 
|  | 1144 | JavaClassGeneratorOptions options = template_options; | 
|  | 1145 | options.types = JavaClassGeneratorOptions::SymbolTypes::kAll; | 
|  | 1146 | if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) { | 
|  | 1147 | return false; | 
|  | 1148 | } | 
|  | 1149 | } | 
|  | 1150 |  | 
|  | 1151 | // Generate R classes for each package that was merged (static library). | 
|  | 1152 | // Use the actual package's resources only. | 
|  | 1153 | for (const std::string& package : table_merger_->merged_packages()) { | 
|  | 1154 | packages_to_callback.push_back(package); | 
|  | 1155 |  | 
|  | 1156 | JavaClassGeneratorOptions options = template_options; | 
|  | 1157 | options.types = JavaClassGeneratorOptions::SymbolTypes::kAll; | 
|  | 1158 | if (!WriteJavaFile(&final_table_, package, package, options)) { | 
|  | 1159 | return false; | 
|  | 1160 | } | 
|  | 1161 | } | 
|  | 1162 |  | 
|  | 1163 | // Generate the main public R class. | 
|  | 1164 | JavaClassGeneratorOptions options = template_options; | 
|  | 1165 |  | 
|  | 1166 | // Only generate public symbols if we have a private package. | 
|  | 1167 | if (options_.private_symbols) { | 
|  | 1168 | options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic; | 
|  | 1169 | } | 
|  | 1170 |  | 
|  | 1171 | if (options.rewrite_callback_options) { | 
|  | 1172 | options.rewrite_callback_options.value().packages_to_callback = | 
|  | 1173 | std::move(packages_to_callback); | 
|  | 1174 | } | 
|  | 1175 |  | 
|  | 1176 | if (!WriteJavaFile(&final_table_, actual_package, output_package, options, | 
|  | 1177 | options_.generate_text_symbols_path)) { | 
|  | 1178 | return false; | 
|  | 1179 | } | 
|  | 1180 |  | 
|  | 1181 | return true; | 
|  | 1182 | } | 
|  | 1183 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1184 | bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) { | 
|  | 1185 | if (!options_.generate_java_class_path) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1186 | return true; | 
|  | 1187 | } | 
|  | 1188 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1189 | std::unique_ptr<ClassDefinition> manifest_class = | 
|  | 1190 | GenerateManifestClass(context_->GetDiagnostics(), manifest_xml); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1191 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1192 | if (!manifest_class) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1193 | // Something bad happened, but we already logged it, so exit. | 
|  | 1194 | return false; | 
|  | 1195 | } | 
|  | 1196 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1197 | if (manifest_class->empty()) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1198 | // Empty Manifest class, no need to generate it. | 
|  | 1199 | return true; | 
|  | 1200 | } | 
|  | 1201 |  | 
|  | 1202 | // Add any JavaDoc annotations to the generated class. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1203 | for (const std::string& annotation : options_.javadoc_annotations) { | 
|  | 1204 | std::string proper_annotation = "@"; | 
|  | 1205 | proper_annotation += annotation; | 
|  | 1206 | manifest_class->GetCommentBuilder()->AppendComment(proper_annotation); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1207 | } | 
|  | 1208 |  | 
| Adam Lesinski | 0d81f70 | 2017-06-27 15:51:09 -0700 | [diff] [blame] | 1209 | const std::string package_utf8 = | 
|  | 1210 | options_.custom_java_package.value_or_default(context_->GetCompilationPackage()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1211 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1212 | std::string out_path = options_.generate_java_class_path.value(); | 
|  | 1213 | file::AppendPath(&out_path, file::PackageToPath(package_utf8)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1214 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1215 | if (!file::mkdirs(out_path)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1216 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to create directory '" << out_path | 
|  | 1217 | << "'"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1218 | return false; | 
|  | 1219 | } | 
|  | 1220 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1221 | file::AppendPath(&out_path, "Manifest.java"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1222 |  | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1223 | io::FileOutputStream fout(out_path); | 
|  | 1224 | if (fout.HadError()) { | 
|  | 1225 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path | 
|  | 1226 | << "': " << fout.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1227 | return false; | 
|  | 1228 | } | 
|  | 1229 |  | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1230 | ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout); | 
|  | 1231 | fout.Flush(); | 
|  | 1232 |  | 
|  | 1233 | if (fout.HadError()) { | 
|  | 1234 | context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path | 
|  | 1235 | << "': " << fout.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1236 | return false; | 
|  | 1237 | } | 
|  | 1238 | return true; | 
|  | 1239 | } | 
|  | 1240 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1241 | bool WriteProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keep_set) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1242 | if (!out) { | 
|  | 1243 | return true; | 
|  | 1244 | } | 
|  | 1245 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1246 | const std::string& out_path = out.value(); | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1247 | io::FileOutputStream fout(out_path); | 
|  | 1248 | if (fout.HadError()) { | 
|  | 1249 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path | 
|  | 1250 | << "': " << fout.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1251 | return false; | 
|  | 1252 | } | 
|  | 1253 |  | 
| Adam Lesinski | a693c4a | 2017-11-09 11:29:39 -0800 | [diff] [blame] | 1254 | proguard::WriteKeepSet(keep_set, &fout); | 
|  | 1255 | fout.Flush(); | 
|  | 1256 |  | 
|  | 1257 | if (fout.HadError()) { | 
|  | 1258 | context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path | 
|  | 1259 | << "': " << fout.GetError()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1260 | return false; | 
|  | 1261 | } | 
|  | 1262 | return true; | 
|  | 1263 | } | 
|  | 1264 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1265 | bool MergeStaticLibrary(const std::string& input, bool override) { | 
|  | 1266 | if (context_->IsVerbose()) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1267 | context_->GetDiagnostics()->Note(DiagMessage() << "merging static library " << input); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1268 | } | 
|  | 1269 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1270 | std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics()); | 
|  | 1271 | if (apk == nullptr) { | 
| Adam Lesinski | b5dc4bd | 2017-02-22 19:29:29 -0800 | [diff] [blame] | 1272 | context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1273 | return false; | 
|  | 1274 | } | 
|  | 1275 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1276 | ResourceTable* table = apk->GetResourceTable(); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1277 | ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1278 | if (!pkg) { | 
| Adam Lesinski | b5dc4bd | 2017-02-22 19:29:29 -0800 | [diff] [blame] | 1279 | context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1280 | return false; | 
|  | 1281 | } | 
|  | 1282 |  | 
|  | 1283 | bool result; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1284 | if (options_.no_static_lib_packages) { | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 1285 | // Merge all resources as if they were in the compilation package. This is the old behavior | 
|  | 1286 | // of aapt. | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1287 |  | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 1288 | // Add the package to the set of --extra-packages so we emit an R.java for each library | 
|  | 1289 | // package. | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1290 | if (!pkg->name.empty()) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1291 | options_.extra_java_packages.insert(pkg->name); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1292 | } | 
|  | 1293 |  | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 1294 | // Clear the package name, so as to make the resources look like they are coming from the | 
|  | 1295 | // local package. | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1296 | pkg->name = ""; | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1297 | result = table_merger_->Merge(Source(input), table, override); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1298 |  | 
|  | 1299 | } else { | 
|  | 1300 | // This is the proper way to merge libraries, where the package name is | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1301 | // preserved and resource names are mangled. | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1302 | result = table_merger_->MergeAndMangle(Source(input), pkg->name, table); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1303 | } | 
|  | 1304 |  | 
|  | 1305 | if (!result) { | 
|  | 1306 | return false; | 
|  | 1307 | } | 
|  | 1308 |  | 
|  | 1309 | // Make sure to move the collection into the set of IFileCollections. | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1310 | merged_apks_.push_back(std::move(apk)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1311 | return true; | 
|  | 1312 | } | 
|  | 1313 |  | 
| Adam Lesinski | 2427dce | 2017-11-30 15:10:28 -0800 | [diff] [blame] | 1314 | bool MergeExportedSymbols(const Source& source, | 
|  | 1315 | const std::vector<SourcedResourceName>& exported_symbols) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1316 | // Add the exports of this file to the table. | 
| Adam Lesinski | 2427dce | 2017-11-30 15:10:28 -0800 | [diff] [blame] | 1317 | for (const SourcedResourceName& exported_symbol : exported_symbols) { | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1318 | ResourceName res_name = exported_symbol.name; | 
|  | 1319 | if (res_name.package.empty()) { | 
|  | 1320 | res_name.package = context_->GetCompilationPackage(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1321 | } | 
|  | 1322 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1323 | Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1324 | if (mangled_name) { | 
|  | 1325 | res_name = mangled_name.value(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1326 | } | 
|  | 1327 |  | 
|  | 1328 | std::unique_ptr<Id> id = util::make_unique<Id>(); | 
| Adam Lesinski | 2427dce | 2017-11-30 15:10:28 -0800 | [diff] [blame] | 1329 | id->SetSource(source.WithLine(exported_symbol.line)); | 
| Adam Lesinski | 71be705 | 2017-12-12 16:48:07 -0800 | [diff] [blame] | 1330 | bool result = | 
|  | 1331 | final_table_.AddResourceMangled(res_name, ConfigDescription::DefaultConfig(), | 
|  | 1332 | std::string(), std::move(id), context_->GetDiagnostics()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1333 | if (!result) { | 
|  | 1334 | return false; | 
|  | 1335 | } | 
|  | 1336 | } | 
|  | 1337 | return true; | 
|  | 1338 | } | 
|  | 1339 |  | 
| Adam Lesinski | 2427dce | 2017-11-30 15:10:28 -0800 | [diff] [blame] | 1340 | bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) { | 
|  | 1341 | if (context_->IsVerbose()) { | 
|  | 1342 | context_->GetDiagnostics()->Note(DiagMessage() | 
|  | 1343 | << "merging '" << compiled_file.name | 
|  | 1344 | << "' from compiled file " << compiled_file.source); | 
|  | 1345 | } | 
|  | 1346 |  | 
|  | 1347 | if (!table_merger_->MergeFile(compiled_file, override, file)) { | 
|  | 1348 | return false; | 
|  | 1349 | } | 
|  | 1350 | return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols); | 
|  | 1351 | } | 
|  | 1352 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1353 | // Takes a path to load as a ZIP file and merges the files within into the master ResourceTable. | 
|  | 1354 | // If override is true, conflicting resources are allowed to override each other, in order of last | 
|  | 1355 | // seen. | 
|  | 1356 | // An io::IFileCollection is created from the ZIP file and added to the set of | 
|  | 1357 | // io::IFileCollections that are open. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1358 | bool MergeArchive(const std::string& input, bool override) { | 
|  | 1359 | if (context_->IsVerbose()) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1360 | context_->GetDiagnostics()->Note(DiagMessage() << "merging archive " << input); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1361 | } | 
|  | 1362 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1363 | std::string error_str; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1364 | std::unique_ptr<io::ZipFileCollection> collection = | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1365 | io::ZipFileCollection::Create(input, &error_str); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1366 | if (!collection) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1367 | context_->GetDiagnostics()->Error(DiagMessage(input) << error_str); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1368 | return false; | 
|  | 1369 | } | 
|  | 1370 |  | 
|  | 1371 | bool error = false; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1372 | for (auto iter = collection->Iterator(); iter->HasNext();) { | 
|  | 1373 | if (!MergeFile(iter->Next(), override)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1374 | error = true; | 
|  | 1375 | } | 
|  | 1376 | } | 
|  | 1377 |  | 
|  | 1378 | // Make sure to move the collection into the set of IFileCollections. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1379 | collections_.push_back(std::move(collection)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1380 | return !error; | 
|  | 1381 | } | 
|  | 1382 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1383 | // Takes a path to load and merge into the master ResourceTable. If override is true, | 
|  | 1384 | // conflicting resources are allowed to override each other, in order of last seen. | 
|  | 1385 | // If the file path ends with .flata, .jar, .jack, or .zip the file is treated | 
|  | 1386 | // as ZIP archive and the files within are merged individually. | 
|  | 1387 | // Otherwise the file is processed on its own. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1388 | bool MergePath(const std::string& path, bool override) { | 
|  | 1389 | if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") || | 
|  | 1390 | util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) { | 
|  | 1391 | return MergeArchive(path, override); | 
|  | 1392 | } else if (util::EndsWith(path, ".apk")) { | 
|  | 1393 | return MergeStaticLibrary(path, override); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1394 | } | 
|  | 1395 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1396 | io::IFile* file = file_collection_->InsertFile(path); | 
|  | 1397 | return MergeFile(file, override); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1398 | } | 
|  | 1399 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1400 | // Takes an AAPT Container file (.apc/.flat) to load and merge into the master ResourceTable. | 
|  | 1401 | // If override is true, conflicting resources are allowed to override each other, in order of last | 
|  | 1402 | // seen. | 
|  | 1403 | // All other file types are ignored. This is because these files could be coming from a zip, | 
|  | 1404 | // where we could have other files like classes.dex. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1405 | bool MergeFile(io::IFile* file, bool override) { | 
|  | 1406 | const Source& src = file->GetSource(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1407 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1408 | if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) { | 
| Adam Lesinski | 6a396c1 | 2016-10-20 14:38:23 -0700 | [diff] [blame] | 1409 | // Since AAPT compiles these file types and appends .flat to them, seeing | 
|  | 1410 | // their raw extensions is a sign that they weren't compiled. | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1411 | const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG"; | 
|  | 1412 | context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type | 
|  | 1413 | << " file passed as argument. Must be " | 
|  | 1414 | "compiled first into .flat file."); | 
| Adam Lesinski | 6a396c1 | 2016-10-20 14:38:23 -0700 | [diff] [blame] | 1415 | return false; | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1416 | } else if (!util::EndsWith(src.path, ".apc") && !util::EndsWith(src.path, ".flat")) { | 
|  | 1417 | if (context_->IsVerbose()) { | 
|  | 1418 | context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring unrecognized file"); | 
|  | 1419 | return true; | 
|  | 1420 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1421 | } | 
|  | 1422 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1423 | std::unique_ptr<io::InputStream> input_stream = file->OpenInputStream(); | 
|  | 1424 | if (input_stream == nullptr) { | 
|  | 1425 | context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open file"); | 
|  | 1426 | return false; | 
|  | 1427 | } | 
|  | 1428 |  | 
|  | 1429 | if (input_stream->HadError()) { | 
|  | 1430 | context_->GetDiagnostics()->Error(DiagMessage(src) | 
|  | 1431 | << "failed to open file: " << input_stream->GetError()); | 
|  | 1432 | return false; | 
|  | 1433 | } | 
|  | 1434 |  | 
|  | 1435 | ContainerReaderEntry* entry; | 
|  | 1436 | ContainerReader reader(input_stream.get()); | 
| Mohamed Heikal | 1084432 | 2018-02-07 15:17:38 -0500 | [diff] [blame] | 1437 |  | 
|  | 1438 | if (reader.HadError()) { | 
|  | 1439 | context_->GetDiagnostics()->Error(DiagMessage(src) | 
|  | 1440 | << "failed to read file: " << reader.GetError()); | 
|  | 1441 | return false; | 
|  | 1442 | } | 
|  | 1443 |  | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1444 | while ((entry = reader.Next()) != nullptr) { | 
|  | 1445 | if (entry->Type() == ContainerEntryType::kResTable) { | 
|  | 1446 | pb::ResourceTable pb_table; | 
|  | 1447 | if (!entry->GetResTable(&pb_table)) { | 
|  | 1448 | context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read resource table: " | 
|  | 1449 | << entry->GetError()); | 
|  | 1450 | return false; | 
|  | 1451 | } | 
|  | 1452 |  | 
|  | 1453 | ResourceTable table; | 
|  | 1454 | std::string error; | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1455 | if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) { | 
| Adam Lesinski | 0045116 | 2017-10-03 07:44:08 -0700 | [diff] [blame] | 1456 | context_->GetDiagnostics()->Error(DiagMessage(src) | 
|  | 1457 | << "failed to deserialize resource table: " << error); | 
|  | 1458 | return false; | 
|  | 1459 | } | 
|  | 1460 |  | 
|  | 1461 | if (!table_merger_->Merge(src, &table, override)) { | 
|  | 1462 | context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to merge resource table"); | 
|  | 1463 | return false; | 
|  | 1464 | } | 
|  | 1465 | } else if (entry->Type() == ContainerEntryType::kResFile) { | 
|  | 1466 | pb::internal::CompiledFile pb_compiled_file; | 
|  | 1467 | off64_t offset; | 
|  | 1468 | size_t len; | 
|  | 1469 | if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &len)) { | 
|  | 1470 | context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to get resource file: " | 
|  | 1471 | << entry->GetError()); | 
|  | 1472 | return false; | 
|  | 1473 | } | 
|  | 1474 |  | 
|  | 1475 | ResourceFile resource_file; | 
|  | 1476 | std::string error; | 
|  | 1477 | if (!DeserializeCompiledFileFromPb(pb_compiled_file, &resource_file, &error)) { | 
|  | 1478 | context_->GetDiagnostics()->Error(DiagMessage(src) | 
|  | 1479 | << "failed to read compiled header: " << error); | 
|  | 1480 | return false; | 
|  | 1481 | } | 
|  | 1482 |  | 
|  | 1483 | if (!MergeCompiledFile(resource_file, file->CreateFileSegment(offset, len), override)) { | 
|  | 1484 | return false; | 
|  | 1485 | } | 
|  | 1486 | } | 
|  | 1487 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1488 | return true; | 
|  | 1489 | } | 
|  | 1490 |  | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1491 | bool CopyAssetsDirsToApk(IArchiveWriter* writer) { | 
|  | 1492 | std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets; | 
|  | 1493 | for (const std::string& assets_dir : options_.assets_dirs) { | 
|  | 1494 | Maybe<std::vector<std::string>> files = | 
|  | 1495 | file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr); | 
|  | 1496 | if (!files) { | 
|  | 1497 | return false; | 
|  | 1498 | } | 
|  | 1499 |  | 
|  | 1500 | for (const std::string& file : files.value()) { | 
|  | 1501 | std::string full_key = "assets/" + file; | 
|  | 1502 | std::string full_path = assets_dir; | 
|  | 1503 | file::AppendPath(&full_path, file); | 
|  | 1504 |  | 
|  | 1505 | auto iter = merged_assets.find(full_key); | 
|  | 1506 | if (iter == merged_assets.end()) { | 
|  | 1507 | merged_assets.emplace(std::move(full_key), | 
|  | 1508 | util::make_unique<io::RegularFile>(Source(std::move(full_path)))); | 
|  | 1509 | } else if (context_->IsVerbose()) { | 
|  | 1510 | context_->GetDiagnostics()->Warn(DiagMessage(iter->second->GetSource()) | 
|  | 1511 | << "asset file overrides '" << full_path << "'"); | 
|  | 1512 | } | 
|  | 1513 | } | 
|  | 1514 | } | 
|  | 1515 |  | 
|  | 1516 | for (auto& entry : merged_assets) { | 
|  | 1517 | uint32_t compression_flags = ArchiveEntry::kCompress; | 
|  | 1518 | std::string extension = file::GetExtension(entry.first).to_string(); | 
|  | 1519 | if (options_.extensions_to_not_compress.count(extension) > 0) { | 
|  | 1520 | compression_flags = 0u; | 
|  | 1521 | } | 
|  | 1522 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1523 | if (!io::CopyFileToArchive(context_, entry.second.get(), entry.first, compression_flags, | 
|  | 1524 | writer)) { | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1525 | return false; | 
|  | 1526 | } | 
|  | 1527 | } | 
|  | 1528 | return true; | 
|  | 1529 | } | 
|  | 1530 |  | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 1531 | // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable | 
|  | 1532 | // to the IArchiveWriter. | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1533 | bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest, | 
|  | 1534 | ResourceTable* table) { | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 1535 | const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib; | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 1536 | bool result = FlattenXml(context_, *manifest, "AndroidManifest.xml", keep_raw_values, | 
|  | 1537 | true /*utf16*/, options_.output_format, writer); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1538 | if (!result) { | 
|  | 1539 | return false; | 
|  | 1540 | } | 
|  | 1541 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1542 | ResourceFileFlattenerOptions file_flattener_options; | 
|  | 1543 | file_flattener_options.keep_raw_values = keep_raw_values; | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1544 | file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything; | 
|  | 1545 | file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1546 | file_flattener_options.no_auto_version = options_.no_auto_version; | 
|  | 1547 | file_flattener_options.no_version_vectors = options_.no_version_vectors; | 
| Yuichi Araki | 4d35cca | 2017-01-18 20:42:17 +0900 | [diff] [blame] | 1548 | file_flattener_options.no_version_transitions = options_.no_version_transitions; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1549 | file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces; | 
|  | 1550 | file_flattener_options.update_proguard_spec = | 
|  | 1551 | static_cast<bool>(options_.generate_proguard_rules_path); | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 1552 | file_flattener_options.output_format = options_.output_format; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1553 |  | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1554 | ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1555 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1556 | if (!file_flattener.Flatten(table, writer)) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1557 | context_->GetDiagnostics()->Error(DiagMessage() << "failed linking file resources"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1558 | return false; | 
|  | 1559 | } | 
|  | 1560 |  | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1561 | // Hack to fix b/68820737. | 
|  | 1562 | // We need to modify the ResourceTable's package name, but that should NOT affect | 
|  | 1563 | // anything else being generated, which includes the Java classes. | 
|  | 1564 | // If required, the package name is modifed before flattening, and then modified back | 
|  | 1565 | // to its original name. | 
|  | 1566 | ResourceTablePackage* package_to_rewrite = nullptr; | 
| Todd Kennedy | 3251299 | 2018-04-25 16:45:59 -0700 | [diff] [blame] | 1567 | // Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80 | 
|  | 1568 | // or higher] as invalid. In order to work around this limitation, we allow the use | 
|  | 1569 | // of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the | 
|  | 1570 | // definition of what a valid "split" package ID is to account for this. | 
|  | 1571 | const bool isSplitPackage = (options_.allow_reserved_package_id && | 
|  | 1572 | context_->GetPackageId() != kAppPackageId && | 
|  | 1573 | context_->GetPackageId() != kFrameworkPackageId) | 
|  | 1574 | || (!options_.allow_reserved_package_id && context_->GetPackageId() > kAppPackageId); | 
|  | 1575 | if (isSplitPackage && | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1576 | included_feature_base_ == make_value(context_->GetCompilationPackage())) { | 
|  | 1577 | // The base APK is included, and this is a feature split. If the base package is | 
|  | 1578 | // the same as this package, then we are building an old style Android Instant Apps feature | 
|  | 1579 | // split and must apply this workaround to avoid requiring namespaces support. | 
|  | 1580 | package_to_rewrite = table->FindPackage(context_->GetCompilationPackage()); | 
|  | 1581 | if (package_to_rewrite != nullptr) { | 
|  | 1582 | CHECK_EQ(1u, table->packages.size()) << "can't change name of package when > 1 package"; | 
|  | 1583 |  | 
|  | 1584 | std::string new_package_name = | 
|  | 1585 | StringPrintf("%s.%s", package_to_rewrite->name.c_str(), | 
|  | 1586 | app_info_.split_name.value_or_default("feature").c_str()); | 
|  | 1587 |  | 
|  | 1588 | if (context_->IsVerbose()) { | 
|  | 1589 | context_->GetDiagnostics()->Note( | 
|  | 1590 | DiagMessage() << "rewriting resource package name for feature split to '" | 
|  | 1591 | << new_package_name << "'"); | 
|  | 1592 | } | 
|  | 1593 | package_to_rewrite->name = new_package_name; | 
|  | 1594 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1595 | } | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1596 |  | 
|  | 1597 | bool success = FlattenTable(table, options_.output_format, writer); | 
|  | 1598 |  | 
|  | 1599 | if (package_to_rewrite != nullptr) { | 
|  | 1600 | // Change the name back. | 
|  | 1601 | package_to_rewrite->name = context_->GetCompilationPackage(); | 
|  | 1602 | if (package_to_rewrite->id) { | 
|  | 1603 | table->included_packages_.erase(package_to_rewrite->id.value()); | 
|  | 1604 | } | 
|  | 1605 | } | 
|  | 1606 |  | 
|  | 1607 | if (!success) { | 
|  | 1608 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table"); | 
|  | 1609 | } | 
|  | 1610 | return success; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1611 | } | 
|  | 1612 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1613 | int Run(const std::vector<std::string>& input_files) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1614 | // Load the AndroidManifest.xml | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1615 | std::unique_ptr<xml::XmlResource> manifest_xml = | 
|  | 1616 | LoadXml(options_.manifest_path, context_->GetDiagnostics()); | 
|  | 1617 | if (!manifest_xml) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1618 | return 1; | 
|  | 1619 | } | 
|  | 1620 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1621 | // First extract the Package name without modifying it (via --rename-manifest-package). | 
|  | 1622 | if (Maybe<AppInfo> maybe_app_info = | 
|  | 1623 | ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1624 | const AppInfo& app_info = maybe_app_info.value(); | 
|  | 1625 | context_->SetCompilationPackage(app_info.package); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1626 | } | 
|  | 1627 |  | 
| Adam Lesinski | c628437 | 2017-12-04 13:46:23 -0800 | [diff] [blame] | 1628 | // Now that the compilation package is set, load the dependencies. This will also extract | 
|  | 1629 | // the Android framework's versionCode and versionName, if they exist. | 
|  | 1630 | if (!LoadSymbolsFromIncludePaths()) { | 
|  | 1631 | return 1; | 
|  | 1632 | } | 
|  | 1633 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1634 | ManifestFixer manifest_fixer(options_.manifest_fixer_options); | 
|  | 1635 | if (!manifest_fixer.Consume(context_, manifest_xml.get())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1636 | return 1; | 
|  | 1637 | } | 
|  | 1638 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1639 | Maybe<AppInfo> maybe_app_info = | 
|  | 1640 | ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics()); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1641 | if (!maybe_app_info) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1642 | return 1; | 
|  | 1643 | } | 
|  | 1644 |  | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1645 | app_info_ = maybe_app_info.value(); | 
|  | 1646 | context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1647 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1648 | context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1649 |  | 
|  | 1650 | // Override the package ID when it is "android". | 
|  | 1651 | if (context_->GetCompilationPackage() == "android") { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1652 | context_->SetPackageId(0x01); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1653 |  | 
|  | 1654 | // Verify we're building a regular app. | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 1655 | if (context_->GetPackageType() != PackageType::kApp) { | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1656 | context_->GetDiagnostics()->Error( | 
|  | 1657 | DiagMessage() << "package 'android' can only be built as a regular app"); | 
|  | 1658 | return 1; | 
|  | 1659 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1660 | } | 
|  | 1661 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1662 | TableMergerOptions table_merger_options; | 
|  | 1663 | table_merger_options.auto_add_overlay = options_.auto_add_overlay; | 
| Izabela Orlowska | d51efe8 | 2018-04-24 18:18:29 +0100 | [diff] [blame] | 1664 | table_merger_options.strict_visibility = options_.strict_visibility; | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1665 | table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1666 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1667 | if (context_->IsVerbose()) { | 
|  | 1668 | context_->GetDiagnostics()->Note(DiagMessage() | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 1669 | << StringPrintf("linking package '%s' using package ID %02x", | 
|  | 1670 | context_->GetCompilationPackage().data(), | 
|  | 1671 | context_->GetPackageId())); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1672 | } | 
|  | 1673 |  | 
| Adam Lesinski | 2427dce | 2017-11-30 15:10:28 -0800 | [diff] [blame] | 1674 | // Extract symbols from AndroidManifest.xml, since this isn't merged like the other XML files | 
|  | 1675 | // in res/**/*. | 
|  | 1676 | { | 
|  | 1677 | XmlIdCollector collector; | 
|  | 1678 | if (!collector.Consume(context_, manifest_xml.get())) { | 
|  | 1679 | return false; | 
|  | 1680 | } | 
|  | 1681 |  | 
|  | 1682 | if (!MergeExportedSymbols(manifest_xml->file.source, manifest_xml->file.exported_symbols)) { | 
|  | 1683 | return false; | 
|  | 1684 | } | 
|  | 1685 | } | 
|  | 1686 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1687 | for (const std::string& input : input_files) { | 
|  | 1688 | if (!MergePath(input, false)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1689 | context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing input"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1690 | return 1; | 
|  | 1691 | } | 
|  | 1692 | } | 
|  | 1693 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1694 | for (const std::string& input : options_.overlay_files) { | 
|  | 1695 | if (!MergePath(input, true)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1696 | context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing overlays"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1697 | return 1; | 
|  | 1698 | } | 
|  | 1699 | } | 
|  | 1700 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1701 | if (!VerifyNoExternalPackages()) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1702 | return 1; | 
|  | 1703 | } | 
|  | 1704 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 1705 | if (context_->GetPackageType() != PackageType::kStaticLib) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1706 | PrivateAttributeMover mover; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1707 | if (!mover.Consume(context_, &final_table_)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1708 | context_->GetDiagnostics()->Error(DiagMessage() << "failed moving private attributes"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1709 | return 1; | 
|  | 1710 | } | 
|  | 1711 |  | 
|  | 1712 | // Assign IDs if we are building a regular app. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1713 | IdAssigner id_assigner(&options_.stable_id_map); | 
|  | 1714 | if (!id_assigner.Consume(context_, &final_table_)) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1715 | context_->GetDiagnostics()->Error(DiagMessage() << "failed assigning IDs"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1716 | return 1; | 
|  | 1717 | } | 
|  | 1718 |  | 
|  | 1719 | // Now grab each ID and emit it as a file. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1720 | if (options_.resource_id_map_path) { | 
|  | 1721 | for (auto& package : final_table_.packages) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1722 | for (auto& type : package->types) { | 
|  | 1723 | for (auto& entry : type->entries) { | 
|  | 1724 | ResourceName name(package->name, type->type, entry->name); | 
|  | 1725 | // The IDs are guaranteed to exist. | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1726 | options_.stable_id_map[std::move(name)] = | 
|  | 1727 | ResourceId(package->id.value(), type->id.value(), entry->id.value()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1728 | } | 
|  | 1729 | } | 
|  | 1730 | } | 
|  | 1731 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1732 | if (!WriteStableIdMapToPath(context_->GetDiagnostics(), options_.stable_id_map, | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1733 | options_.resource_id_map_path.value())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1734 | return 1; | 
|  | 1735 | } | 
|  | 1736 | } | 
|  | 1737 | } else { | 
|  | 1738 | // Static libs are merged with other apps, and ID collisions are bad, so | 
|  | 1739 | // verify that | 
|  | 1740 | // no IDs have been set. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1741 | if (!VerifyNoIdsSet()) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1742 | return 1; | 
|  | 1743 | } | 
|  | 1744 | } | 
|  | 1745 |  | 
|  | 1746 | // Add the names to mangle based on our source merge earlier. | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1747 | context_->SetNameManglerPolicy( | 
|  | 1748 | NameManglerPolicy{context_->GetCompilationPackage(), table_merger_->merged_packages()}); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1749 |  | 
|  | 1750 | // Add our table to the symbol table. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1751 | context_->GetExternalSymbols()->PrependSource( | 
|  | 1752 | util::make_unique<ResourceTableSymbolSource>(&final_table_)); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1753 |  | 
| Adam Lesinski | 1e4b0e5 | 2017-04-27 15:01:10 -0700 | [diff] [blame] | 1754 | // Workaround for pre-O runtime that would treat negative resource IDs | 
|  | 1755 | // (any ID with a package ID > 7f) as invalid. Intercept any ID (PPTTEEEE) with PP > 0x7f | 
|  | 1756 | // and type == 'id', and return the ID 0x7fPPEEEE. IDs don't need to be real resources, they | 
|  | 1757 | // are just identifiers. | 
|  | 1758 | if (context_->GetMinSdkVersion() < SDK_O && context_->GetPackageType() == PackageType::kApp) { | 
|  | 1759 | if (context_->IsVerbose()) { | 
|  | 1760 | context_->GetDiagnostics()->Note(DiagMessage() | 
|  | 1761 | << "enabling pre-O feature split ID rewriting"); | 
|  | 1762 | } | 
|  | 1763 | context_->GetExternalSymbols()->SetDelegate( | 
|  | 1764 | util::make_unique<FeatureSplitSymbolTableDelegate>(context_)); | 
|  | 1765 | } | 
|  | 1766 |  | 
| Adam Lesinski | 34a1687 | 2018-02-23 16:18:10 -0800 | [diff] [blame] | 1767 | // Before we process anything, remove the resources whose default values don't exist. | 
|  | 1768 | // We want to force any references to these to fail the build. | 
|  | 1769 | if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) { | 
|  | 1770 | context_->GetDiagnostics()->Error(DiagMessage() | 
|  | 1771 | << "failed removing resources with no defaults"); | 
|  | 1772 | return 1; | 
|  | 1773 | } | 
|  | 1774 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1775 | ReferenceLinker linker; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1776 | if (!linker.Consume(context_, &final_table_)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1777 | context_->GetDiagnostics()->Error(DiagMessage() << "failed linking references"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1778 | return 1; | 
|  | 1779 | } | 
|  | 1780 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 1781 | if (context_->GetPackageType() == PackageType::kStaticLib) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1782 | if (!options_.products.empty()) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1783 | context_->GetDiagnostics()->Warn(DiagMessage() | 
|  | 1784 | << "can't select products when building static library"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1785 | } | 
|  | 1786 | } else { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1787 | ProductFilter product_filter(options_.products); | 
|  | 1788 | if (!product_filter.Consume(context_, &final_table_)) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1789 | context_->GetDiagnostics()->Error(DiagMessage() << "failed stripping products"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1790 | return 1; | 
|  | 1791 | } | 
|  | 1792 | } | 
|  | 1793 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1794 | if (!options_.no_auto_version) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1795 | AutoVersioner versioner; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1796 | if (!versioner.Consume(context_, &final_table_)) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1797 | context_->GetDiagnostics()->Error(DiagMessage() << "failed versioning styles"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1798 | return 1; | 
|  | 1799 | } | 
|  | 1800 | } | 
|  | 1801 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 1802 | if (context_->GetPackageType() != PackageType::kStaticLib && context_->GetMinSdkVersion() > 0) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1803 | if (context_->IsVerbose()) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1804 | context_->GetDiagnostics()->Note(DiagMessage() | 
|  | 1805 | << "collapsing resource versions for minimum SDK " | 
|  | 1806 | << context_->GetMinSdkVersion()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1807 | } | 
|  | 1808 |  | 
|  | 1809 | VersionCollapser collapser; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1810 | if (!collapser.Consume(context_, &final_table_)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1811 | return 1; | 
|  | 1812 | } | 
|  | 1813 | } | 
|  | 1814 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1815 | if (!options_.no_resource_deduping) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1816 | ResourceDeduper deduper; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1817 | if (!deduper.Consume(context_, &final_table_)) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1818 | context_->GetDiagnostics()->Error(DiagMessage() << "failed deduping resources"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1819 | return 1; | 
|  | 1820 | } | 
|  | 1821 | } | 
|  | 1822 |  | 
| Adam Koski | dc21dea | 2017-07-21 10:55:27 -0700 | [diff] [blame] | 1823 | proguard::KeepSet proguard_keep_set = | 
|  | 1824 | proguard::KeepSet(options_.generate_conditional_proguard_rules); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1825 | proguard::KeepSet proguard_main_dex_keep_set; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1826 |  | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 1827 | if (context_->GetPackageType() == PackageType::kStaticLib) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1828 | if (options_.table_splitter_options.config_filter != nullptr || | 
| Pierre Lecesne | 672384b | 2017-02-06 10:29:02 +0000 | [diff] [blame] | 1829 | !options_.table_splitter_options.preferred_densities.empty()) { | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1830 | context_->GetDiagnostics()->Warn(DiagMessage() | 
|  | 1831 | << "can't strip resources when building static library"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1832 | } | 
|  | 1833 | } else { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1834 | // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or | 
|  | 1835 | // equal to the minSdk. | 
|  | 1836 | options_.split_constraints = | 
|  | 1837 | AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1838 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1839 | TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1840 | if (!table_splitter.VerifySplitConstraints(context_)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1841 | return 1; | 
|  | 1842 | } | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1843 | table_splitter.SplitTable(&final_table_); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1844 |  | 
|  | 1845 | // Now we need to write out the Split APKs. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1846 | auto path_iter = options_.split_paths.begin(); | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1847 | auto split_constraints_iter = options_.split_constraints.begin(); | 
|  | 1848 | for (std::unique_ptr<ResourceTable>& split_table : table_splitter.splits()) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1849 | if (context_->IsVerbose()) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1850 | context_->GetDiagnostics()->Note(DiagMessage(*path_iter) | 
|  | 1851 | << "generating split with configurations '" | 
|  | 1852 | << util::Joiner(split_constraints_iter->configs, ", ") | 
|  | 1853 | << "'"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1854 | } | 
|  | 1855 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1856 | std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(*path_iter); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1857 | if (!archive_writer) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1858 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1859 | return 1; | 
|  | 1860 | } | 
|  | 1861 |  | 
|  | 1862 | // Generate an AndroidManifest.xml for each split. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1863 | std::unique_ptr<xml::XmlResource> split_manifest = | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1864 | GenerateSplitManifest(app_info_, *split_constraints_iter); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1865 |  | 
|  | 1866 | XmlReferenceLinker linker; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1867 | if (!linker.Consume(context_, split_manifest.get())) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1868 | context_->GetDiagnostics()->Error(DiagMessage() | 
|  | 1869 | << "failed to create Split AndroidManifest.xml"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1870 | return 1; | 
|  | 1871 | } | 
|  | 1872 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1873 | if (!WriteApk(archive_writer.get(), &proguard_keep_set, split_manifest.get(), | 
|  | 1874 | split_table.get())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1875 | return 1; | 
|  | 1876 | } | 
|  | 1877 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1878 | ++path_iter; | 
|  | 1879 | ++split_constraints_iter; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1880 | } | 
|  | 1881 | } | 
|  | 1882 |  | 
|  | 1883 | // Start writing the base APK. | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1884 | std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(options_.output_path); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1885 | if (!archive_writer) { | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1886 | context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1887 | return 1; | 
|  | 1888 | } | 
|  | 1889 |  | 
|  | 1890 | bool error = false; | 
|  | 1891 | { | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 1892 | // AndroidManifest.xml has no resource name, but the CallSite is built from the name | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1893 | // (aka, which package the AndroidManifest.xml is coming from). | 
|  | 1894 | // So we give it a package name so it can see local resources. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1895 | manifest_xml->file.name.package = context_->GetCompilationPackage(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1896 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1897 | XmlReferenceLinker manifest_linker; | 
|  | 1898 | if (manifest_linker.Consume(context_, manifest_xml.get())) { | 
|  | 1899 | if (options_.generate_proguard_rules_path && | 
| Adam Koski | dc21dea | 2017-07-21 10:55:27 -0700 | [diff] [blame] | 1900 | !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1901 | error = true; | 
|  | 1902 | } | 
|  | 1903 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1904 | if (options_.generate_main_dex_proguard_rules_path && | 
| Adam Koski | dc21dea | 2017-07-21 10:55:27 -0700 | [diff] [blame] | 1905 | !proguard::CollectProguardRulesForManifest(manifest_xml.get(), | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1906 | &proguard_main_dex_keep_set, true)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1907 | error = true; | 
| Alexandria Cornwall | 637b482 | 2016-08-11 09:53:16 -0700 | [diff] [blame] | 1908 | } | 
|  | 1909 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1910 | if (options_.generate_java_class_path) { | 
|  | 1911 | if (!WriteManifestJavaFile(manifest_xml.get())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1912 | error = true; | 
|  | 1913 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 1914 | } | 
|  | 1915 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1916 | if (options_.no_xml_namespaces) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1917 | // PackageParser will fail if URIs are removed from | 
|  | 1918 | // AndroidManifest.xml. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1919 | XmlNamespaceRemover namespace_remover(true /* keepUris */); | 
|  | 1920 | if (!namespace_remover.Consume(context_, manifest_xml.get())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1921 | error = true; | 
|  | 1922 | } | 
| Rohit Agrawal | e49bb30 | 2016-04-22 12:27:55 -0700 | [diff] [blame] | 1923 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1924 | } else { | 
|  | 1925 | error = true; | 
|  | 1926 | } | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 1927 | } | 
| Adam Lesinski | fb48d29 | 2015-11-07 15:52:13 -0800 | [diff] [blame] | 1928 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1929 | if (error) { | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1930 | context_->GetDiagnostics()->Error(DiagMessage() << "failed processing manifest"); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1931 | return 1; | 
|  | 1932 | } | 
| Adam Lesinski | a6fe345 | 2015-12-09 15:20:52 -0800 | [diff] [blame] | 1933 |  | 
| Adam Lesinski | b39ad7c | 2017-03-13 11:40:48 -0700 | [diff] [blame] | 1934 | if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) { | 
|  | 1935 | return 1; | 
|  | 1936 | } | 
|  | 1937 |  | 
|  | 1938 | if (!CopyAssetsDirsToApk(archive_writer.get())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1939 | return 1; | 
|  | 1940 | } | 
| Adam Lesinski | fb48d29 | 2015-11-07 15:52:13 -0800 | [diff] [blame] | 1941 |  | 
| Izabela Orlowska | 23a6e1e | 2017-12-05 14:52:07 +0000 | [diff] [blame] | 1942 | if (options_.generate_java_class_path || options_.generate_text_symbols_path) { | 
| Adam Lesinski | 1ef0fa9 | 2017-08-15 21:32:49 -0700 | [diff] [blame] | 1943 | if (!GenerateJavaClasses()) { | 
| Adam Lesinski | b5dc4bd | 2017-02-22 19:29:29 -0800 | [diff] [blame] | 1944 | return 1; | 
|  | 1945 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1946 | } | 
|  | 1947 |  | 
| Adam Lesinski | b5dc4bd | 2017-02-22 19:29:29 -0800 | [diff] [blame] | 1948 | if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1949 | return 1; | 
|  | 1950 | } | 
|  | 1951 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1952 | if (!WriteProguardFile(options_.generate_main_dex_proguard_rules_path, | 
|  | 1953 | proguard_main_dex_keep_set)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1954 | return 1; | 
|  | 1955 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1956 | return 0; | 
|  | 1957 | } | 
|  | 1958 |  | 
|  | 1959 | private: | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1960 | LinkOptions options_; | 
|  | 1961 | LinkContext* context_; | 
|  | 1962 | ResourceTable final_table_; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1963 |  | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1964 | AppInfo app_info_; | 
|  | 1965 |  | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1966 | std::unique_ptr<TableMerger> table_merger_; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1967 |  | 
|  | 1968 | // A pointer to the FileCollection representing the filesystem (not archives). | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1969 | std::unique_ptr<io::FileCollection> file_collection_; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1970 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1971 | // A vector of IFileCollections. This is mainly here to retain ownership of the | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1972 | // collections. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1973 | std::vector<std::unique_ptr<io::IFileCollection>> collections_; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1974 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1975 | // The set of merged APKs. This is mainly here to retain ownership of the APKs. | 
|  | 1976 | std::vector<std::unique_ptr<LoadedApk>> merged_apks_; | 
|  | 1977 |  | 
|  | 1978 | // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs. | 
|  | 1979 | std::vector<std::unique_ptr<LoadedApk>> static_library_includes_; | 
| Adam Lesinski | ceb9b2f | 2017-02-16 12:05:42 -0800 | [diff] [blame] | 1980 |  | 
|  | 1981 | // The set of shared libraries being used, mapping their assigned package ID to package name. | 
|  | 1982 | std::map<size_t, std::string> shared_libs_; | 
| Adam Lesinski | 490595a | 2017-11-07 17:08:07 -0800 | [diff] [blame] | 1983 |  | 
|  | 1984 | // The package name of the base application, if it is included. | 
|  | 1985 | Maybe<std::string> included_feature_base_; | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 1986 | }; | 
|  | 1987 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 1988 | int LinkCommand::Action(const std::vector<std::string>& args) { | 
|  | 1989 | LinkContext context(diag_); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1990 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 1991 | // Expand all argument-files passed into the command line. These start with '@'. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1992 | std::vector<std::string> arg_list; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 1993 | for (const std::string& arg : args) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1994 | if (util::StartsWith(arg, "@")) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 1995 | const std::string path = arg.substr(1, arg.size() - 1); | 
|  | 1996 | std::string error; | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 1997 | if (!file::AppendArgsFromFile(path, &arg_list, &error)) { | 
|  | 1998 | context.GetDiagnostics()->Error(DiagMessage(path) << error); | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 1999 | return 1; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2000 | } | 
|  | 2001 | } else { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 2002 | arg_list.push_back(arg); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2003 | } | 
|  | 2004 | } | 
|  | 2005 |  | 
|  | 2006 | // Expand all argument-files passed to -R. | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2007 | for (const std::string& arg : overlay_arg_list_) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 2008 | if (util::StartsWith(arg, "@")) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2009 | const std::string path = arg.substr(1, arg.size() - 1); | 
|  | 2010 | std::string error; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2011 | if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) { | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 2012 | context.GetDiagnostics()->Error(DiagMessage(path) << error); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2013 | return 1; | 
|  | 2014 | } | 
|  | 2015 | } else { | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2016 | options_.overlay_files.push_back(arg); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2017 | } | 
|  | 2018 | } | 
|  | 2019 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2020 | if (verbose_) { | 
|  | 2021 | context.SetVerbose(verbose_); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2022 | } | 
|  | 2023 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2024 | if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) { | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 2025 | context.GetDiagnostics()->Error( | 
|  | 2026 | DiagMessage() | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2027 | << "only one of --shared-lib, --static-lib, or --proto_format can be defined"); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2028 | return 1; | 
|  | 2029 | } | 
|  | 2030 |  | 
| Adam Lesinski | e59f0d8 | 2017-10-13 09:36:53 -0700 | [diff] [blame] | 2031 | // The default build type. | 
|  | 2032 | context.SetPackageType(PackageType::kApp); | 
|  | 2033 | context.SetPackageId(kAppPackageId); | 
|  | 2034 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2035 | if (shared_lib_) { | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 2036 | context.SetPackageType(PackageType::kSharedLib); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2037 | context.SetPackageId(0x00); | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2038 | } else if (static_lib_) { | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 2039 | context.SetPackageType(PackageType::kStaticLib); | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2040 | options_.output_format = OutputFormat::kProto; | 
|  | 2041 | } else if (proto_format_) { | 
|  | 2042 | options_.output_format = OutputFormat::kProto; | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2043 | } | 
|  | 2044 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2045 | if (options_.auto_namespace_static_lib) { | 
|  | 2046 | if (!static_lib_) { | 
| Chris Warrington | 481f027 | 2018-02-06 14:03:39 +0000 | [diff] [blame] | 2047 | context.GetDiagnostics()->Error( | 
|  | 2048 | DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib"); | 
|  | 2049 | return 1; | 
|  | 2050 | } | 
|  | 2051 | context.SetAutoNamespace(true); | 
|  | 2052 | } | 
|  | 2053 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2054 | if (package_id_) { | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 2055 | if (context.GetPackageType() != PackageType::kApp) { | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2056 | context.GetDiagnostics()->Error( | 
|  | 2057 | DiagMessage() << "can't specify --package-id when not building a regular app"); | 
|  | 2058 | return 1; | 
|  | 2059 | } | 
|  | 2060 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2061 | const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value()); | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2062 | if (!maybe_package_id_int) { | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2063 | context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value() | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2064 | << "' is not a valid integer"); | 
|  | 2065 | return 1; | 
|  | 2066 | } | 
|  | 2067 |  | 
|  | 2068 | const uint32_t package_id_int = maybe_package_id_int.value(); | 
| Todd Kennedy | 3251299 | 2018-04-25 16:45:59 -0700 | [diff] [blame] | 2069 | if (package_id_int > std::numeric_limits<uint8_t>::max() | 
|  | 2070 | || package_id_int == kFrameworkPackageId | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2071 | || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) { | 
| Adam Lesinski | f34b6f4 | 2017-03-03 16:33:26 -0800 | [diff] [blame] | 2072 | context.GetDiagnostics()->Error( | 
|  | 2073 | DiagMessage() << StringPrintf( | 
|  | 2074 | "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int)); | 
|  | 2075 | return 1; | 
|  | 2076 | } | 
|  | 2077 | context.SetPackageId(static_cast<uint8_t>(package_id_int)); | 
|  | 2078 | } | 
|  | 2079 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2080 | // Populate the set of extra packages for which to generate R.java. | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2081 | for (std::string& extra_package : extra_java_packages_) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2082 | // A given package can actually be a colon separated list of packages. | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 2083 | for (StringPiece package : util::Split(extra_package, ':')) { | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2084 | options_.extra_java_packages.insert(package.to_string()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2085 | } | 
|  | 2086 | } | 
|  | 2087 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2088 | if (product_list_) { | 
|  | 2089 | for (StringPiece product : util::Tokenize(product_list_.value(), ',')) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2090 | if (product != "" && product != "default") { | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2091 | options_.products.insert(product.to_string()); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2092 | } | 
|  | 2093 | } | 
|  | 2094 | } | 
|  | 2095 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 2096 | std::unique_ptr<IConfigFilter> filter; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2097 | if (!configs_.empty()) { | 
|  | 2098 | filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics()); | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 2099 | if (filter == nullptr) { | 
|  | 2100 | return 1; | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 2101 | } | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2102 | options_.table_splitter_options.config_filter = filter.get(); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2103 | } | 
|  | 2104 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2105 | if (preferred_density_) { | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 2106 | Maybe<uint16_t> density = | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2107 | ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics()); | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 2108 | if (!density) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2109 | return 1; | 
| Adam Lesinski | c51562c | 2016-04-28 11:12:38 -0700 | [diff] [blame] | 2110 | } | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2111 | options_.table_splitter_options.preferred_densities.push_back(density.value()); | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 2112 | } | 
| Adam Lesinski | c51562c | 2016-04-28 11:12:38 -0700 | [diff] [blame] | 2113 |  | 
| Adam Lesinski | d0f492d | 2017-04-03 18:12:45 -0700 | [diff] [blame] | 2114 | // Parse the split parameters. | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2115 | for (const std::string& split_arg : split_args_) { | 
|  | 2116 | options_.split_paths.push_back({}); | 
|  | 2117 | options_.split_constraints.push_back({}); | 
|  | 2118 | if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(), | 
|  | 2119 | &options_.split_constraints.back())) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2120 | return 1; | 
| Adam Lesinski | 1e21ff0 | 2016-06-24 14:57:58 -0700 | [diff] [blame] | 2121 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2122 | } | 
| Adam Lesinski | 1e21ff0 | 2016-06-24 14:57:58 -0700 | [diff] [blame] | 2123 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2124 | if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) { | 
|  | 2125 | if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(), | 
|  | 2126 | &options_.stable_id_map)) { | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2127 | return 1; | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 2128 | } | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2129 | } | 
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 2130 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2131 | // Populate some default no-compress extensions that are already compressed. | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2132 | options_.extensions_to_not_compress.insert( | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2133 | {".jpg",   ".jpeg", ".png",  ".gif", ".wav",  ".mp2",  ".mp3",  ".ogg", | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2134 | ".aac",   ".mpg",  ".mpeg", ".mid", ".midi", ".smf",  ".jet",  ".rtttl", | 
|  | 2135 | ".imy",   ".xmf",  ".mp4",  ".m4a", ".m4v",  ".3gp",  ".3gpp", ".3g2", | 
|  | 2136 | ".3gpp2", ".amr",  ".awb",  ".wma", ".wmv",  ".webm", ".mkv"}); | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2137 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2138 | // Turn off auto versioning for static-libs. | 
| Adam Lesinski | b522f04 | 2017-04-21 16:57:59 -0700 | [diff] [blame] | 2139 | if (context.GetPackageType() == PackageType::kStaticLib) { | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2140 | options_.no_auto_version = true; | 
|  | 2141 | options_.no_version_vectors = true; | 
|  | 2142 | options_.no_version_transitions = true; | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2143 | } | 
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 2144 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 2145 | Linker cmd(&context, options_); | 
| Adam Lesinski | ce5e56e | 2016-10-21 17:56:45 -0700 | [diff] [blame] | 2146 | return cmd.Run(arg_list); | 
| Adam Lesinski | 1ab598f | 2015-08-14 14:26:04 -0700 | [diff] [blame] | 2147 | } | 
|  | 2148 |  | 
| Adam Lesinski | cacb28f | 2016-10-19 12:18:14 -0700 | [diff] [blame] | 2149 | }  // namespace aapt |