diff options
Diffstat (limited to 'tools/aapt2/link/AutoVersioner.cpp')
-rw-r--r-- | tools/aapt2/link/AutoVersioner.cpp | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp new file mode 100644 index 000000000000..c7e603ea3774 --- /dev/null +++ b/tools/aapt2/link/AutoVersioner.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConfigDescription.h" +#include "ResourceTable.h" +#include "SdkConstants.h" +#include "ValueVisitor.h" + +#include "link/Linkers.h" +#include "util/Comparators.h" + +#include <algorithm> +#include <cassert> + +namespace aapt { + +bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config, + const int sdkVersionToGenerate) { + assert(sdkVersionToGenerate > config.sdkVersion); + const auto endIter = entry->values.end(); + auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig); + + // The source config came from this list, so it should be here. + assert(iter != entry->values.end()); + ++iter; + + // The next configuration either only varies in sdkVersion, or it is completely different + // and therefore incompatible. If it is incompatible, we must generate the versioned resource. + + // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other + // qualifiers, so we need to iterate through the entire list to be sure there + // are no higher sdk level versions of this resource. + ConfigDescription tempConfig(config); + for (; iter != endIter; ++iter) { + tempConfig.sdkVersion = iter->config.sdkVersion; + if (tempConfig == iter->config) { + // The two configs are the same, check the sdk version. + return sdkVersionToGenerate < iter->config.sdkVersion; + } + } + + // No match was found, so we should generate the versioned resource. + return true; +} + +bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) { + for (auto& package : table->packages) { + for (auto& type : package->types) { + if (type->type != ResourceType::kStyle) { + continue; + } + + for (auto& entry : type->entries) { + for (size_t i = 0; i < entry->values.size(); i++) { + ResourceConfigValue& configValue = entry->values[i]; + if (configValue.config.sdkVersion >= SDK_LOLLIPOP_MR1) { + // If this configuration is only used on L-MR1 then we don't need + // to do anything since we use private attributes since that version. + continue; + } + + if (Style* style = valueCast<Style>(configValue.value.get())) { + Maybe<size_t> minSdkStripped; + std::vector<Style::Entry> stripped; + + auto iter = style->entries.begin(); + while (iter != style->entries.end()) { + assert(iter->key.id && "IDs must be assigned and linked"); + + // Find the SDK level that is higher than the configuration allows. + const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value()); + if (sdkLevel > std::max<size_t>(configValue.config.sdkVersion, 1)) { + // Record that we are about to strip this. + stripped.emplace_back(std::move(*iter)); + + // We use the smallest SDK level to generate the new style. + if (minSdkStripped) { + minSdkStripped = std::min(minSdkStripped.value(), sdkLevel); + } else { + minSdkStripped = sdkLevel; + } + + // Erase this from this style. + iter = style->entries.erase(iter); + continue; + } + ++iter; + } + + if (minSdkStripped && !stripped.empty()) { + // We found attributes from a higher SDK level. Check that + // there is no other defined resource for the version we want to + // generate. + if (shouldGenerateVersionedResource(entry.get(), + configValue.config, + minSdkStripped.value())) { + // Let's create a new Style for this versioned resource. + ConfigDescription newConfig(configValue.config); + newConfig.sdkVersion = minSdkStripped.value(); + + std::unique_ptr<Style> newStyle(style->clone(&table->stringPool)); + newStyle->setComment(style->getComment()); + newStyle->setSource(style->getSource()); + + // Move the previously stripped attributes into this style. + newStyle->entries.insert(newStyle->entries.end(), + std::make_move_iterator(stripped.begin()), + std::make_move_iterator(stripped.end())); + + // Insert the new Resource into the correct place. + auto iter = std::lower_bound(entry->values.begin(), + entry->values.end(), + newConfig, + cmp::lessThanConfig); + + entry->values.insert( + iter, + ResourceConfigValue{ newConfig, std::move(newStyle) }); + } + } + } + } + } + } + } + return true; +} + +} // namespace aapt |