Document declared providers. Change-Id: Ifdc1773fb1439c16327bb84961998793a942e666 PiperOrigin-RevId: 154151084
diff --git a/site/designs/index.md b/site/designs/index.md index 67656da..6792ee2 100644 --- a/site/designs/index.md +++ b/site/designs/index.md
@@ -18,6 +18,10 @@ Always go to the [documentation](/versions/master/docs/bazel-overview.md) for descriptions of current Bazel functionality. +## Bazel Code Style and Best Practices + +* [Implementing Native Declared Provders](https://docs.google.com/document/d/1BKCBuYvf6fwmMnFu3KMWdRFysDgGCzdMiNxY_foosAM/preview) 12 April 2017 + ## Bazel Design Documents <ul>
diff --git a/site/versions/master/docs/skylark/cookbook.md b/site/versions/master/docs/skylark/cookbook.md index 4e17364..b71ce51 100644 --- a/site/versions/master/docs/skylark/cookbook.md +++ b/site/versions/master/docs/skylark/cookbook.md
@@ -503,7 +503,7 @@ content=command, executable=True) - return struct( + return [DefaultInfo( # Create runfiles from the files specified in the data attribute. # The shell executable - the output of this rule - can use them at runtime. # It is also possible to define data_runfiles and default_runfiles. @@ -513,7 +513,7 @@ # to have a field named "runfiles" in order to create the actual runfiles # symlink tree. runfiles=ctx.runfiles(files=ctx.files.data) - ) + )] execute = rule( implementation=_impl, @@ -661,14 +661,16 @@ `sum.bzl`: ```python +NumberInfo = provider() + def _impl(ctx): result = ctx.attr.number - for i in ctx.attr.deps: - result += i.number + for dep in ctx.attr.deps: + result += dep[NumberInfo].number ctx.file_action(output=ctx.outputs.out, content=str(result)) - # Fields in the struct will be visible by other rules. - return struct(number=result) + # Return the provider with result, visible to other rules. + return [NumberInfo(number=result)] sum = rule( implementation=_impl, @@ -709,15 +711,17 @@ `sum.bzl`: ```python +NumberInfo = provider() + def _impl(ctx): result = ctx.attr.number - for i in ctx.attr.deps: - if hasattr(i, "number"): - result += i.number + for dep in ctx.attr.deps: + if NumberInfo in dep: + result += dep[NumberInfo].number ctx.file_action(output=ctx.outputs.out, content=str(result)) - # Fields in the struct will be visible by other rules. - return struct(number=result) + # Return the provider with result, visible to other rules. + return [NumberInfo(number=result)] sum = rule( implementation=_impl, @@ -864,7 +868,7 @@ files = depset() files += ctx.attr.dep_rule_1.files files += ctx.attr.dep_rule_2.files - return struct(files=files) + return [DefaultInfo(files=files)] # This rule binds the depending rules together master_rule = rule(
diff --git a/site/versions/master/docs/skylark/rules.md b/site/versions/master/docs/skylark/rules.md index 5db62cf..56ef328 100644 --- a/site/versions/master/docs/skylark/rules.md +++ b/site/versions/master/docs/skylark/rules.md
@@ -114,11 +114,7 @@ ```python def _impl(ctx): ... - return struct( - runfiles = ..., - my_provider = ..., - ... - ) + return [DefaultInfo(runfiles=...), MyInfo(...)] my_rule = rule( implementation = _impl, @@ -350,46 +346,72 @@ using [depsets](depsets.md) to hold the data more efficiently without excessive copying. -The following data types can be passed using providers: +Providers can be declared using the [provider()](lib/globals.html#provider) function: -* [bool](lib/bool.html) -* [integer](lib/int.html) -* [string](lib/string.html) -* [file](lib/File.html) -* [label](lib/Label.html) -* [None](lib/globals.html#None) -* anything composed of these types and [lists](lib/list.html), - [dicts](lib/dict.html), [depsets](lib/depset.html) or - [structs](lib/struct.html). +```python +TransitiveDataInfo = provider() +``` -Providers are created from the return value of the rule implementation function: +Rule implementation function can then construct and return provider instances: ```python def rule_implementation(ctx): ... - return struct( - transitive_data = depset(["a", "b", "c"]) - ) + return [TransitiveDataInfo(value = ["a", "b", "c"])] ``` -A dependent rule might access these data as struct fields of the `target` being -depended upon: +`TransitiveDataInfo` acts both as a constructor for provider instances and as a key to access them. +A [target](lib/Target.html) serves as a map from each provider that the target supports, to the +target's corresponding instance of that provider. +A rule can access the providers of its dependencies using the square bracket notation (`[]`): ```python def dependent_rule_implementation(ctx): ... s = depset() for dep_target in ctx.attr.deps: - # Use `print(dir(dep_target))` to see the list of providers. - s += dep_target.transitive_data + s += dep_target[TransitiveDataInfo].value ... ``` +All targets have a [`DefaultInfo`](lib/globals.html#DefaultInfo) provider that can be used to access +some information relevant to all targets. + Providers are only available during the analysis phase. Examples of usage: * [mandatory providers](cookbook.md#mandatory-providers) * [optional providers](cookbook.md#optional-providers) +> *Note:* +> Historically, Bazel also supported provider instances that are identified by strings and +> accessed as fields on the `target` object instead of as keys. This style is deprecated +> but still supported. Return legacy providers as follows: +> +```python +def rule_implementation(ctx): + ... + modern_provider = TransitiveDataInfo(value = ["a", "b", "c"]) + # Legacy style. + return struct(legacy_provider = struct(...), + another_legacy_provider = struct(...), + # The `providers` field contains provider instances that can be accessed + # the "modern" way. + providers = [modern_provider]) +``` +> To access legacy providers, use the dot notation. +> Note that the same target can define both modern and legacy providers: +> +```python +def dependent_rule_implementation(ctx): + ... + s = depset() + for dep_target in ctx.attr.deps: + x = dep_target.legacy_provider # legacy style + s += dep_target[TransitiveDataInfo].value # modern style + ... +``` +> **We recommend using modern providers for all future code.** + ## Runfiles Runfiles are a set of files used by the (often executable) output of a rule @@ -418,9 +440,9 @@ # deps and data attributes. collect_default = True, ) - # Add a field named "runfiles" to the return struct in order to actually + # Add a field named "runfiles" to the DefaultInfo provider in order to actually # create the symlink tree. - return struct(runfiles = runfiles) + return [DefaultInfo(runfiles=runfiles)] ``` Note that non-executable rule outputs can also have runfiles. For example, a
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java b/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java index e2e0ccf..8275e6a 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java +++ b/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java
@@ -27,19 +27,37 @@ /** * Declared Provider (a constructor for {@link SkylarkClassObject}). * - * Declared providers can be declared either natively ({@link NativeClassObjectConstructor} - * or in Skylark {@link SkylarkClassObjectConstructor}. + * <p>Declared providers can be declared either natively ({@link NativeClassObjectConstructor} or in + * Skylark {@link SkylarkClassObjectConstructor}. * - * {@link ClassObjectConstructor} serves both as "type identifier" for declared provider + * <p>{@link ClassObjectConstructor} serves both as "type identifier" for declared provider * instances and as a function that can be called to construct a provider. * - * Prefer to use {@link Key} as a serializable identifier of {@link ClassObjectConstructor}. - * In particular, {@link Key} should be used in all data structures exposed to Skyframe. + * <p>Prefer to use {@link Key} as a serializable identifier of {@link ClassObjectConstructor}. In + * particular, {@link Key} should be used in all data structures exposed to Skyframe. */ -@SkylarkModule(name = "provider", - doc = "A constructor for simple value objects. " - + "See the global <a href=\"globals.html#provider\">provider</a> function " - + "for more details." +@SkylarkModule( + name = "Provider", + doc = + "A constructor for simple value objects, known as provider instances." + + "<br>" + + "This value has a dual purpose:" + + " <ul>" + + " <li>It is a function that can be called to construct 'struct'-like values:" + + "<pre class=\"language-python\">DataInfo = provider()\n" + + "d = DataInfo(x = 2, y = 3)\n" + + "print(d.x + d.y) # prints 5</pre>" + + " <i>Some providers, defined internally, do not allow instance creation</i>" + + " </li>" + + " <li>It is a <i>key</i> to access a provider instance on a" + + " <a href=\"lib/Target.html\">Target</a>" + + "<pre class=\"language-python\">DataInfo = provider()\n" + + "def _rule_impl(ctx)\n" + + " ... ctx.attr.dep[DataInfo]</pre>" + + " </li>" + + " </ul>" + + "Create a new <code>Provider</code> using the " + + "<a href=\"globals.html#provider\">provider</a> function." ) @Immutable public abstract class ClassObjectConstructor extends BaseFunction {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java index 24803c1..aefdfaf 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -194,16 +194,18 @@ @SkylarkSignature( name = "DefaultInfo", returnType = ClassObjectConstructor.class, - doc = "A provider that is provided by every rule, even if it iss not returned explicitly. " - + "A <code>DefaultInfo</code> accepts all special parameters that can be returned " - + "from rule implementation function in a struct, which are <code>runfiles</code>, " - + "<code>data_runfiles</code>, <code>default_runfiles</code>, " - + "<code>output_groups</code>, <code>instrumented_files</code>, and all " - + "<a href=\"skylark-provider.html\">providers</a> that are available on built-in rules. " - + "Each instance of the default provider contains the following standard fields: " - + "<code>data_runfiles</code>, <code>default_runfiles</code>, <code>files</code>, " - + "and <code>files_to_run</code>. The values of these fields are equivalent to the " - + "values of the corresponding fields of the target the default provider belongs to." + doc = + "A provider that is provided by every rule, even if it iss not returned explicitly. " + + "A <code>DefaultInfo</code> accepts all special parameters that can be returned " + + "from rule implementation function in a struct, which are <code>runfiles</code>, " + + "<code>data_runfiles</code>, <code>default_runfiles</code>, <code>files</code>, " + + "<code>output_groups</code>, <code>instrumented_files</code>, and all " + + "<a href=\"skylark-provider.html\">providers</a> that are available on built-in " + + "rules. Each instance of the default provider contains the following standard " + + "fields: <code>data_runfiles</code>, <code>default_runfiles</code>, " + + "<code>files</code>, and <code>files_to_run</code>. The values of these fields " + + "are equivalent to the values of the corresponding fields of the target " + + "the default provider belongs to." ) private static final ClassObjectConstructor defaultInfo = DefaultProvider.SKYLARK_CONSTRUCTOR; @@ -226,7 +228,7 @@ ) private static final ClassObjectConstructor actions = ActionsProvider.SKYLARK_CONSTRUCTOR; - @SkylarkSignature(name = "provider", returnType = SkylarkClassObjectConstructor.class, doc = + @SkylarkSignature(name = "provider", returnType = ClassObjectConstructor.class, doc = "Creates a declared provider 'constructor'. The return value of this" + "function can be used to create \"struct-like\" values. Example:<br>" + "<pre class=\"language-python\">data = provider()\n" @@ -236,7 +238,7 @@ ) private static final BuiltinFunction provider = new BuiltinFunction("provider") { - public SkylarkClassObjectConstructor invoke(Location location) { + public ClassObjectConstructor invoke(Location location) { return new SkylarkClassObjectConstructor( "<no name>", // name is set on export. location);