use of com.google.common.collect.ImmutableSortedSet in project buck by facebook.
the class JavaDepsFinder method findDepsForBuildFiles.
private DepsForBuildFiles findDepsForBuildFiles(final TargetGraph graph, final DependencyInfo dependencyInfo, final Console console) {
// For the rules that expect to have their deps generated, look through all of their required
// symbols and try to find the build rule that provides each symbols. Store these build rules in
// the depsForBuildFiles data structure.
//
// Currently, we process each rule with autodeps=True on a single thread. See the class overview
// for DepsForBuildFiles about what it would take to do this work in a multi-threaded way.
DepsForBuildFiles depsForBuildFiles = new DepsForBuildFiles();
for (final TargetNode<?, ?> rule : dependencyInfo.rulesWithAutodeps) {
final Set<BuildTarget> providedDeps = dependencyInfo.rulesWithAutodepsToProvidedDeps.get(rule);
final Predicate<TargetNode<?, ?>> isVisibleDepNotAlreadyInProvidedDeps = provider -> provider.isVisibleTo(graph, rule) && !providedDeps.contains(provider.getBuildTarget());
final boolean isJavaTestRule = rule.getDescription() instanceof JavaTestDescription;
for (DependencyType type : DependencyType.values()) {
HashMultimap<TargetNode<?, ?>, String> ruleToSymbolsMap;
switch(type) {
case DEPS:
ruleToSymbolsMap = dependencyInfo.ruleToRequiredSymbols;
break;
case EXPORTED_DEPS:
ruleToSymbolsMap = dependencyInfo.ruleToExportedSymbols;
break;
default:
throw new IllegalStateException("Unrecognized type: " + type);
}
final DependencyType typeOfDepToAdd;
if (isJavaTestRule) {
// java_test rules do not honor exported_deps: add all dependencies to the ordinary deps.
typeOfDepToAdd = DependencyType.DEPS;
} else {
typeOfDepToAdd = type;
}
for (String requiredSymbol : ruleToSymbolsMap.get(rule)) {
BuildTarget provider = findProviderForSymbolFromBuckConfig(requiredSymbol);
if (provider != null) {
depsForBuildFiles.addDep(rule.getBuildTarget(), provider, typeOfDepToAdd);
continue;
}
Set<TargetNode<?, ?>> providers = dependencyInfo.symbolToProviders.get(requiredSymbol);
SortedSet<TargetNode<?, ?>> candidateProviders = providers.stream().filter(isVisibleDepNotAlreadyInProvidedDeps).collect(MoreCollectors.toImmutableSortedSet(Comparator.<TargetNode<?, ?>>naturalOrder()));
int numCandidates = candidateProviders.size();
if (numCandidates == 1) {
depsForBuildFiles.addDep(rule.getBuildTarget(), Iterables.getOnlyElement(candidateProviders).getBuildTarget(), typeOfDepToAdd);
} else if (numCandidates > 1) {
// Warn the user that there is an ambiguity. This could be very common with macros that
// generate multiple versions of a java_library() with the same sources.
// If numProviders is 0, then hopefully the dep is provided by something the user
// hardcoded in the BUCK file.
console.printErrorText(String.format("WARNING: Multiple providers for %s: %s. " + "Consider adding entry to .buckconfig to eliminate ambiguity:\n" + "[autodeps]\n" + "java-package-mappings = %s => %s", requiredSymbol, Joiner.on(", ").join(candidateProviders), requiredSymbol, Iterables.getFirst(candidateProviders, null)));
} else {
// If there aren't any candidates, then see if there is a visible rule that can provide
// the symbol via its exported_deps. We make this a secondary check because we prefer to
// depend on the rule that defines the symbol directly rather than one of possibly many
// rules that provides it via its exported_deps.
ImmutableSortedSet<TargetNode<?, ?>> newCandidates = providers.stream().flatMap(candidate -> dependencyInfo.ruleToRulesThatExportIt.get(candidate).stream()).filter(ruleThatExportsCandidate -> ruleThatExportsCandidate.isVisibleTo(graph, rule)).collect(MoreCollectors.toImmutableSortedSet(Comparator.<TargetNode<?, ?>>naturalOrder()));
int numNewCandidates = newCandidates.size();
if (numNewCandidates == 1) {
depsForBuildFiles.addDep(rule.getBuildTarget(), Iterables.getOnlyElement(newCandidates).getBuildTarget(), typeOfDepToAdd);
} else if (numNewCandidates > 1) {
console.printErrorText(String.format("WARNING: No providers found for '%s' for build rule %s, " + "but there are multiple rules that export a rule to provide %s: %s", requiredSymbol, rule.getBuildTarget(), requiredSymbol, Joiner.on(", ").join(newCandidates)));
}
// In the case that numNewCandidates is 0, we assume that the user is taking
// responsibility for declaring a provider for the symbol by hardcoding it in the deps.
}
}
}
}
return depsForBuildFiles;
}
use of com.google.common.collect.ImmutableSortedSet in project buck by facebook.
the class JavaFileParser method extractFeaturesFromJavaCode.
public JavaFileFeatures extractFeaturesFromJavaCode(String code) {
// For now, we will harcode this. Ultimately, we probably want to make this configurable via
// .buckconfig. For example, the Buck project itself is diligent about disallowing wildcard
// imports, but the one exception is the Java code generated via Thrift in src-gen.
final boolean shouldThrowForUnsupportedWildcardImport = false;
final AtomicBoolean isPoisonedByUnsupportedWildcardImport = new AtomicBoolean(false);
final CompilationUnit compilationUnit = makeCompilationUnitFromSource(code);
final ImmutableSortedSet.Builder<String> providedSymbols = ImmutableSortedSet.naturalOrder();
final ImmutableSortedSet.Builder<String> requiredSymbols = ImmutableSortedSet.naturalOrder();
final ImmutableSortedSet.Builder<String> exportedSymbols = ImmutableSortedSet.naturalOrder();
final ImmutableSortedSet.Builder<String> requiredSymbolsFromExplicitImports = ImmutableSortedSet.naturalOrder();
compilationUnit.accept(new ASTVisitor() {
@Nullable
private String packageName;
/** Maps simple name to fully-qualified name. */
private Map<String, String> simpleImportedTypes = new HashMap<>();
/**
* Maps wildcard import prefixes, such as {@code "java.util"} to the types in the respective
* package if a wildcard import such as {@code import java.util.*} is used.
*/
private Map<String, ImmutableSet<String>> wildcardImports = new HashMap<>();
@Override
public boolean visit(PackageDeclaration node) {
Preconditions.checkState(packageName == null, "There should be at most one package declaration");
packageName = node.getName().getFullyQualifiedName();
return false;
}
// providedSymbols
@Override
public boolean visit(TypeDeclaration node) {
// Local classes can be declared inside of methods. Skip over these.
if (node.getParent() instanceof TypeDeclarationStatement) {
return true;
}
String fullyQualifiedName = getFullyQualifiedTypeName(node);
if (fullyQualifiedName != null) {
providedSymbols.add(fullyQualifiedName);
}
@SuppressWarnings("unchecked") List<Type> interfaceTypes = node.superInterfaceTypes();
for (Type interfaceType : interfaceTypes) {
tryAddType(interfaceType, DependencyType.EXPORTED);
}
Type superclassType = node.getSuperclassType();
if (superclassType != null) {
tryAddType(superclassType, DependencyType.EXPORTED);
}
return true;
}
@Override
public boolean visit(EnumDeclaration node) {
String fullyQualifiedName = getFullyQualifiedTypeName(node);
if (fullyQualifiedName != null) {
providedSymbols.add(fullyQualifiedName);
}
return true;
}
@Override
public boolean visit(AnnotationTypeDeclaration node) {
String fullyQualifiedName = getFullyQualifiedTypeName(node);
if (fullyQualifiedName != null) {
providedSymbols.add(fullyQualifiedName);
}
return true;
}
// requiredSymbols
/**
* Uses heuristics to try to figure out what type of QualifiedName this is. Returns a non-null
* value if this is believed to be a reference that qualifies as a "required symbol"
* relationship.
*/
@Override
public boolean visit(QualifiedName node) {
QualifiedName ancestor = findMostQualifiedAncestor(node);
ASTNode parent = ancestor.getParent();
if (!(parent instanceof PackageDeclaration) && !(parent instanceof ImportDeclaration)) {
String symbol = ancestor.getFullyQualifiedName();
// lookup.
if (CharMatcher.javaUpperCase().matches(symbol.charAt(0))) {
addTypeFromDotDelimitedSequence(symbol, DependencyType.REQUIRED);
}
}
return false;
}
/**
* @param expr could be "Example", "Example.field", "com.example.Example". Note it could also
* be a built-in type, such as "java.lang.Integer", in which case it will not be added to
* the set of required symbols.
*/
private void addTypeFromDotDelimitedSequence(String expr, DependencyType dependencyType) {
// check it against JAVA_LANG_TYPES.
if (startsWithUppercaseChar(expr)) {
int index = expr.indexOf('.');
if (index >= 0) {
String leftmostComponent = expr.substring(0, index);
if (JAVA_LANG_TYPES.contains(leftmostComponent)) {
return;
}
}
}
expr = qualifyWithPackageNameIfNecessary(expr);
addSymbol(expr, dependencyType);
}
@Override
public boolean visit(ImportDeclaration node) {
String fullyQualifiedName = node.getName().getFullyQualifiedName();
// third-party code. As such, we will tolerate these for some of the common cases.
if (node.isOnDemand()) {
ImmutableSet<String> value = SUPPORTED_WILDCARD_IMPORTS.get(fullyQualifiedName);
if (value != null) {
wildcardImports.put(fullyQualifiedName, value);
return false;
} else if (shouldThrowForUnsupportedWildcardImport) {
throw new RuntimeException(String.format("Use of wildcard 'import %s.*' makes it impossible to statically determine " + "required symbols in this file. Please enumerate explicit imports.", fullyQualifiedName));
} else {
isPoisonedByUnsupportedWildcardImport.set(true);
return false;
}
}
// Only worry about the dependency on the enclosing type.
Optional<String> simpleName = getSimpleNameFromFullyQualifiedName(fullyQualifiedName);
if (simpleName.isPresent()) {
String name = simpleName.get();
int index = fullyQualifiedName.indexOf("." + name);
String enclosingType = fullyQualifiedName.substring(0, index + name.length() + 1);
requiredSymbolsFromExplicitImports.add(enclosingType);
simpleImportedTypes.put(name, enclosingType);
} else {
LOG.warn("Suspicious import lacks obvious enclosing type: %s", fullyQualifiedName);
// The one example we have seen of this in the wild is
// "org.whispersystems.curve25519.java.curve_sigs". In practice, we still need to add it
// as a required symbol in this case.
requiredSymbols.add(fullyQualifiedName);
}
return false;
}
@Override
public boolean visit(MethodInvocation node) {
if (node.getExpression() == null) {
return true;
}
String receiver = node.getExpression().toString();
if (looksLikeAType(receiver)) {
addTypeFromDotDelimitedSequence(receiver, DependencyType.REQUIRED);
}
return true;
}
/** An annotation on a member with zero arguments. */
@Override
public boolean visit(MarkerAnnotation node) {
DependencyType dependencyType = findDependencyTypeForAnnotation(node);
addSimpleTypeName(node.getTypeName(), dependencyType);
return true;
}
/** An annotation on a member with named arguments. */
@Override
public boolean visit(NormalAnnotation node) {
DependencyType dependencyType = findDependencyTypeForAnnotation(node);
addSimpleTypeName(node.getTypeName(), dependencyType);
return true;
}
/** An annotation on a member with a single, unnamed argument. */
@Override
public boolean visit(SingleMemberAnnotation node) {
DependencyType dependencyType = findDependencyTypeForAnnotation(node);
addSimpleTypeName(node.getTypeName(), dependencyType);
return true;
}
private DependencyType findDependencyTypeForAnnotation(Annotation annotation) {
ASTNode parentNode = annotation.getParent();
if (parentNode == null) {
return DependencyType.REQUIRED;
}
if (parentNode instanceof BodyDeclaration) {
// Note that BodyDeclaration is an abstract class. Its subclasses are things like
// FieldDeclaration and MethodDeclaration. We want to be sure that an annotation on any
// non-private declaration is considered an exported symbol.
BodyDeclaration declaration = (BodyDeclaration) parentNode;
int modifiers = declaration.getModifiers();
if ((modifiers & Modifier.PRIVATE) == 0) {
return DependencyType.EXPORTED;
}
}
return DependencyType.REQUIRED;
}
@Override
public boolean visit(SimpleType node) {
// This method is responsible for finding the overwhelming majority of the required symbols
// in the AST.
tryAddType(node, DependencyType.REQUIRED);
return true;
}
// exportedSymbols
@Override
public boolean visit(MethodDeclaration node) {
// Types from private method signatures need not be exported.
if ((node.getModifiers() & Modifier.PRIVATE) != 0) {
return true;
}
Type returnType = node.getReturnType2();
if (returnType != null) {
tryAddType(returnType, DependencyType.EXPORTED);
}
@SuppressWarnings("unchecked") List<SingleVariableDeclaration> params = node.parameters();
for (SingleVariableDeclaration decl : params) {
tryAddType(decl.getType(), DependencyType.EXPORTED);
}
@SuppressWarnings("unchecked") List<Type> exceptions = node.thrownExceptionTypes();
for (Type exception : exceptions) {
tryAddType(exception, DependencyType.EXPORTED);
}
return true;
}
@Override
public boolean visit(FieldDeclaration node) {
// Types from private fields need not be exported.
if ((node.getModifiers() & Modifier.PRIVATE) == 0) {
tryAddType(node.getType(), DependencyType.EXPORTED);
}
return true;
}
private void tryAddType(Type type, DependencyType dependencyType) {
if (type.isSimpleType()) {
SimpleType simpleType = (SimpleType) type;
Name simpleTypeName = simpleType.getName();
String simpleName = simpleTypeName.toString();
// rather than simply required.
if (!CharMatcher.javaUpperCase().matchesAllOf(simpleName) || (dependencyType == DependencyType.EXPORTED && simpleImportedTypes.containsKey(simpleName))) {
addSimpleTypeName(simpleTypeName, dependencyType);
}
} else if (type.isArrayType()) {
ArrayType arrayType = (ArrayType) type;
tryAddType(arrayType.getElementType(), dependencyType);
} else if (type.isParameterizedType()) {
ParameterizedType parameterizedType = (ParameterizedType) type;
tryAddType(parameterizedType.getType(), dependencyType);
@SuppressWarnings("unchecked") List<Type> argTypes = parameterizedType.typeArguments();
for (Type argType : argTypes) {
tryAddType(argType, dependencyType);
}
}
}
private void addSimpleTypeName(Name simpleTypeName, DependencyType dependencyType) {
String simpleName = simpleTypeName.toString();
if (JAVA_LANG_TYPES.contains(simpleName)) {
return;
}
String fullyQualifiedNameForSimpleName = simpleImportedTypes.get(simpleName);
if (fullyQualifiedNameForSimpleName != null) {
// May need to promote from required to exported in this case.
if (dependencyType == DependencyType.EXPORTED) {
addSymbol(fullyQualifiedNameForSimpleName, DependencyType.EXPORTED);
}
return;
}
// the iterator most of the time.
if (!wildcardImports.isEmpty()) {
for (Map.Entry<String, ImmutableSet<String>> entry : wildcardImports.entrySet()) {
Set<String> types = entry.getValue();
if (types.contains(simpleName)) {
String packageName = entry.getKey();
addSymbol(packageName + "." + simpleName, dependencyType);
return;
}
}
}
String symbol = simpleTypeName.getFullyQualifiedName();
symbol = qualifyWithPackageNameIfNecessary(symbol);
addSymbol(symbol, dependencyType);
}
private void addSymbol(String symbol, DependencyType dependencyType) {
((dependencyType == DependencyType.REQUIRED) ? requiredSymbols : exportedSymbols).add(symbol);
}
private String qualifyWithPackageNameIfNecessary(String symbol) {
if (!startsWithUppercaseChar(symbol)) {
return symbol;
}
// If the symbol starts with a capital letter, then we assume that it is a reference to
// a type in the same package.
int index = symbol.indexOf('.');
if (index >= 0) {
symbol = symbol.substring(0, index);
}
if (packageName != null) {
symbol = packageName + "." + symbol;
}
return symbol;
}
});
// TODO(bolinfest): Special treatment for exportedSymbols when poisoned by wildcard import.
ImmutableSortedSet<String> totalExportedSymbols = exportedSymbols.build();
// If we were poisoned by an unsupported wildcard import, then we should rely exclusively on
// the explicit imports to determine the required symbols.
Set<String> totalRequiredSymbols = new HashSet<>();
if (isPoisonedByUnsupportedWildcardImport.get()) {
totalRequiredSymbols.addAll(requiredSymbolsFromExplicitImports.build());
} else {
totalRequiredSymbols.addAll(requiredSymbolsFromExplicitImports.build());
totalRequiredSymbols.addAll(requiredSymbols.build());
}
// Make sure that required and exported symbols are disjoint sets.
totalRequiredSymbols.removeAll(totalExportedSymbols);
return new JavaFileFeatures(providedSymbols.build(), ImmutableSortedSet.copyOf(totalRequiredSymbols), totalExportedSymbols);
}
use of com.google.common.collect.ImmutableSortedSet in project buck by facebook.
the class JavaLibraryRules method getAbiInputs.
public static ImmutableSortedSet<SourcePath> getAbiInputs(BuildRuleResolver resolver, Iterable<BuildRule> inputs) throws NoSuchBuildTargetException {
ImmutableSortedSet.Builder<SourcePath> abiRules = ImmutableSortedSet.naturalOrder();
for (BuildRule dep : inputs) {
if (dep instanceof HasJavaAbi) {
Optional<BuildTarget> abiJarTarget = ((HasJavaAbi) dep).getAbiJar();
if (abiJarTarget.isPresent()) {
BuildRule abiJarRule = resolver.requireRule(abiJarTarget.get());
abiRules.add(Preconditions.checkNotNull(abiJarRule.getSourcePathToOutput()));
}
}
}
return abiRules.build();
}
use of com.google.common.collect.ImmutableSortedSet in project buck by facebook.
the class JavaSymbolFinder method getDefiningPaths.
/**
* Find all Java source files that define a given fully-qualified symbol (like "com.example.a.A").
* To do this, open up all the Java files that could define it (see {@link #getCandidatePaths})
* and parse them with our Eclipse-based {@link JavaFileParser}.
*/
private ImmutableSortedSet<Path> getDefiningPaths(String symbol, Collection<Path> srcRoots) {
ImmutableSortedSet.Builder<Path> definingPaths = ImmutableSortedSet.naturalOrder();
// TODO(shs96c): This should use the same javac env as was used for compiling the code.
JavaFileParser parser = JavaFileParser.createJavaFileParser(javacOptions);
for (Path candidatePath : getCandidatePaths(symbol, srcRoots)) {
Optional<String> content = projectFilesystem.readFileIfItExists(projectFilesystem.getPathForRelativeExistingPath(candidatePath));
if (content.isPresent() && parser.getExportedSymbolsFromString(content.get()).contains(symbol)) {
definingPaths.add(candidatePath);
}
}
return definingPaths.build();
}
use of com.google.common.collect.ImmutableSortedSet in project buck by facebook.
the class SwiftLibraryDescription method createBuildRule.
@Override
public <A extends SwiftLibraryDescription.Arg> BuildRule createBuildRule(TargetGraph targetGraph, BuildRuleParams params, final BuildRuleResolver resolver, A args) throws NoSuchBuildTargetException {
Optional<LinkerMapMode> flavoredLinkerMapMode = LinkerMapMode.FLAVOR_DOMAIN.getValue(params.getBuildTarget());
params = LinkerMapMode.removeLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode);
final BuildTarget buildTarget = params.getBuildTarget();
// See if we're building a particular "type" and "platform" of this library, and if so, extract
// them from the flavors attached to the build target.
Optional<Map.Entry<Flavor, CxxPlatform>> platform = cxxPlatformFlavorDomain.getFlavorAndValue(buildTarget);
final ImmutableSortedSet<Flavor> buildFlavors = buildTarget.getFlavors();
ImmutableSortedSet<BuildRule> filteredExtraDeps = params.getExtraDeps().get().stream().filter(input -> !input.getBuildTarget().getUnflavoredBuildTarget().equals(buildTarget.getUnflavoredBuildTarget())).collect(MoreCollectors.toImmutableSortedSet());
params = params.copyReplacingExtraDeps(Suppliers.ofInstance(filteredExtraDeps));
if (!buildFlavors.contains(SWIFT_COMPANION_FLAVOR) && platform.isPresent()) {
final CxxPlatform cxxPlatform = platform.get().getValue();
Optional<SwiftPlatform> swiftPlatform = swiftPlatformFlavorDomain.getValue(buildTarget);
if (!swiftPlatform.isPresent()) {
throw new HumanReadableException("Platform %s is missing swift compiler", cxxPlatform);
}
// See if we're building a particular "type" and "platform" of this library, and if so,
// extract them from the flavors attached to the build target.
Optional<Map.Entry<Flavor, Type>> type = LIBRARY_TYPE.getFlavorAndValue(buildTarget);
if (!buildFlavors.contains(SWIFT_COMPILE_FLAVOR) && type.isPresent()) {
Set<Flavor> flavors = Sets.newHashSet(params.getBuildTarget().getFlavors());
flavors.remove(type.get().getKey());
BuildTarget target = BuildTarget.builder(params.getBuildTarget().getUnflavoredBuildTarget()).addAllFlavors(flavors).build();
if (flavoredLinkerMapMode.isPresent()) {
target = target.withAppendedFlavors(flavoredLinkerMapMode.get().getFlavor());
}
BuildRuleParams typeParams = params.withBuildTarget(target);
switch(type.get().getValue()) {
case SHARED:
return createSharedLibraryBuildRule(typeParams, resolver, target, swiftPlatform.get(), cxxPlatform, args.soname, flavoredLinkerMapMode);
case STATIC:
case MACH_O_BUNDLE:
}
throw new RuntimeException("unhandled library build type");
}
// All swift-compile rules of swift-lib deps are required since we need their swiftmodules
// during compilation.
final Function<BuildRule, BuildRule> requireSwiftCompile = input -> {
try {
Preconditions.checkArgument(input instanceof SwiftLibrary);
return ((SwiftLibrary) input).requireSwiftCompileRule(cxxPlatform.getFlavor());
} catch (NoSuchBuildTargetException e) {
throw new HumanReadableException(e, "Could not find SwiftCompile with target %s", buildTarget);
}
};
params = params.copyAppendingExtraDeps(params.getDeps().stream().filter(SwiftLibrary.class::isInstance).map(requireSwiftCompile).collect(MoreCollectors.toImmutableSet()));
params = params.copyAppendingExtraDeps(params.getDeps().stream().filter(CxxLibrary.class::isInstance).map(input -> {
BuildTarget companionTarget = input.getBuildTarget().withAppendedFlavors(SWIFT_COMPANION_FLAVOR);
return resolver.getRuleOptional(companionTarget).map(requireSwiftCompile);
}).filter(Optional::isPresent).map(Optional::get).collect(MoreCollectors.toImmutableSortedSet()));
return new SwiftCompile(cxxPlatform, swiftBuckConfig, params, swiftPlatform.get().getSwift(), args.frameworks, args.moduleName.orElse(buildTarget.getShortName()), BuildTargets.getGenPath(params.getProjectFilesystem(), buildTarget, "%s"), args.srcs, args.compilerFlags, args.enableObjcInterop, args.bridgingHeader);
}
// Otherwise, we return the generic placeholder of this library.
params = LinkerMapMode.restoreLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode);
return new SwiftLibrary(params, resolver, ImmutableSet.of(), swiftPlatformFlavorDomain, args.frameworks, args.libraries, args.supportedPlatformsRegex, args.preferredLinkage.orElse(NativeLinkable.Linkage.ANY));
}
Aggregations