// 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();
}
}
}