Skip to content

Commit 5a45d4f

Browse files
committed
Make compiler.include_dirs expose all paths correct again for MSVC
including the SDK dirs after compiler.initialize() Changes in pypa#153 broke compatibility (pywin32), used shadowed class variables in an odd manner and conceiled the MSVC dirs from public access.
1 parent c802880 commit 5a45d4f

4 files changed

Lines changed: 44 additions & 39 deletions

File tree

distutils/_msvccompiler.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,6 @@ def __init__(self, verbose=0, dry_run=0, force=0):
224224
self.plat_name = None
225225
self.initialized = False
226226

227-
@classmethod
228-
def _configure(cls, vc_env):
229-
"""
230-
Set class-level include/lib dirs.
231-
"""
232-
cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
233-
cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
234-
235227
@staticmethod
236228
def _parse_path(val):
237229
return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
@@ -250,12 +242,14 @@ def initialize(self, plat_name=None):
250242
# Get the vcvarsall.bat spec for the requested platform.
251243
plat_spec = PLAT_TO_VCVARS[plat_name]
252244

245+
# Add include/lib dirs from vcvarsall.bat
253246
vc_env = _get_vc_env(plat_spec)
254247
if not vc_env:
255248
raise DistutilsPlatformError(
256249
"Unable to find a compatible " "Visual Studio installation."
257250
)
258-
self._configure(vc_env)
251+
self.include_dirs.extend(self._parse_path(vc_env.get('include', '')))
252+
self.library_dirs.extend(self._parse_path(vc_env.get('lib', '')))
259253

260254
self._paths = vc_env.get('path', '')
261255
paths = self._paths.split(os.pathsep)

distutils/ccompiler.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,6 @@ class CCompiler:
9393
}
9494
language_order = ["c++", "objc", "c"]
9595

96-
include_dirs = []
97-
"""
98-
include dirs specific to this compiler class
99-
"""
100-
101-
library_dirs = []
102-
"""
103-
library dirs specific to this compiler class
104-
"""
105-
10696
def __init__(self, verbose=0, dry_run=0, force=0):
10797
self.dry_run = dry_run
10898
self.force = force
@@ -395,9 +385,6 @@ def _fix_compile_args(self, output_dir, macros, include_dirs):
395385
else:
396386
raise TypeError("'include_dirs' (if supplied) must be a list of strings")
397387

398-
# add include dirs for class
399-
include_dirs += self.__class__.include_dirs
400-
401388
return output_dir, macros, include_dirs
402389

403390
def _prep_compile(self, sources, output_dir, depends=None):
@@ -454,9 +441,6 @@ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
454441
else:
455442
raise TypeError("'library_dirs' (if supplied) must be a list of strings")
456443

457-
# add library dirs for class
458-
library_dirs += self.__class__.library_dirs
459-
460444
if runtime_library_dirs is None:
461445
runtime_library_dirs = self.runtime_library_dirs
462446
elif isinstance(runtime_library_dirs, (list, tuple)):

distutils/command/build_ext.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,11 +314,6 @@ def run(self): # noqa: C901
314314
force=self.force,
315315
)
316316
customize_compiler(self.compiler)
317-
# If we are cross-compiling, init the compiler now (if we are not
318-
# cross-compiling, init would not hurt, but people may rely on
319-
# late initialization of compiler even if they shouldn't...)
320-
if os.name == 'nt' and self.plat_name != get_platform():
321-
self.compiler.initialize(self.plat_name)
322317

323318
# And make sure that any compile/link-related options (which might
324319
# come from the command-line or from the setup script) are set in
@@ -342,6 +337,12 @@ def run(self): # noqa: C901
342337
if self.link_objects is not None:
343338
self.compiler.set_link_objects(self.link_objects)
344339

340+
# If we are cross-compiling, init the compiler now (if we are not
341+
# cross-compiling, init would not hurt, but people may rely on
342+
# late initialization of compiler even if they shouldn't...)
343+
if os.name == 'nt' and self.plat_name != get_platform():
344+
self.compiler.initialize(self.plat_name)
345+
345346
# Now actually compile and link everything.
346347
self.build_extensions()
347348

distutils/tests/test_ccompiler.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
import os
2+
import io
23
import sys
34
import platform
45
import textwrap
56
import sysconfig
7+
import tempfile
8+
from test import support
69

710
import pytest
811

912
from distutils import ccompiler
13+
from distutils.core import Distribution
14+
from distutils.extension import Extension
15+
from distutils.command.build_ext import build_ext
16+
17+
18+
is_windows = platform.system() == "Windows"
1019

1120

1221
def _make_strs(paths):
@@ -22,8 +31,7 @@ def _make_strs(paths):
2231
def c_file(tmp_path):
2332
c_file = tmp_path / 'foo.c'
2433
gen_headers = ('Python.h',)
25-
is_windows = platform.system() == "Windows"
26-
plat_headers = ('windows.h',) * is_windows
34+
plat_headers = ('stdlib.h',) + ('Windows.h',) * is_windows
2735
all_headers = gen_headers + plat_headers
2836
headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
2937
payload = (
@@ -40,16 +48,34 @@ def c_file(tmp_path):
4048
return c_file
4149

4250

43-
def test_set_include_dirs(c_file):
51+
def test_include_dirs(c_file):
4452
"""
45-
Extensions should build even if set_include_dirs is invoked.
46-
In particular, compiler-specific paths should not be overridden.
53+
Test basic standard include dirs to be available, including SDK on Windows.
4754
"""
4855
compiler = ccompiler.new_compiler()
4956
python = sysconfig.get_paths()['include']
50-
compiler.set_include_dirs([python])
57+
compiler.add_include_dir(python)
5158
compiler.compile(_make_strs([c_file]))
5259

53-
# do it again, setting include dirs after any initialization
54-
compiler.set_include_dirs([python])
55-
compiler.compile(_make_strs([c_file]))
60+
61+
@pytest.mark.skipif(not is_windows, reason="Test only on Windows")
62+
def test_ext_include_dirs(c_file):
63+
"""
64+
Test that include_dirs in an extension does not replace standard include dirs.
65+
"""
66+
ext = Extension('foo', _make_strs([c_file]), include_dirs=['_nonexisting_'])
67+
dist = Distribution({'name': 'foo', 'ext_modules': [ext]})
68+
cmd = build_ext(dist)
69+
tmp_dir = tempfile.mkdtemp()
70+
cmd.build_lib = tmp_dir
71+
cmd.build_temp = tmp_dir
72+
73+
old_stdout = sys.stdout
74+
if not support.verbose:
75+
# silence compiler output
76+
sys.stdout = io.StringIO()
77+
try:
78+
cmd.ensure_finalized()
79+
cmd.run()
80+
finally:
81+
sys.stdout = old_stdout

0 commit comments

Comments
 (0)