-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce PyInfo to replace the legacy "py" struct provider
This CL adds the PyInfo data type and registers it with a bootstrap. A follow-up CL will make the Python rules actually produce and consume this provider. The new provider's API exactly mirrors the current API of the legacy struct provider, as encapsulated by PyStructUtils. Work toward #7010. RELNOTES: None PiperOrigin-RevId: 231460993
- Loading branch information
Showing
17 changed files
with
767 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
251 changes: 251 additions & 0 deletions
251
src/main/java/com/google/devtools/build/lib/rules/python/PyInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
// Copyright 2019 The Bazel 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. | ||
|
||
package com.google.devtools.build.lib.rules.python; | ||
|
||
import com.google.common.base.Preconditions; | ||
import com.google.devtools.build.lib.actions.Artifact; | ||
import com.google.devtools.build.lib.collect.nestedset.NestedSet; | ||
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; | ||
import com.google.devtools.build.lib.collect.nestedset.Order; | ||
import com.google.devtools.build.lib.events.Location; | ||
import com.google.devtools.build.lib.packages.BuiltinProvider; | ||
import com.google.devtools.build.lib.packages.Info; | ||
import com.google.devtools.build.lib.skylarkbuildapi.python.PyInfoApi; | ||
import com.google.devtools.build.lib.syntax.EvalException; | ||
import com.google.devtools.build.lib.syntax.EvalUtils; | ||
import com.google.devtools.build.lib.syntax.Runtime; | ||
import com.google.devtools.build.lib.syntax.SkylarkNestedSet; | ||
import java.util.Objects; | ||
import javax.annotation.Nullable; | ||
|
||
/** Instance of the provider type for the Python rules. */ | ||
public class PyInfo extends Info implements PyInfoApi<Artifact> { | ||
|
||
public static final String STARLARK_NAME = "PyInfo"; | ||
|
||
public static final PyInfoProvider PROVIDER = new PyInfoProvider(); | ||
|
||
/** | ||
* Returns true if the given depset has a content type that is a subtype of the given class, and | ||
* has an order compatible with the given order. | ||
*/ | ||
private static boolean depsetHasTypeAndCompatibleOrder( | ||
SkylarkNestedSet depset, Class<?> clazz, Order order) { | ||
// Work around #7266 by special-casing the empty set in the type check. | ||
boolean typeOk = depset.isEmpty() || depset.getContentType().canBeCastTo(clazz); | ||
boolean orderOk = depset.getOrder().isCompatible(order); | ||
return typeOk && orderOk; | ||
} | ||
|
||
/** | ||
* Returns the type name of a value and possibly additional description. | ||
* | ||
* <p>For depsets, this includes its content type and order. | ||
*/ | ||
private static String describeType(Object value) { | ||
String typeName = EvalUtils.getDataTypeName(value, /*fullDetails=*/ true); | ||
if (value instanceof SkylarkNestedSet) { | ||
return ((SkylarkNestedSet) value).getOrder().getSkylarkName() + "-ordered " + typeName; | ||
} else { | ||
return typeName; | ||
} | ||
} | ||
|
||
private final SkylarkNestedSet transitiveSources; | ||
private final boolean usesSharedLibraries; | ||
private final SkylarkNestedSet imports; | ||
private final boolean hasPy2OnlySources; | ||
private final boolean hasPy3OnlySources; | ||
|
||
private PyInfo( | ||
@Nullable Location location, | ||
SkylarkNestedSet transitiveSources, | ||
boolean usesSharedLibraries, | ||
SkylarkNestedSet imports, | ||
boolean hasPy2OnlySources, | ||
boolean hasPy3OnlySources) { | ||
super(PROVIDER, location); | ||
Preconditions.checkArgument( | ||
depsetHasTypeAndCompatibleOrder(transitiveSources, Artifact.class, Order.COMPILE_ORDER)); | ||
// TODO(brandjon): PyCommon currently requires COMPILE_ORDER, but we'll probably want to change | ||
// that to NAIVE_LINK (preorder). In the meantime, order isn't an invariant of the provider | ||
// itself, so we use STABLE here to accept any order. | ||
Preconditions.checkArgument( | ||
depsetHasTypeAndCompatibleOrder(imports, String.class, Order.STABLE_ORDER)); | ||
this.transitiveSources = transitiveSources; | ||
this.usesSharedLibraries = usesSharedLibraries; | ||
this.imports = imports; | ||
this.hasPy2OnlySources = hasPy2OnlySources; | ||
this.hasPy3OnlySources = hasPy3OnlySources; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
// PyInfo implements value equality, but note that it contains identity-equality fields | ||
// (depsets), so you generally shouldn't rely on equality comparisons. | ||
if (!(other instanceof PyInfo)) { | ||
return false; | ||
} | ||
PyInfo otherInfo = (PyInfo) other; | ||
return (this.transitiveSources.equals(otherInfo.transitiveSources) | ||
&& this.usesSharedLibraries == otherInfo.usesSharedLibraries | ||
&& this.imports.equals(otherInfo.imports) | ||
&& this.hasPy2OnlySources == otherInfo.hasPy2OnlySources | ||
&& this.hasPy3OnlySources == otherInfo.hasPy3OnlySources); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash( | ||
PyInfo.class, | ||
transitiveSources, | ||
usesSharedLibraries, | ||
imports, | ||
hasPy2OnlySources, | ||
hasPy3OnlySources); | ||
} | ||
|
||
@Override | ||
public SkylarkNestedSet getTransitiveSources() { | ||
return transitiveSources; | ||
} | ||
|
||
@Override | ||
public boolean getUsesSharedLibraries() { | ||
return usesSharedLibraries; | ||
} | ||
|
||
@Override | ||
public SkylarkNestedSet getImports() { | ||
return imports; | ||
} | ||
|
||
@Override | ||
public boolean getHasPy2OnlySources() { | ||
return hasPy2OnlySources; | ||
} | ||
|
||
@Override | ||
public boolean getHasPy3OnlySources() { | ||
return hasPy3OnlySources; | ||
} | ||
|
||
/** The singular PyInfo provider type object. */ | ||
public static class PyInfoProvider extends BuiltinProvider<PyInfo> | ||
implements PyInfoApi.PyInfoProviderApi { | ||
|
||
private PyInfoProvider() { | ||
super(STARLARK_NAME, PyInfo.class); | ||
} | ||
|
||
@Override | ||
public PyInfo constructor( | ||
SkylarkNestedSet transitiveSources, | ||
boolean usesSharedLibraries, | ||
Object importsUncast, | ||
boolean hasPy2OnlySources, | ||
boolean hasPy3OnlySources, | ||
Location loc) | ||
throws EvalException { | ||
SkylarkNestedSet imports = | ||
importsUncast.equals(Runtime.UNBOUND) | ||
? SkylarkNestedSet.of(String.class, NestedSetBuilder.emptySet(Order.COMPILE_ORDER)) | ||
: (SkylarkNestedSet) importsUncast; | ||
|
||
if (!depsetHasTypeAndCompatibleOrder( | ||
transitiveSources, Artifact.class, Order.COMPILE_ORDER)) { | ||
throw new EvalException( | ||
loc, | ||
String.format( | ||
"'transitive_sources' field should be a postorder-compatible depset of Files (got " | ||
+ "a '%s')", | ||
describeType(transitiveSources))); | ||
} | ||
if (!depsetHasTypeAndCompatibleOrder(imports, String.class, Order.STABLE_ORDER)) { | ||
throw new EvalException( | ||
loc, | ||
String.format( | ||
"'imports' field should be a depset of strings (got a '%s')", | ||
describeType(imports))); | ||
} | ||
|
||
return new PyInfo( | ||
loc, | ||
transitiveSources, | ||
usesSharedLibraries, | ||
imports, | ||
hasPy2OnlySources, | ||
hasPy3OnlySources); | ||
} | ||
} | ||
|
||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
/** Builder for {@link PyInfo}. */ | ||
public static class Builder { | ||
Location location = null; | ||
NestedSet<Artifact> transitiveSources = NestedSetBuilder.emptySet(Order.COMPILE_ORDER); | ||
boolean usesSharedLibraries = false; | ||
NestedSet<String> imports = NestedSetBuilder.emptySet(Order.COMPILE_ORDER); | ||
boolean hasPy2OnlySources = false; | ||
boolean hasPy3OnlySources = false; | ||
|
||
// Use the static builder() method instead. | ||
private Builder() {} | ||
|
||
public Builder setLocation(Location location) { | ||
this.location = location; | ||
return this; | ||
} | ||
|
||
public Builder setTransitiveSources(NestedSet<Artifact> transitiveSources) { | ||
this.transitiveSources = transitiveSources; | ||
return this; | ||
} | ||
|
||
public Builder setUsesSharedLibraries(boolean usesSharedLibraries) { | ||
this.usesSharedLibraries = usesSharedLibraries; | ||
return this; | ||
} | ||
|
||
public Builder setImports(NestedSet<String> imports) { | ||
this.imports = imports; | ||
return this; | ||
} | ||
|
||
public Builder setHasPy2OnlySources(boolean hasPy2OnlySources) { | ||
this.hasPy2OnlySources = hasPy2OnlySources; | ||
return this; | ||
} | ||
|
||
public Builder setHasPy3OnlySources(boolean hasPy3OnlySources) { | ||
this.hasPy3OnlySources = hasPy3OnlySources; | ||
return this; | ||
} | ||
|
||
public PyInfo build() { | ||
Preconditions.checkNotNull(transitiveSources); | ||
return new PyInfo( | ||
location, | ||
SkylarkNestedSet.of(Artifact.class, transitiveSources), | ||
usesSharedLibraries, | ||
SkylarkNestedSet.of(String.class, imports), | ||
hasPy2OnlySources, | ||
hasPy3OnlySources); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.