blob: 5020536bd941256980ce4b07dca87e55c5beb475 [file] [log] [blame]
// Copyright 2017 The Tulsi Authors. All rights reserved.
//
// 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.
import Foundation
/// This class acts as a data store for all of the RuleEntries that are read by our Aspects. It
/// effectively maps from BuildLabel to RuleEntry, with the caveat that a single BuildLabel may
/// actually have multiple RuleEntries depending on Bazel configuration splits. For example, if an
/// objc_library is depended on by an ios_application with a minimum_os_version of 9 and depended on
/// by an ios_unit_test with a minimum_os_version of 10, then we will actually discover two versions
/// of the same objc_library, one for iOS 10 and the other for iOS 9.
public class RuleEntryMap {
private var labelToEntries = [BuildLabel: [RuleEntry]]()
private var allEntries = [RuleEntry]()
private let localizedMessageLogger: LocalizedMessageLogger?
init(localizedMessageLogger: LocalizedMessageLogger? = nil) {
self.localizedMessageLogger = localizedMessageLogger
}
init(_ ruleEntryMap: RuleEntryMap) {
localizedMessageLogger = ruleEntryMap.localizedMessageLogger
allEntries = ruleEntryMap.allEntries
labelToEntries = ruleEntryMap.labelToEntries
}
public var allRuleEntries: [RuleEntry] {
return allEntries
}
public func insert(ruleEntry: RuleEntry) {
allEntries.append(ruleEntry)
let label = ruleEntry.label
guard var entries = labelToEntries[label] else {
labelToEntries[label] = [ruleEntry]
return
}
// Don't warn about duplicate entries with the same DeploymentTarget as they should
// only be caused by a root-level library which we already warn about.
entries.append(ruleEntry)
labelToEntries[label] = entries
}
public func hasAnyRuleEntry(withBuildLabel buildLabel: BuildLabel) -> Bool {
return anyRuleEntry(withBuildLabel: buildLabel) != nil
}
/// Returns a RuleEntry from the list of RuleEntries with the specified BuildLabel.
public func anyRuleEntry(withBuildLabel buildLabel: BuildLabel) -> RuleEntry? {
guard let ruleEntries = labelToEntries[buildLabel] else {
return nil
}
return ruleEntries.last
}
/// Returns a list of RuleEntry with the specified BuildLabel.
public func ruleEntries(buildLabel: BuildLabel) -> [RuleEntry] {
guard let ruleEntries = labelToEntries[buildLabel] else {
return [RuleEntry]()
}
return ruleEntries
}
/// Returns a RuleEntry which is a dep of the given RuleEntry, matched by configuration.
public func ruleEntry(buildLabel: BuildLabel, depender: RuleEntry) -> RuleEntry? {
guard let deploymentTarget = depender.deploymentTarget else {
localizedMessageLogger?.warning("DependentRuleEntryHasNoDeploymentTarget",
comment: "Error when a RuleEntry with deps does not have a DeploymentTarget. RuleEntry's label is in %1$@, dep's label is in %2$@.",
values: depender.label.description,
buildLabel.description)
return anyRuleEntry(withBuildLabel: buildLabel)
}
return ruleEntry(buildLabel: buildLabel, deploymentTarget: deploymentTarget)
}
/// Returns a RuleEntry with the given buildLabel and deploymentTarget.
public func ruleEntry(buildLabel: BuildLabel, deploymentTarget: DeploymentTarget) -> RuleEntry? {
guard let ruleEntries = labelToEntries[buildLabel] else {
return nil
}
guard !ruleEntries.isEmpty else {
return nil
}
// If there's only one, we just assume that it's right.
if ruleEntries.count == 1 {
return ruleEntries.first
}
for ruleEntry in ruleEntries {
if deploymentTarget == ruleEntry.deploymentTarget {
return ruleEntry
}
}
// Must be multiple. Shoot out a warning and return the last.
localizedMessageLogger?.warning("AmbiguousRuleEntryReference",
comment: "Warning when unable to resolve a RuleEntry for a given DeploymentTarget. RuleEntry's label is in %1$@.",
values: buildLabel.description)
return ruleEntries.last
}
}