aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst13
-rw-r--r--bootstrap.py16
-rw-r--r--pkg_resources/tests/test_markers.py5
-rwxr-xr-xsetup.cfg2
-rwxr-xr-xsetup.py4
-rw-r--r--setuptools/command/__init__.py2
-rw-r--r--setuptools/command/build_clib.py98
-rw-r--r--setuptools/dep_util.py23
-rw-r--r--setuptools/tests/test_build_clib.py59
-rw-r--r--setuptools/tests/test_dep_util.py30
-rw-r--r--setuptools/tests/test_easy_install.py5
-rw-r--r--setuptools/tests/test_msvc.py5
12 files changed, 242 insertions, 20 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 3fa2b76b..4058e23b 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,16 @@
+v34.1.0
+-------
+
+* #930: ``build_info`` now accepts two new parameters
+ to optimize and customize the building of C libraries.
+
+v34.0.3
+-------
+
+* #947: Loosen restriction on the version of six required,
+ restoring compatibility with environments relying on
+ six 1.6.0 and later.
+
v34.0.2
-------
diff --git a/bootstrap.py b/bootstrap.py
index f7644f12..ee3b53c8 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -17,7 +17,6 @@ import sys
import textwrap
import subprocess
-import pip
minimal_egg_info = textwrap.dedent("""
[distutils.commands]
@@ -75,7 +74,7 @@ def gen_deps():
@contextlib.contextmanager
def install_deps():
"Just in time make the deps available"
- gen_deps()
+ import pip
tmpdir = tempfile.mkdtemp()
args = [
'install',
@@ -90,7 +89,16 @@ def install_deps():
shutil.rmtree(tmpdir)
-if __name__ == '__main__':
+def main():
ensure_egg_info()
- with install_deps():
+ gen_deps()
+ try:
+ # first assume dependencies are present
run_egg_info()
+ except Exception:
+ # but if that fails, try again with dependencies just in time
+ with install_deps():
+ run_egg_info()
+
+
+__name__ == '__main__' and main()
diff --git a/pkg_resources/tests/test_markers.py b/pkg_resources/tests/test_markers.py
index 78810b6e..9306d5b3 100644
--- a/pkg_resources/tests/test_markers.py
+++ b/pkg_resources/tests/test_markers.py
@@ -1,7 +1,4 @@
-try:
- import unittest.mock as mock
-except ImportError:
- import mock
+from unittest import mock
from pkg_resources import evaluate_marker
diff --git a/setup.cfg b/setup.cfg
index 928c37b5..be162bbf 100755
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 34.0.2
+current_version = 34.1.0
commit = True
tag = True
diff --git a/setup.py b/setup.py
index 41a8df3b..88795a23 100755
--- a/setup.py
+++ b/setup.py
@@ -88,7 +88,7 @@ def pypi_link(pkg_filename):
setup_params = dict(
name="setuptools",
- version="34.0.2",
+ version="34.1.0",
description="Easily download, build, install, upgrade, and uninstall "
"Python packages",
author="Python Packaging Authority",
@@ -164,7 +164,7 @@ setup_params = dict(
python_requires='>=2.6,!=3.0.*,!=3.1.*,!=3.2.*',
install_requires=[
'packaging>=16.8',
- 'six>=1.10.0',
+ 'six>=1.6.0',
'appdirs>=1.4.0',
],
extras_require={
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index efbe9411..c96d33c2 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -2,7 +2,7 @@ __all__ = [
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
- 'register', 'bdist_wininst', 'upload_docs', 'upload',
+ 'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib',
]
from distutils.command.bdist import bdist
diff --git a/setuptools/command/build_clib.py b/setuptools/command/build_clib.py
new file mode 100644
index 00000000..09caff6f
--- /dev/null
+++ b/setuptools/command/build_clib.py
@@ -0,0 +1,98 @@
+import distutils.command.build_clib as orig
+from distutils.errors import DistutilsSetupError
+from distutils import log
+from setuptools.dep_util import newer_pairwise_group
+
+
+class build_clib(orig.build_clib):
+ """
+ Override the default build_clib behaviour to do the following:
+
+ 1. Implement a rudimentary timestamp-based dependency system
+ so 'compile()' doesn't run every time.
+ 2. Add more keys to the 'build_info' dictionary:
+ * obj_deps - specify dependencies for each object compiled.
+ this should be a dictionary mapping a key
+ with the source filename to a list of
+ dependencies. Use an empty string for global
+ dependencies.
+ * cflags - specify a list of additional flags to pass to
+ the compiler.
+ """
+
+ def build_libraries(self, libraries):
+ for (lib_name, build_info) in libraries:
+ sources = build_info.get('sources')
+ if sources is None or not isinstance(sources, (list, tuple)):
+ raise DistutilsSetupError(
+ "in 'libraries' option (library '%s'), "
+ "'sources' must be present and must be "
+ "a list of source filenames" % lib_name)
+ sources = list(sources)
+
+ log.info("building '%s' library", lib_name)
+
+ # Make sure everything is the correct type.
+ # obj_deps should be a dictionary of keys as sources
+ # and a list/tuple of files that are its dependencies.
+ obj_deps = build_info.get('obj_deps', dict())
+ if not isinstance(obj_deps, dict):
+ raise DistutilsSetupError(
+ "in 'libraries' option (library '%s'), "
+ "'obj_deps' must be a dictionary of "
+ "type 'source: list'" % lib_name)
+ dependencies = []
+
+ # Get the global dependencies that are specified by the '' key.
+ # These will go into every source's dependency list.
+ global_deps = obj_deps.get('', list())
+ if not isinstance(global_deps, (list, tuple)):
+ raise DistutilsSetupError(
+ "in 'libraries' option (library '%s'), "
+ "'obj_deps' must be a dictionary of "
+ "type 'source: list'" % lib_name)
+
+ # Build the list to be used by newer_pairwise_group
+ # each source will be auto-added to its dependencies.
+ for source in sources:
+ src_deps = [source]
+ src_deps.extend(global_deps)
+ extra_deps = obj_deps.get(source, list())
+ if not isinstance(extra_deps, (list, tuple)):
+ raise DistutilsSetupError(
+ "in 'libraries' option (library '%s'), "
+ "'obj_deps' must be a dictionary of "
+ "type 'source: list'" % lib_name)
+ src_deps.extend(extra_deps)
+ dependencies.append(src_deps)
+
+ expected_objects = self.compiler.object_filenames(
+ sources,
+ output_dir=self.build_temp
+ )
+
+ if newer_pairwise_group(dependencies, expected_objects) != ([], []):
+ # First, compile the source code to object files in the library
+ # directory. (This should probably change to putting object
+ # files in a temporary build directory.)
+ macros = build_info.get('macros')
+ include_dirs = build_info.get('include_dirs')
+ cflags = build_info.get('cflags')
+ objects = self.compiler.compile(
+ sources,
+ output_dir=self.build_temp,
+ macros=macros,
+ include_dirs=include_dirs,
+ extra_postargs=cflags,
+ debug=self.debug
+ )
+
+ # Now "link" the object files together into a static library.
+ # (On Unix at least, this isn't really linking -- it just
+ # builds an archive. Whatever.)
+ self.compiler.create_static_lib(
+ expected_objects,
+ lib_name,
+ output_dir=self.build_clib,
+ debug=self.debug
+ )
diff --git a/setuptools/dep_util.py b/setuptools/dep_util.py
new file mode 100644
index 00000000..2931c13e
--- /dev/null
+++ b/setuptools/dep_util.py
@@ -0,0 +1,23 @@
+from distutils.dep_util import newer_group
+
+# yes, this is was almost entirely copy-pasted from
+# 'newer_pairwise()', this is just another convenience
+# function.
+def newer_pairwise_group(sources_groups, targets):
+ """Walk both arguments in parallel, testing if each source group is newer
+ than its corresponding target. Returns a pair of lists (sources_groups,
+ targets) where sources is newer than target, according to the semantics
+ of 'newer_group()'.
+ """
+ if len(sources_groups) != len(targets):
+ raise ValueError("'sources_group' and 'targets' must be the same length")
+
+ # build a pair of lists (sources_groups, targets) where source is newer
+ n_sources = []
+ n_targets = []
+ for i in range(len(sources_groups)):
+ if newer_group(sources_groups[i], targets[i]):
+ n_sources.append(sources_groups[i])
+ n_targets.append(targets[i])
+
+ return n_sources, n_targets
diff --git a/setuptools/tests/test_build_clib.py b/setuptools/tests/test_build_clib.py
new file mode 100644
index 00000000..7e3d1de9
--- /dev/null
+++ b/setuptools/tests/test_build_clib.py
@@ -0,0 +1,59 @@
+import pytest
+import os
+import shutil
+
+from unittest import mock
+from distutils.errors import DistutilsSetupError
+from setuptools.command.build_clib import build_clib
+from setuptools.dist import Distribution
+
+
+class TestBuildCLib:
+ @mock.patch(
+ 'setuptools.command.build_clib.newer_pairwise_group'
+ )
+ def test_build_libraries(self, mock_newer):
+ dist = Distribution()
+ cmd = build_clib(dist)
+
+ # this will be a long section, just making sure all
+ # exceptions are properly raised
+ libs = [('example', {'sources': 'broken.c'})]
+ with pytest.raises(DistutilsSetupError):
+ cmd.build_libraries(libs)
+
+ obj_deps = 'some_string'
+ libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})]
+ with pytest.raises(DistutilsSetupError):
+ cmd.build_libraries(libs)
+
+ obj_deps = {'': ''}
+ libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})]
+ with pytest.raises(DistutilsSetupError):
+ cmd.build_libraries(libs)
+
+ obj_deps = {'source.c': ''}
+ libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})]
+ with pytest.raises(DistutilsSetupError):
+ cmd.build_libraries(libs)
+
+ # with that out of the way, let's see if the crude dependency
+ # system works
+ cmd.compiler = mock.MagicMock(spec=cmd.compiler)
+ mock_newer.return_value = ([],[])
+
+ obj_deps = {'': ('global.h',), 'example.c': ('example.h',)}
+ libs = [('example', {'sources': ['example.c'] ,'obj_deps': obj_deps})]
+
+ cmd.build_libraries(libs)
+ assert [['example.c', 'global.h', 'example.h']] in mock_newer.call_args[0]
+ assert not cmd.compiler.compile.called
+ assert cmd.compiler.create_static_lib.call_count == 1
+
+ # reset the call numbers so we can test again
+ cmd.compiler.reset_mock()
+
+ mock_newer.return_value = '' # anything as long as it's not ([],[])
+ cmd.build_libraries(libs)
+ assert cmd.compiler.compile.call_count == 1
+ assert cmd.compiler.create_static_lib.call_count == 1
diff --git a/setuptools/tests/test_dep_util.py b/setuptools/tests/test_dep_util.py
new file mode 100644
index 00000000..e5027c10
--- /dev/null
+++ b/setuptools/tests/test_dep_util.py
@@ -0,0 +1,30 @@
+from setuptools.dep_util import newer_pairwise_group
+import os
+import pytest
+
+
+@pytest.fixture
+def groups_target(tmpdir):
+ """Sets up some older sources, a target and newer sources.
+ Returns a 3-tuple in this order.
+ """
+ creation_order = ['older.c', 'older.h', 'target.o', 'newer.c', 'newer.h']
+ mtime = 0
+
+ for i in range(len(creation_order)):
+ creation_order[i] = os.path.join(str(tmpdir), creation_order[i])
+ with open(creation_order[i], 'w'):
+ pass
+
+ # make sure modification times are sequential
+ os.utime(creation_order[i], (mtime, mtime))
+ mtime += 1
+
+ return creation_order[:2], creation_order[2], creation_order[3:]
+
+
+def test_newer_pairwise_group(groups_target):
+ older = newer_pairwise_group([groups_target[0]], [groups_target[1]])
+ newer = newer_pairwise_group([groups_target[2]], [groups_target[1]])
+ assert older == ([], [])
+ assert newer == ([groups_target[2]], [groups_target[1]])
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index f5c932da..b75e6ff2 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -14,15 +14,12 @@ import itertools
import distutils.errors
import io
import zipfile
+from unittest import mock
import time
from six.moves import urllib
import pytest
-try:
- from unittest import mock
-except ImportError:
- import mock
from setuptools import sandbox
from setuptools.sandbox import run_setup
diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py
index a0c76ea0..fbeed1d5 100644
--- a/setuptools/tests/test_msvc.py
+++ b/setuptools/tests/test_msvc.py
@@ -5,12 +5,9 @@ Tests for msvc support module.
import os
import contextlib
import distutils.errors
+from unittest import mock
import pytest
-try:
- from unittest import mock
-except ImportError:
- import mock
from . import contexts