Search in sources :

Example 1 with PluginDependency

use of org.spongepowered.plugin.metadata.model.PluginDependency in project SpongeCommon by SpongePowered.

the class DependencyResolver method resolveAndSortCandidates.

public static <T extends PluginResource> ResolutionResult<T> resolveAndSortCandidates(final Collection<PluginCandidate<T>> candidates, final Logger logger) {
    final Map<String, Node<T>> nodes = new HashMap<>();
    final ResolutionResult<T> resolutionResult = new ResolutionResult<>();
    for (final PluginCandidate<T> candidate : candidates) {
        final String id = candidate.metadata().id();
        // If we already have an entry, this is now a duplicate ID situation.
        if (nodes.containsKey(id)) {
            resolutionResult.duplicateIds().add(id);
        } else {
            nodes.put(id, new Node<>(candidate));
        }
    }
    for (final Map.Entry<String, Node<T>> entry : nodes.entrySet()) {
        // Attach deps, invalid deps will appear at this point.
        final Node<T> node = entry.getValue();
        for (final PluginDependency pd : node.candidate.metadata().dependencies()) {
            final boolean isOptional = pd.optional();
            final Node<T> dep = nodes.get(pd.id());
            if (dep == null) {
                if (isOptional) {
                    // just move on to the next dep
                    continue;
                }
                node.invalid = true;
                final String failure;
                if (pd.version() != null) {
                    failure = String.format("%s version %s", pd.id(), pd.version());
                } else {
                    failure = pd.id();
                }
                resolutionResult.missingDependencies().computeIfAbsent(entry.getValue().candidate, k -> new ArrayList<>()).add(failure);
                // no need to process this further
                node.checked = true;
                continue;
            }
            if (!DependencyResolver.checkVersion(pd.version(), dep.candidate.metadata().version())) {
                if (isOptional) {
                    // just move on to the next dep
                    continue;
                }
                resolutionResult.versionMismatch().computeIfAbsent(entry.getValue().candidate, k -> new ArrayList<>()).add(Tuple.of(pd.version().toString(), dep.candidate));
                node.invalid = true;
                // no need to process this further.
                node.checked = true;
            }
            if (pd.loadOrder() == PluginDependency.LoadOrder.BEFORE) {
                if (!pd.optional()) {
                    // Because of what we're about to do, we need to just make sure that
                    // if the "before" dep fails within here, then we still throw it out.
                    // Note, we can only do this for sorting, once sorted, if this loads
                    // but the before dep doesn't, well, it's on the plugin to solve, not
                    // us.
                    node.beforeRequiredDependency.add(node);
                }
                // don't bomb out the dep if this doesn't load - so set it to be optional.
                // however, we otherwise treat it as an AFTER dep on the target dep
                DependencyResolver.setDependency(dep, node, true);
            } else {
                DependencyResolver.setDependency(node, dep, pd.optional());
            }
        }
    }
    // Check for invalid deps
    DependencyResolver.checkCyclic(nodes.values(), resolutionResult);
    for (final Node<T> node : nodes.values()) {
        DependencyResolver.calculateSecondaryFailures(node, resolutionResult);
    }
    // Now to sort them.
    final List<Node<T>> original = nodes.values().stream().filter(x -> !x.invalid).collect(Collectors.toCollection(ArrayList::new));
    final List<Node<T>> toLoad = new ArrayList<>(original);
    final LinkedHashSet<Node<T>> sorted = new LinkedHashSet<>();
    toLoad.stream().filter(x -> x.dependencies.isEmpty() && x.optionalDependencies.isEmpty()).forEach(sorted::add);
    toLoad.removeIf(sorted::contains);
    int size = toLoad.size();
    boolean excludeOptionals = false;
    while (!toLoad.isEmpty()) {
        boolean containsOptionalDeps = false;
        for (final Node<T> node : toLoad) {
            if (sorted.containsAll(node.dependencies) && DependencyResolver.checkOptionalDependencies(excludeOptionals, sorted, node)) {
                final boolean hasOptionalDeps = !node.optionalDependencies.isEmpty();
                containsOptionalDeps |= hasOptionalDeps;
                sorted.add(node);
                if (excludeOptionals && hasOptionalDeps) {
                    logger.warn("Plugin {} will be loaded before its optional dependencies: [ {} ]", node.candidate.metadata().id(), node.optionalDependencies.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", ")));
                }
            }
        }
        toLoad.removeIf(sorted::contains);
        if (toLoad.size() == size) {
            // without them.
            if (excludeOptionals || !containsOptionalDeps) {
                // We have a problem
                throw new IllegalStateException(String.format("Dependency resolver could not resolve order of all plugins.\n\n" + "Attempted to sort %d plugins: [ %s ]\n" + "Could not sort %d plugins: [ %s ]", original.size(), original.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", ")), toLoad.size(), toLoad.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", "))));
            }
            logger.warn("Failed to resolve plugin load order due to failed dependency resolution, attempting to resolve order ignoring optional" + " dependencies.");
            excludeOptionals = true;
        } else {
            size = toLoad.size();
            excludeOptionals = false;
        }
    }
    final Collection<PluginCandidate<T>> sortedSuccesses = resolutionResult.sortedSuccesses();
    for (final Node<T> x : sorted) {
        sortedSuccesses.add(x.candidate);
    }
    return resolutionResult;
}
Also used : PluginCandidate(org.spongepowered.plugin.PluginCandidate) Collection(java.util.Collection) ArtifactVersion(org.apache.maven.artifact.versioning.ArtifactVersion) Set(java.util.Set) HashMap(java.util.HashMap) Tuple(org.spongepowered.api.util.Tuple) Collectors(java.util.stream.Collectors) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Objects(java.util.Objects) List(java.util.List) Logger(org.apache.logging.log4j.Logger) PluginDependency(org.spongepowered.plugin.metadata.model.PluginDependency) Map(java.util.Map) VersionRange(org.apache.maven.artifact.versioning.VersionRange) LinkedList(java.util.LinkedList) PluginResource(org.spongepowered.plugin.PluginResource) Nullable(org.checkerframework.checker.nullness.qual.Nullable) LinkedHashSet(java.util.LinkedHashSet) LinkedHashSet(java.util.LinkedHashSet) PluginDependency(org.spongepowered.plugin.metadata.model.PluginDependency) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) PluginCandidate(org.spongepowered.plugin.PluginCandidate) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with PluginDependency

