Automated rollback of commit fa0fac2a4e8a2e5c01b8390878289d00dcc17dba.

*** Reason for rollback ***

Remove example changes; those need to build with the last Bazel release.

*** Original change description ***

Automated rollback of commit 0f9c6ea574918dda094cf5423fa3822112846c30.

*** Reason for rollback ***

Breaks Kokoro and I accidentally submitted the change without presubmit checks.

*** Original change description ***

Make __init__.py files creation optional

Introduce a new attribute to py_binary and py_test to control whether to
create `__init__.py` or not.

Fixes https://github.com/bazelbuild/rules_python/issues/55

Closes #4470.

PiperOrigin-RevId: 185806241
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
index 77e7e42..43091e8 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
@@ -18,6 +18,7 @@
 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.BOOLEAN;
 import static com.google.devtools.build.lib.syntax.Type.STRING;
 import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
 
@@ -135,14 +136,14 @@
     @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(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.
@@ -157,11 +158,13 @@
           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.defaultTargetPythonVersion().toString())
-               .allowedValues(new AllowedValueSet(PythonVersion.getTargetPythonValues()))
-               .nonconfigurable("read by PythonUtils.getNewPythonVersion, which doesn't have access"
-                   + " to configuration keys"))
+          .add(
+              attr("default_python_version", STRING)
+                  .value(PythonVersion.defaultTargetPythonVersion().toString())
+                  .allowedValues(new AllowedValueSet(PythonVersion.getTargetPythonValues()))
+                  .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
@@ -170,11 +173,21 @@
           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))
