Repository: pypa/setuptools

Issue:
[BUG] setuptools.build_meta proceeds with no setup.py or setup.cfg
### setuptools version

setuptools==58.1.0

### Python version

Python 3.9

### OS

Linux

### Additional environment information

_No response_

### Description

Running `pip install .` in a directory that contains nothing but a `pyproject.toml` with these contents:
```toml
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
```
successfully installs a distribution containing no modules or packages and no useful metadata:
```console
$ cat /tmp/39/lib/python3.9/site-packages/UNKNOWN-0.0.0.dist-info/METADATA
Metadata-Version: 2.1
Name: UNKNOWN
Version: 0.0.0
Summary: UNKNOWN
Home-page: UNKNOWN
License: UNKNOWN
Platform: UNKNOWN

UNKNOWN
```


### Expected behavior

I would expect the build backend to fail, due to it lacking any useful configuration, rather than succeeding in building a package that is empty except for placeholder metadata.

This may be a duplicate of #2329, but that one was specifically scoped to `build_meta:__legacy__`, and all of the discussion was about the legacy backend. In this case, you can see that the same issue occurs even with the PEP 517 backend.

### How to Reproduce

See description

### Output

See description

### Code of Conduct

- [X] I agree to follow the PSF Code of Conduct


Discussion:

Resolving patch:
diff --git a/setuptools/dist.py b/setuptools/dist.py
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -472,6 +472,19 @@ def __init__(self, attrs=None):
         )
         self._finalize_requires()
 
+    def _validate_metadata(self):
+        required = {"name"}
+        provided = {
+            key
+            for key in vars(self.metadata)
+            if getattr(self.metadata, key, None) is not None
+        }
+        missing = required - provided
+
+        if missing:
+            msg = f"Required package metadata is missing: {missing}"
+            raise DistutilsSetupError(msg)
+
     def _set_metadata_defaults(self, attrs):
         """
         Fill-in missing metadata fields not supported by distutils.


Test patch:
diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py
--- a/setuptools/tests/test_bdist_egg.py
+++ b/setuptools/tests/test_bdist_egg.py
@@ -13,7 +13,7 @@
 SETUP_PY = """\
 from setuptools import setup
 
-setup(name='foo', py_modules=['hi'])
+setup(py_modules=['hi'])
 """
 
 
@@ -52,7 +52,6 @@ def test_exclude_source_files(self, setup_context, user_override):
         dist = Distribution(dict(
             script_name='setup.py',
             script_args=['bdist_egg', '--exclude-source-files'],
-            name='foo',
             py_modules=['hi'],
         ))
         with contexts.quiet():
diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py
--- a/setuptools/tests/test_build_py.py
+++ b/setuptools/tests/test_build_py.py
@@ -18,7 +18,6 @@ def test_directories_in_package_data_glob(tmpdir_cwd):
         script_name='setup.py',
         script_args=['build_py'],
         packages=[''],
-        name='foo',
         package_data={'': ['path/*']},
     ))
     os.makedirs('path/subpath')
@@ -40,7 +39,6 @@ def test_read_only(tmpdir_cwd):
         script_args=['build_py'],
         packages=['pkg'],
         package_data={'pkg': ['data.dat']},
-        name='pkg',
     ))
     os.makedirs('pkg')
     open('pkg/__init__.py', 'w').close()
@@ -70,7 +68,6 @@ def test_executable_data(tmpdir_cwd):
         script_args=['build_py'],
         packages=['pkg'],
         package_data={'pkg': ['run-me']},
-        name='pkg',
     ))
     os.makedirs('pkg')
     open('pkg/__init__.py', 'w').close()
diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py
--- a/setuptools/tests/test_dist.py
+++ b/setuptools/tests/test_dist.py
@@ -374,3 +374,8 @@ def test_check_specifier():
 )
 def test_rfc822_unescape(content, result):
     assert (result or content) == rfc822_unescape(rfc822_escape(content))
+
+
+def test_metadata_name():
+    with pytest.raises(DistutilsSetupError, match='missing.*name'):
+        Distribution()._validate_metadata()
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -64,7 +64,7 @@ def as_requirement(self):
 SETUP_PY = DALS("""
     from setuptools import setup
 
-    setup(name='foo')
+    setup()
     """)
 
 
diff --git a/setuptools/tests/test_sphinx_upload_docs.py b/setuptools/tests/test_sphinx_upload_docs.py
--- a/setuptools/tests/test_sphinx_upload_docs.py
+++ b/setuptools/tests/test_sphinx_upload_docs.py
@@ -25,7 +25,6 @@ def sphinx_doc_sample_project(tmpdir_cwd):
 class TestSphinxUploadDocs:
     def test_sphinx_doc(self):
         params = dict(
-            name='foo',
             packages=['test'],
         )
         dist = Distribution(params)
diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py
--- a/setuptools/tests/test_test.py
+++ b/setuptools/tests/test_test.py
@@ -10,7 +10,6 @@
 @pytest.mark.usefixtures('tmpdir_cwd')
 def test_tests_are_run_once(capfd):
     params = dict(
-        name='foo',
         packages=['dummy'],
     )
     files = {
diff --git a/setuptools/tests/test_upload_docs.py b/setuptools/tests/test_upload_docs.py
--- a/setuptools/tests/test_upload_docs.py
+++ b/setuptools/tests/test_upload_docs.py
@@ -18,7 +18,7 @@ def sample_project(tmpdir_cwd):
         'setup.py': DALS("""
             from setuptools import setup
 
-            setup(name='foo')
+            setup()
             """),
         'build': {
             'index.html': 'Hello world.',