use of org.spongepowered.plugin.metadata.model.PluginDependency in project SpongeCommon by SpongePowered.

the class PluginFileConfigurable method getConfigList.

@Override
public List<? extends IConfigurable> getConfigList(final String... key) {
    if (key.length < 1) {
        return Collections.emptyList();
    }
    final String query = key[0];
    if (key.length != this.requiredConfigElements(query)) {
        return Collections.emptyList();
    }
    if ("mods".equals(query)) {
        final Set<PluginMetadata> metadataById = this.container.metadata();
        if (metadataById.isEmpty()) {
            return Collections.emptyList();
        }
        final List<IConfigurable> metadataConfigurables = new ArrayList<>();
        metadataById.forEach((metadata) -> metadataConfigurables.add(new PluginMetadataConfigurable(metadata)));
        return metadataConfigurables;
    }
    if (key.length != 2) {
        return Collections.emptyList();
    }
    final String plugin = key[1];
    final PluginMetadata metadata = this.container.metadata(plugin).orElse(null);
    if (metadata == null) {
        return Collections.emptyList();
    }
    if ("dependencies".equals(query)) {
        // TODO Should we inject a dependency on SpongeForge?
        final Set<PluginDependency> dependencies = metadata.dependencies();
        if (dependencies.isEmpty()) {
            return Collections.emptyList();
        }
        final List<IConfigurable> depConfigurables = new ArrayList<>();
        for (final PluginDependency dependency : dependencies) {
            depConfigurables.add(new PluginDependencyConfigurable(metadata, dependency));
        }
        return depConfigurables;
    }
    return Collections.emptyList();
}
Also used : PluginDependency(org.spongepowered.plugin.metadata.model.PluginDependency) ArrayList(java.util.ArrayList) PluginMetadata(org.spongepowered.plugin.metadata.PluginMetadata) IConfigurable(net.minecraftforge.forgespi.language.IConfigurable)

Aggregations

ArrayList (java.util.ArrayList)2 PluginDependency (org.spongepowered.plugin.metadata.model.PluginDependency)2 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 LinkedHashSet (java.util.LinkedHashSet)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 Map (java.util.Map)1 Objects (java.util.Objects)1 Set (java.util.Set)1 Collectors (java.util.stream.Collectors)1 IConfigurable (net.minecraftforge.forgespi.language.IConfigurable)1 Logger (org.apache.logging.log4j.Logger)1 ArtifactVersion (org.apache.maven.artifact.versioning.ArtifactVersion)1 VersionRange (org.apache.maven.artifact.versioning.VersionRange)1 Nullable (org.checkerframework.checker.nullness.qual.Nullable)1 Tuple (org.spongepowered.api.util.Tuple)1 PluginCandidate (org.spongepowered.plugin.PluginCandidate)1 PluginResource (org.spongepowered.plugin.PluginResource)1