// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
package com.twitter.intellij.pants.service.project.modifier;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.twitter.intellij.pants.PantsException;
import com.twitter.intellij.pants.service.PantsCompileOptionsExecutor;
import com.twitter.intellij.pants.service.project.PantsProjectInfoModifierExtension;
import com.twitter.intellij.pants.service.project.model.ProjectInfo;
import com.twitter.intellij.pants.service.project.model.TargetInfo;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class PantsCyclicDependenciesModifier implements PantsProjectInfoModifierExtension {
@Override
public void modify(@NotNull ProjectInfo projectInfo, @NotNull PantsCompileOptionsExecutor executor, @NotNull Logger log) {
final Set<Map.Entry<String, TargetInfo>> originalEntries =
new HashSet<Map.Entry<String, TargetInfo>>(projectInfo.getTargets().entrySet());
for (Map.Entry<String, TargetInfo> nameAndInfo : originalEntries) {
final String targetName = nameAndInfo.getKey();
final TargetInfo targetInfo = nameAndInfo.getValue();
if (!projectInfo.getTargets().containsKey(targetName)) {
// already removed
continue;
}
for (String dependencyTargetName : targetInfo.getTargets()) {
TargetInfo dependencyTargetInfo = projectInfo.getTarget(dependencyTargetName);
if (dependencyTargetInfo != null && dependencyTargetInfo.dependOn(targetName)) {
if (targetName.equals(dependencyTargetName)) {
throw new PantsException(String.format("Self cyclic dependency found %s", targetName));
}
log.info(String.format("Found cyclic dependency between %s and %s", targetName, dependencyTargetName));
final String combinedTargetName = combinedTargetsName(targetName, dependencyTargetName);
final TargetInfo combinedInfo = targetInfo.union(dependencyTargetInfo);
combinedInfo.removeDependency(targetName);
combinedInfo.removeDependency(dependencyTargetName);
projectInfo.addTarget(combinedTargetName, combinedInfo);
projectInfo.replaceDependency(targetName, combinedTargetName);
projectInfo.removeTarget(targetName);
projectInfo.replaceDependency(dependencyTargetName, combinedTargetName);
projectInfo.removeTarget(dependencyTargetName);
}
}
}
}
@NotNull
private String combinedTargetsName(String... targetNames) {
assert targetNames.length > 0;
String commonPrefix = targetNames[0];
for (String name : targetNames) {
commonPrefix = StringUtil.commonPrefix(commonPrefix, name);
}
final String finalCommonPrefix = commonPrefix;
return
commonPrefix +
StringUtil.join(
ContainerUtil.sorted(
ContainerUtil.map(
targetNames,
new Function<String, String>() {
@Override
public String fun(String targetName) {
return targetName.substring(finalCommonPrefix.length());
}
}
)
),
"_and_"
);
}
}