Allow multiple setup hooks in packaging’s setup.cfg files (#12240).

Original patch by Erik Bray.
diff --git a/Lib/packaging/config.py b/Lib/packaging/config.py
index 3427d9a..21bbcf8 100644
--- a/Lib/packaging/config.py
+++ b/Lib/packaging/config.py
@@ -61,17 +61,15 @@
 
 
 class Config:
-    """Reads configuration files and work with the Distribution instance
-    """
+    """Class used to work with configuration files"""
     def __init__(self, dist):
         self.dist = dist
-        self.setup_hook = None
+        self.setup_hooks = []
 
-    def run_hook(self, config):
-        if self.setup_hook is None:
-            return
-        # the hook gets only the config
-        self.setup_hook(config)
+    def run_hooks(self, config):
+        """Run setup hooks in the order defined in the spec."""
+        for hook in self.setup_hooks:
+            hook(config)
 
     def find_config_files(self):
         """Find as many configuration files as should be processed for this
@@ -131,17 +129,20 @@
         for section in parser.sections():
             content[section] = dict(parser.items(section))
 
-        # global:setup_hook is called *first*
+        # global setup hooks are called first
         if 'global' in content:
-            if 'setup_hook' in content['global']:
-                setup_hook = content['global']['setup_hook']
-                try:
-                    self.setup_hook = resolve_name(setup_hook)
-                except ImportError as e:
-                    logger.warning('could not import setup_hook: %s',
-                            e.args[0])
-                else:
-                    self.run_hook(content)
+            if 'setup_hooks' in content['global']:
+                setup_hooks = split_multiline(content['global']['setup_hooks'])
+
+                for line in setup_hooks:
+                    try:
+                        hook = resolve_name(line)
+                    except ImportError as e:
+                        logger.warning('cannot find setup hook: %s', e.args[0])
+                    else:
+                        self.setup_hooks.append(hook)
+
+                self.run_hooks(content)
 
         metadata = self.dist.metadata
 
diff --git a/Lib/packaging/tests/test_config.py b/Lib/packaging/tests/test_config.py
index 1669862..6be63eb 100644
--- a/Lib/packaging/tests/test_config.py
+++ b/Lib/packaging/tests/test_config.py
@@ -90,7 +90,7 @@
 compilers =
     packaging.tests.test_config.DCompiler
 
-setup_hook = %(setup-hook)s
+setup_hooks = %(setup-hooks)s
 
 
 
@@ -135,8 +135,16 @@
         pass
 
 
-def hook(content):
-    content['metadata']['version'] += '.dev1'
+def version_hook(config):
+    config['metadata']['version'] += '.dev1'
+
+
+def first_hook(config):
+    config['files']['modules'] += '\n first'
+
+
+def third_hook(config):
+    config['files']['modules'] += '\n third'
 
 
 class FooBarBazTest:
@@ -186,7 +194,7 @@
 
     def write_setup(self, kwargs=None):
         opts = {'description-file': 'README', 'extra-files': '',
-                'setup-hook': 'packaging.tests.test_config.hook'}
+                'setup-hooks': 'packaging.tests.test_config.version_hook'}
         if kwargs:
             opts.update(kwargs)
         self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8')
@@ -318,13 +326,27 @@
         self.assertEqual(ext.extra_compile_args, cargs)
         self.assertEqual(ext.language, 'cxx')
 
-    def test_missing_setuphook_warns(self):
-        self.write_setup({'setup-hook': 'this.does._not.exist'})
+    def test_missing_setup_hook_warns(self):
+        self.write_setup({'setup-hooks': 'this.does._not.exist'})
         self.write_file('README', 'yeah')
         dist = self.get_dist()
         logs = self.get_logs(logging.WARNING)
         self.assertEqual(1, len(logs))
-        self.assertIn('could not import setup_hook', logs[0])
+        self.assertIn('cannot find setup hook', logs[0])
+
+    def test_multiple_setup_hooks(self):
+        self.write_setup({
+            'setup-hooks': '\n  packaging.tests.test_config.first_hook'
+                           '\n  packaging.tests.test_config.missing_hook'
+                           '\n  packaging.tests.test_config.third_hook'
+        })
+        self.write_file('README', 'yeah')
+        dist = self.get_dist()
+
+        self.assertEqual(['haven', 'first', 'third'], dist.py_modules)
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('cannot find setup hook', logs[0])
 
     def test_metadata_requires_description_files_missing(self):
         self.write_setup({'description-file': 'README README2'})
diff --git a/Lib/packaging/tests/test_util.py b/Lib/packaging/tests/test_util.py
index 68ad8eb..f657ab2 100644
--- a/Lib/packaging/tests/test_util.py
+++ b/Lib/packaging/tests/test_util.py
@@ -495,7 +495,7 @@
 
     def test_cfg_to_args(self):
         opts = {'description-file': 'README', 'extra-files': '',
-                'setup-hook': 'packaging.tests.test_config.hook'}
+                'setup-hooks': 'packaging.tests.test_config.version_hook'}
         self.write_file('setup.cfg', SETUP_CFG % opts)
         self.write_file('README', 'loooong description')