Skip to content

Commit

Permalink
Introduce PyInfo to replace the legacy "py" struct provider
Browse files Browse the repository at this point in the history
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 bazelbuild#7010.

RELNOTES: None
PiperOrigin-RevId: 231460993
  • Loading branch information
brandjon authored and weixiao-huang committed Jan 31, 2019
1 parent 48bfb25 commit 3c67250
Show file tree
Hide file tree
Showing 17 changed files with 767 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/main/java/com/google/devtools/build/docgen/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/python",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/repository",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/test",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi",
Expand All @@ -32,6 +33,7 @@ java_library(
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/java",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/platform",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/python",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/test",
"//src/main/java/com/google/devtools/common/options",
Expand Down Expand Up @@ -84,6 +86,7 @@ filegroup(
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/cpp:srcs",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/java:srcs",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/platform:srcs",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/python:srcs",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/repository:srcs",
"//src/main/java/com/google/devtools/build/skydoc/fakebuildapi/test:srcs",
"//src/main/java/com/google/devtools/build/skydoc/rendering:srcs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcBootstrap;
import com.google.devtools.build.lib.skylarkbuildapi.java.JavaBootstrap;
import com.google.devtools.build.lib.skylarkbuildapi.platform.PlatformBootstrap;
import com.google.devtools.build.lib.skylarkbuildapi.python.PyBootstrap;
import com.google.devtools.build.lib.skylarkbuildapi.repository.RepositoryBootstrap;
import com.google.devtools.build.lib.skylarkbuildapi.test.TestingBootstrap;
import com.google.devtools.build.lib.syntax.MethodLibrary;
Expand Down Expand Up @@ -55,6 +56,7 @@
import com.google.devtools.build.skydoc.fakebuildapi.java.FakeJavaInfo.FakeJavaInfoProvider;
import com.google.devtools.build.skydoc.fakebuildapi.java.FakeJavaProtoCommon;
import com.google.devtools.build.skydoc.fakebuildapi.platform.FakePlatformCommon;
import com.google.devtools.build.skydoc.fakebuildapi.python.FakePyInfo.FakePyInfoProvider;
import com.google.devtools.build.skydoc.fakebuildapi.repository.FakeRepositoryModule;
import com.google.devtools.build.skydoc.fakebuildapi.test.FakeAnalysisFailureInfoProvider;
import com.google.devtools.build.skydoc.fakebuildapi.test.FakeAnalysisTestResultInfoProvider;
Expand Down Expand Up @@ -186,6 +188,7 @@ private Map<String, Object> collectBzlGlobals() {
new FakeJavaProtoCommon(),
new FakeJavaCcLinkParamsProvider.Provider());
PlatformBootstrap platformBootstrap = new PlatformBootstrap(new FakePlatformCommon());
PyBootstrap pyBootstrap = new PyBootstrap(new FakePyInfoProvider());
RepositoryBootstrap repositoryBootstrap = new RepositoryBootstrap(new FakeRepositoryModule());
TestingBootstrap testingBootstrap = new TestingBootstrap(new FakeTestingModule(),
new FakeAnalysisFailureInfoProvider(),
Expand All @@ -198,6 +201,7 @@ private Map<String, Object> collectBzlGlobals() {
configBootstrap.addBindingsToBuilder(envBuilder);
javaBootstrap.addBindingsToBuilder(envBuilder);
platformBootstrap.addBindingsToBuilder(envBuilder);
pyBootstrap.addBindingsToBuilder(envBuilder);
repositoryBootstrap.addBindingsToBuilder(envBuilder);
testingBootstrap.addBindingsToBuilder(envBuilder);

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/google/devtools/build/lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/apple",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/python",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
Expand Down Expand Up @@ -1119,11 +1120,13 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/concurrent",
"//src/main/java/com/google/devtools/build/lib/rules/cpp",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/python",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//src/main/java/com/google/devtools/common/options",
"//src/main/protobuf:crosstool_config_java_proto",
"//src/main/protobuf:extra_actions_base_java_proto",
"//third_party:guava",
"//third_party:jsr305",
"//third_party/protobuf:protobuf_java",
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@
import com.google.devtools.build.lib.rules.proto.ProtoConfiguration;
import com.google.devtools.build.lib.rules.proto.ProtoInfo;
import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainRule;
import com.google.devtools.build.lib.rules.python.PyInfo;
import com.google.devtools.build.lib.rules.python.PythonConfigurationLoader;
import com.google.devtools.build.lib.rules.repository.CoreWorkspaceRules;
import com.google.devtools.build.lib.rules.repository.NewLocalRepositoryRule;
import com.google.devtools.build.lib.rules.test.TestingSupportRules;
import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidBootstrap;
import com.google.devtools.build.lib.skylarkbuildapi.python.PyBootstrap;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.ResourceFileLoader;
import com.google.devtools.build.lib.vfs.PathFragment;
Expand Down Expand Up @@ -342,6 +344,8 @@ public void init(ConfiguredRuleClassProvider.Builder builder) {
builder.addRuleDefinition(new BazelPyBinaryRule());
builder.addRuleDefinition(new BazelPyTestRule());
builder.addRuleDefinition(new BazelPyRuntimeRule());

builder.addSkylarkBootstrap(new PyBootstrap(PyInfo.PROVIDER));
}

@Override
Expand Down
251 changes: 251 additions & 0 deletions src/main/java/com/google/devtools/build/lib/rules/python/PyInfo.java
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import com.google.devtools.build.lib.syntax.SkylarkType;

/** Static helper class for creating and accessing instances of the legacy "py" struct provider. */
// TODO(#7010): Replace this with a real provider.
// TODO(#7010): Remove this in favor of PyInfo.
public class PyStructUtils {

// Disable construction.
Expand Down Expand Up @@ -76,7 +76,7 @@ private PyStructUtils() {}
builder.put(USES_SHARED_LIBRARIES, false);
builder.put(
IMPORTS,
SkylarkNestedSet.of(String.class, NestedSetBuilder.<String>compileOrder().build()));
SkylarkNestedSet.of(String.class, NestedSetBuilder.<String>emptySet(Order.COMPILE_ORDER)));
builder.put(HAS_PY2_ONLY_SOURCES, false);
builder.put(HAS_PY3_ONLY_SOURCES, false);
DEFAULTS = builder.build();
Expand Down Expand Up @@ -110,7 +110,7 @@ public static NestedSet<Artifact> getTransitiveSources(StructImpl info) throws E
"'%s' provider's '%s' field should be a depset of Files (got a '%s')",
PROVIDER_NAME,
TRANSITIVE_SOURCES,
EvalUtils.getDataTypeNameFromClass(fieldValue.getClass()));
EvalUtils.getDataTypeName(fieldValue, /*fullDetails=*/ true));
NestedSet<Artifact> unwrappedValue = castValue.getSet(Artifact.class);
if (!unwrappedValue.getOrder().isCompatible(Order.COMPILE_ORDER)) {
throw new EvalException(
Expand All @@ -137,7 +137,7 @@ public static boolean getUsesSharedLibraries(StructImpl info) throws EvalExcepti
"'%s' provider's '%s' field should be a boolean (got a '%s')",
PROVIDER_NAME,
USES_SHARED_LIBRARIES,
EvalUtils.getDataTypeNameFromClass(fieldValue.getClass()));
EvalUtils.getDataTypeName(fieldValue, /*fullDetails=*/ true));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ filegroup(
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp:srcs",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java:srcs",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/platform:srcs",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/python:srcs",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/repository:srcs",
"//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/test:srcs",
],
Expand Down
Loading

0 comments on commit 3c67250

Please sign in to comment.