// Copyright 2014 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.bazel.rules.python; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.BuildType.LABEL; import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static com.google.devtools.build.lib.packages.BuildType.TRISTATE; import static com.google.devtools.build.lib.syntax.Type.STRING; import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.python.PyCommon; import com.google.devtools.build.lib.rules.python.PyRuleClasses; import com.google.devtools.build.lib.rules.python.PythonVersion; import com.google.devtools.build.lib.util.FileType; /** * Bazel-specific rule definitions for Python rules. */ public final class BazelPyRuleClasses { public static final FileType PYTHON_SOURCE = FileType.of(".py"); /** * Base class for Python rule definitions. */ public static final class PyBaseRule implements RuleDefinition { @Override public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { return builder /* <!-- #BLAZE_RULE($base_py).ATTRIBUTE(deps) --> The list of other libraries to be linked in to the binary target. See general comments about <code>deps</code> at <a href="${link common-definitions#common-attributes}"> Attributes common to all build rules</a>. These can be <a href="${link py_binary}"><code>py_binary</code></a> rules, <a href="${link py_library}"><code>py_library</code></a> rules or <a href="${link cc_library}"><code>cc_library</code></a> rules, <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .override(builder.copy("deps") .legacyMandatoryProviders(PyCommon.PYTHON_SKYLARK_PROVIDER_NAME) .allowedFileTypes()) /* <!-- #BLAZE_RULE($base_py).ATTRIBUTE(imports) --> List of import directories to be added to the <code>PYTHONPATH</code>. <p> Subject to <a href="${link make-variables}">"Make variable"</a> substitution. These import directories will be added for this rule and all rules that depend on it (note: not the rules this rule depends on. Each directory will be added to <code>PYTHONPATH</code> by <a href="${link py_binary}"><code>py_binary</code></a> rules that depend on this rule. </p> <p> Absolute paths (paths that start with <code>/</code>) and paths that references a path above the execution root are not allowed and will result in an error. </p> <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .add(attr("imports", STRING_LIST).value(ImmutableList.<String>of())) /* <!-- #BLAZE_RULE($base_py).ATTRIBUTE(srcs_version) --> A string specifying the Python major version(s) that the <code>.py</code> source files listed in the <code>srcs</code> of this rule are compatible with. Valid values are:<br/> <code>"PY2ONLY"</code> - Python 2 code that is <b>not</b> suitable for <code>2to3</code> conversion.<br/> <code>"PY2"</code> - Python 2 code that is expected to work when run through <code>2to3</code>.<br/> <code>"PY2AND3"</code> - Code that is compatible with both Python 2 and 3 without <code>2to3</code> conversion.<br/> <code>"PY3"</code> - Python 3 code that will not run on Python 2.<br/> <br/> <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .add(attr("srcs_version", STRING) .value(PythonVersion.defaultValue().toString())) // do not depend on lib2to3:2to3 rule, because it creates circular dependencies // 2to3 is itself written in Python and depends on many libraries. .add(attr("$python2to3", LABEL).cfg(HOST).exec() .value(env.getToolsLabel("//tools/python:2to3"))) .setPreferredDependencyPredicate(PyRuleClasses.PYTHON_SOURCE) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$base_py") .type(RuleClassType.ABSTRACT) .ancestors(BaseRuleClasses.RuleBase.class) .build(); } } /** * Base class for Python rule definitions that produce binaries. */ public static final class PyBinaryBaseRule implements RuleDefinition { @Override public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironment env) { return builder /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(data) --> The list of files needed by this binary at runtime. See general comments about <code>data</code> at <a href="${link common-definitions#common-attributes}"> Attributes common to all build rules</a>. Also see the <a href="${link py_library.data}"><code>data</code></a> argument of the <a href="${link py_library}"><code>py_library</code></a> rule for details. <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(main) --> The name of the source file that is the main entry point of the application. This file must also be listed in <code>srcs</code>. If left unspecified, <code>name</code> is used instead (see above). If <code>name</code> does not match any filename in <code>srcs</code>, <code>main</code> must be specified. <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .add(attr("main", LABEL).allowedFileTypes(PYTHON_SOURCE)) /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(default_python_version) --> A string specifying the default Python major version to use when building this binary and all of its <code>deps</code>. Valid values are <code>"PY2"</code> (default) or <code>"PY3"</code>. Python 3 support is experimental. <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .add(attr("default_python_version", STRING) .value(PythonVersion.defaultValue().toString()) .nonconfigurable("read by PythonUtils.getNewPythonVersion, which doesn't have access" + " to configuration keys")) /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(srcs) --> The list of source files that are processed to create the target. This includes all your checked-in code and any generated source files. The line between <code>srcs</code> and <code>deps</code> is loose. The <code>.py</code> files probably belong in <code>srcs</code> and library targets probably belong in <code>deps</code>, but don't worry about it too much. <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .add(attr("srcs", LABEL_LIST) .mandatory() .allowedFileTypes(PYTHON_SOURCE) .direct_compile_time_input() .allowedFileTypes(BazelPyRuleClasses.PYTHON_SOURCE)) /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(stamp) --> Enable link stamping. Whether to encode build information into the binary. Possible values: <ul> <li><code>stamp = 1</code>: Stamp the build information into the binary. Stamped binaries are only rebuilt when their dependencies change. Use this if there are tests that depend on the build information.</li> <li><code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.</li> <li><code>stamp = -1</code>: Embedding of build information is controlled by the <a href="../blaze-user-manual.html#flag--stamp">--[no]stamp</a> Blaze flag.</li> </ul> <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ .add(attr("stamp", TRISTATE).value(TriState.AUTO)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$base_py_binary") .type(RuleClassType.ABSTRACT) .ancestors(PyBaseRule.class, BazelCppRuleClasses.CcLinkingRule.class) .build(); } } }