Skip to content

Commit

Permalink
Merge pull request #153 from zooba/msvccompiler
Browse files Browse the repository at this point in the history
Ensure Windows SDK directories are not cleared when caller specifies include/library dirs
  • Loading branch information
jaraco committed Aug 10, 2022
2 parents 3e4c7a7 + 9f9a3e5 commit b65aa40
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 26 deletions.
21 changes: 13 additions & 8 deletions distutils/_msvccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ def __init__(self, verbose=0, dry_run=0, force=0):
# target platform (.plat_name is consistent with 'bdist')
self.plat_name = None

@classmethod
def _configure(cls, vc_env):
"""
Set class-level include/lib dirs.
"""
cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))

@staticmethod
def _parse_path(val):
return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]

def initialize(self, plat_name=None):
def _repeat_call_error(plat_name=None):
raise AssertionError("repeat initialize not allowed")
Expand All @@ -243,6 +255,7 @@ def _repeat_call_error(plat_name=None):
raise DistutilsPlatformError(
"Unable to find a compatible " "Visual Studio installation."
)
self._configure(vc_env)

self._paths = vc_env.get('path', '')
paths = self._paths.split(os.pathsep)
Expand All @@ -253,14 +266,6 @@ def _repeat_call_error(plat_name=None):
self.mc = _find_exe("mc.exe", paths) # message compiler
self.mt = _find_exe("mt.exe", paths) # message compiler

for dir in vc_env.get('include', '').split(os.pathsep):
if dir:
self.add_include_dir(dir.rstrip(os.sep))

for dir in vc_env.get('lib', '').split(os.pathsep):
if dir:
self.add_library_dir(dir.rstrip(os.sep))

self.preprocess_options = None
# bpo-38597: Always compile with dynamic linking
# Future releases of Python 3.x will include all past
Expand Down
35 changes: 17 additions & 18 deletions distutils/ccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ class CCompiler:
}
language_order = ["c++", "objc", "c"]

include_dirs = []
"""
include dirs specific to this compiler class
"""

library_dirs = []
"""
library dirs specific to this compiler class
"""

def __init__(self, verbose=0, dry_run=0, force=0):
self.dry_run = dry_run
self.force = force
Expand Down Expand Up @@ -324,24 +334,7 @@ def set_link_objects(self, objects):

def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra):
"""Process arguments and decide which source files to compile."""
if outdir is None:
outdir = self.output_dir
elif not isinstance(outdir, str):
raise TypeError("'output_dir' must be a string or None")

if macros is None:
macros = self.macros
elif isinstance(macros, list):
macros = macros + (self.macros or [])
else:
raise TypeError("'macros' (if supplied) must be a list of tuples")

if incdirs is None:
incdirs = self.include_dirs
elif isinstance(incdirs, (list, tuple)):
incdirs = list(incdirs) + (self.include_dirs or [])
else:
raise TypeError("'include_dirs' (if supplied) must be a list of strings")
outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)

if extra is None:
extra = []
Expand Down Expand Up @@ -400,6 +393,9 @@ def _fix_compile_args(self, output_dir, macros, include_dirs):
else:
raise TypeError("'include_dirs' (if supplied) must be a list of strings")

# add include dirs for class
include_dirs += self.__class__.include_dirs

return output_dir, macros, include_dirs

def _prep_compile(self, sources, output_dir, depends=None):
Expand Down Expand Up @@ -456,6 +452,9 @@ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
else:
raise TypeError("'library_dirs' (if supplied) must be a list of strings")

# add library dirs for class
library_dirs += self.__class__.library_dirs

if runtime_library_dirs is None:
runtime_library_dirs = self.runtime_library_dirs
elif isinstance(runtime_library_dirs, (list, tuple)):
Expand Down
55 changes: 55 additions & 0 deletions distutils/tests/test_ccompiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import os
import sys
import platform
import textwrap
import sysconfig

import pytest

from distutils import ccompiler


def _make_strs(paths):
"""
Convert paths to strings for legacy compatibility.
"""
if sys.version_info > (3, 8) and platform.system() != "Windows":
return paths
return list(map(os.fspath, paths))


@pytest.fixture
def c_file(tmp_path):
c_file = tmp_path / 'foo.c'
gen_headers = ('Python.h',)
is_windows = platform.system() == "Windows"
plat_headers = ('windows.h',) * is_windows
all_headers = gen_headers + plat_headers
headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
payload = (
textwrap.dedent(
"""
#headers
void PyInit_foo(void) {}
"""
)
.lstrip()
.replace('#headers', headers)
)
c_file.write_text(payload)
return c_file


def test_set_include_dirs(c_file):
"""
Extensions should build even if set_include_dirs is invoked.
In particular, compiler-specific paths should not be overridden.
"""
compiler = ccompiler.new_compiler()
python = sysconfig.get_paths()['include']
compiler.set_include_dirs([python])
compiler.compile(_make_strs([c_file]))

# do it again, setting include dirs after any initialization
compiler.set_include_dirs([python])
compiler.compile(_make_strs([c_file]))

0 comments on commit b65aa40

Please sign in to comment.