+          .add(
+              attr("srcs", LABEL_LIST)
+                  .mandatory()
+                  .allowedFileTypes(PYTHON_SOURCE)
+                  .direct_compile_time_input()
+                  .allowedFileTypes(BazelPyRuleClasses.PYTHON_SOURCE))
+          /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(legacy_create_init) -->
+          Whether to implicitly create empty __init__.py files in the runfiles tree.
+          These are created in every directory containing Python source code or
+          shared libraries, and every parent directory of those directories.
+          Default is true for backward compatibility.  If false, the user is responsible
+          for creating __init__.py files (empty or not) and adding them to `srcs` or `deps`
+          of Python targets as required.
+          <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+          .add(attr("legacy_create_init", BOOLEAN).value(true))
           /* <!-- #BLAZE_RULE($base_py_binary).ATTRIBUTE(stamp) -->
           Enable link stamping.
           Whether to encode build information into the binary. Possible values:
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java
index e070692..b2ef88d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java
@@ -27,6 +27,7 @@
 import com.google.devtools.build.lib.rules.cpp.CcLinkParams;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsInfo;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsStore;
+import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.ArrayList;
 import java.util.List;
@@ -140,7 +141,11 @@
     }
     semantics.collectDefaultRunfiles(ruleContext, builder);
     builder.add(ruleContext, PythonRunfilesProvider.TO_RUNFILES);
-    builder.setEmptyFilesSupplier(PythonUtils.GET_INIT_PY_FILES);
+
+    if (!ruleContext.attributes().has("legacy_create_init", Type.BOOLEAN)
+        || ruleContext.attributes().get("legacy_create_init", Type.BOOLEAN)) {
+      builder.setEmptyFilesSupplier(PythonUtils.GET_INIT_PY_FILES);
+    }
     semantics.collectRunfilesForBinary(ruleContext, builder, common);
     return builder.build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java
index adc0cca..f41d99b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java
@@ -83,7 +83,6 @@
     } else {
       runfilesBuilder.addTransitiveArtifacts(filesToBuild);
     }
-    runfilesBuilder.setEmptyFilesSupplier(PythonUtils.GET_INIT_PY_FILES);
     runfilesBuilder.add(ruleContext, PythonRunfilesProvider.TO_RUNFILES);
     runfilesBuilder.addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES);
 
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
index 61dd892..2a86f2f 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
@@ -1449,9 +1449,9 @@
     scratch.file(
         "test/BUILD",
         "load('//test:rule.bzl', 'skylark_rule')",
-        "py_library(name = 'lib', srcs = ['a.py', 'b.py'])",
+        "py_binary(name = 'lib', srcs = ['lib.py', 'lib2.py'])",
         "skylark_rule(name = 'foo', dep = ':lib')",
-        "py_library(name = 'lib_with_init', srcs = ['a.py', 'b.py', '__init__.py'])",
+        "py_binary(name = 'lib_with_init', srcs = ['lib_with_init.py', 'lib2.py', '__init__.py'])",
         "skylark_rule(name = 'foo_with_init', dep = ':lib_with_init')");
 
     SkylarkRuleContext ruleContext = createRuleContext("//test:foo");
@@ -1460,13 +1460,13 @@
             ruleContext, "[f.short_path for f in ruleContext.attr.dep.default_runfiles.files]");
     assertThat(filenames).isInstanceOf(SkylarkList.class);
     SkylarkList filenamesList = (SkylarkList) filenames;
-    assertThat(filenamesList).containsExactly("test/a.py", "test/b.py").inOrder();
+    assertThat(filenamesList).containsAllOf("test/lib.py", "test/lib2.py");
     Object emptyFilenames =
         evalRuleContextCode(
             ruleContext, "list(ruleContext.attr.dep.default_runfiles.empty_filenames)");
     assertThat(emptyFilenames).isInstanceOf(SkylarkList.class);
     SkylarkList emptyFilenamesList = (SkylarkList) emptyFilenames;
-    assertThat(emptyFilenamesList).containsExactly("test/__init__.py").inOrder();
+    assertThat(emptyFilenamesList).containsExactly("test/__init__.py");
 
     SkylarkRuleContext ruleWithInitContext = createRuleContext("//test:foo_with_init");
     Object noEmptyFilenames =
diff --git a/src/test/py/bazel/py_test.py b/src/test/py/bazel/py_test.py
index 26fe88a..3d97e79 100644
--- a/src/test/py/bazel/py_test.py
+++ b/src/test/py/bazel/py_test.py
@@ -65,5 +65,46 @@
                     .endswith('/a/b.py'))
 
 
+class TestInitPyFiles(test_base.TestBase):
+
+  def createSimpleFiles(self, create_init=True):
+    self.ScratchFile('WORKSPACE')
+
+    self.ScratchFile('src/a/BUILD', [
+        'py_binary(name="a", srcs=["a.py"], deps=[":b"], legacy_create_init=%s)'
+        % create_init,
+        'py_library(name="b", srcs=["b.py"])',
+    ])
+
+    self.ScratchFile('src/a/a.py', [
+        'from src.a import b',
+        'b.Hello()',
+    ])
+
+    self.ScratchFile('src/a/b.py', [
+        'def Hello():',
+        '    print("Hello, World")',
+    ])
+
+  def testInitPyFilesCreated(self):
+    self.createSimpleFiles()
+    exit_code, _, stderr = self.RunBazel(['build', '//src/a:a'])
+    self.AssertExitCode(exit_code, 0, stderr)
+    self.assertTrue(
+        os.path.exists('bazel-bin/src/a/a.runfiles/__main__/src/__init__.py'))
+    self.assertTrue(
+        os.path.exists('bazel-bin/src/a/a.runfiles/__main__/src/a/__init__.py'))
+
+
+def testInitPyFilesNotCreatedWhenLegacyCreateInitIsSet(self):
+  self.createSimpleFiles(create_init=False)
+  exit_code, _, stderr = self.RunBazel(['build', '//src/a:a'])
+  self.AssertExitCode(exit_code, 0, stderr)
+  self.assertFalse(
+      os.path.exists('bazel-bin/src/a/a.runfiles/__main__/src/__init__.py'))
+  self.assertFalse(
+      os.path.exists('bazel-bin/src/a/a.runfiles/__main__/src/a/__init__.py'))
+
+
 if __name__ == '__main__':
   unittest.main()