Skip to content

Commit fd2ec85

Browse files
committed
Merge branch 'master' into jbrill-msvc-fixes
Manually resolved conflicts in CHANGES.txt and SCons/Tool/MSCommon/vc.py.
2 parents 0d1aaa7 + 120b2cd commit fd2ec85

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1068
-759
lines changed

CHANGES.txt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
3838
module was refactored.
3939
- Add arm64 to the MSVS supported architectures list for VS2017 and later to be
4040
consistent with the current documentation of MSVS_ARCH.
41+
- Fix an issue with an unhandled MissingConfiguration exception due to an msvc
42+
registry query that returns a path that does not exist. Multiple invocation
43+
paths were not prepared to handle the MissingConfiguration exception. The
44+
MissingConfiguration exception type was removed.
4145
- For msvc version specifications without an 'Exp' suffix, an express installation
4246
is used when no other edition is detected for the msvc version.
4347
- VS2015 Express (14.1Exp) may not have been detected. The registry keys written
@@ -110,7 +114,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
110114
If YACC_GRAPH_FILE_SUFFIX is not set, it will respect YACCVCGFILESUFFIX.
111115

112116
From Philipp Maierhöfer:
113-
- Fix gfortran tool initialization. Defaults to using binary named gfortran
117+
- Fix gfortran tool initialization. Defaults to using binary named gfortran
114118
as would be expected, and properly set's SHFORTRAN flags to include -fPIC
115119
where previously it was only doing so for the other fortran versions (F77,..)
116120

@@ -194,6 +198,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
194198
than source). Docs updated. Fixes #4326 and #4327.
195199
- Cleaned up dblite module (checker warnings, etc.).
196200
- Some cleanup in the FortranCommon tool.
201+
- Rewrite the internal _subproc routine - a new scons_subproc_run() now
202+
makes use of Python's subprocess.run in a more natural way, getting
203+
around some of the issues with attempted context manager use, fetching
204+
output, etc. - we let the subprocess module do the hard work,
205+
since it's well debugged and supported. _subproc is no longer
206+
called by internal code, but remains in place in case there are builds
207+
which call to it (even though it was never "published API").
197208
- Changed the message about scons -H to clarify it shows built-in options.
198209
- Improve CacheDir() Documentation.
199210
- Release-building setup tweaked. (most of) the targets listed in
@@ -217,9 +228,17 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
217228
(Enabled/specified by SCONS_CACHE_MSVC_CONFIG).
218229
The existing cache will be discarded if there's a decode error reading it.
219230
It's possible there's a race condition creating this issue in certain CI
220-
builds.
221-
Also add a simple filesystem-based locking protocol to try to
222-
avoide the problem occuring.
231+
builds. Also add a simple filesystem-based locking protocol to try to
232+
avoid the problem occuring.
233+
- Update the first two chapters on building with SCons in the User Guide.
234+
- Update docs on Export/Import - make sure mutable/immutable status has
235+
been mentioned.
236+
- Some cleanup to the Util package, including renaming SCons.Util.types
237+
to SCons.Util.sctypes to avoid any possible confusion with the
238+
Python stdlib types module.
239+
- TeX tests: skip tests that use makeindex or epstopdf not installed, or
240+
if `kpsewhich glossaries.sty` fails.
241+
223242

224243
From Jonathon Reinhart:
225244
- Fix another instance of `int main()` in CheckLib() causing failures

RELEASE.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
8080
The "--warn=missing-sconscript" commandline option is no longer available
8181
as the warning was part of the transitional phase.
8282
- Add missing directories to searched paths for mingw installs
83+
- SCons.Util.types renamed to to SCons.Util.sctypes to avoid any possible
84+
confusion with the Python stdlib "types" module. Note that it was briefly
85+
(for 4.5.x only) possible to import directly from SCons.Util.types,
86+
although the preferred usage remains to import from SCons.Util only.
87+
Any code that did the direct import will have to change to import from
88+
SCons.Util.sctypes.
8389

8490
FIXES
8591
-----
@@ -128,7 +134,7 @@ FIXES
128134
with the python logging module was refactored to prevent test failure.
129135
- MSVS: Add arm64 to the MSVS supported architectures list for VS2017 and later to be
130136
consistent with the current documentation of MSVS_ARCH.
131-
- FORTRAN: Fix gfortran tool initialization. Defaults to using binary named gfortran
137+
- FORTRAN: Fix gfortran tool initialization. Defaults to using binary named gfortran
132138
as would be expected, and properly set's SHFORTRAN flags to include -fPIC
133139
where previously it was only doing so for the other fortran versions (F77,..)
134140
- MSCommon: Added more error handling while reading msvc config cache.
@@ -171,6 +177,9 @@ DOCUMENTATION
171177
usage, which had no problem).
172178
- Changed the message about scons -H to clarify it shows built-in options only.
173179
- Cachedir description updated.
180+
- Updated the first two chapters on building with SCons in the User Guide.
181+
- Clarify that exported variables are shared - if mutable, changes made in
182+
an SConscript are visible everywhere that takes a refereence to that object.
174183

175184
DEVELOPMENT
176185
-----------
@@ -185,6 +194,8 @@ DEVELOPMENT
185194
doesn't use so it can be instantiated by unittests and others.
186195
- Added more type annotations to internal routines.
187196
- Cleaned up dblite module (checker warnings, etc.).
197+
- TeX tests: skip tests that use makeindex or epstopdf not installed, or
198+
if `kpsewhich glossaries.sty` fails.
188199

189200
Thanks to the following contributors listed below for their contributions to this release.
190201
==========================================================================================

SCons/Action.py

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -824,13 +824,120 @@ def _resolve_shell_env(env, target, source):
824824
return ENV
825825

826826

827-
def _subproc(scons_env, cmd, error: str='ignore', **kw):
828-
"""Wrapper for subprocess which pulls from construction env.
827+
def scons_subproc_run(
828+
scons_env, *args, error: str = None, **kwargs
829+
) -> subprocess.CompletedProcess:
830+
"""Run a command with arguments using an SCons execution environment.
831+
832+
Does an underlyng call to :func:`subprocess.run` to run a command and
833+
returns a :class:`subprocess.CompletedProcess` instance with the results.
834+
Use when an external command needs to run in an "SCons context" -
835+
that is, with a crafted execution environment, rather than the user's
836+
existing environment, particularly when you need to collect output
837+
back. Typical case: run a tool's "version" option to find out which
838+
version you have installed.
839+
840+
If supplied, the ``env`` keyword argument passes an
841+
execution environment to process into appropriate form before it is
842+
supplied to :mod:`subprocess`; if omitted, *scons_env* is used to derive
843+
a suitable default. The other keyword arguments are passed through,
844+
except that SCons legacy ``error` keyword is remapped to
845+
the subprocess ``check` keyword. The caller is responsible for
846+
setting up the desired arguments for :func:`subprocess.run`.
847+
848+
This function retains the legacy behavior of returning something
849+
vaguely usable even in the face of complete failure: it synthesizes
850+
a :class:`~subprocess.CompletedProcess` instance in this case.
851+
852+
A subset of interesting keyword arguments follows; see the Python
853+
documentation of :mod:`subprocess` for a complete list.
854+
855+
Keyword Arguments:
856+
stdout: (and *stderr*, *stdin*) if set to :const:`subprocess.PIPE`.
857+
send input to or collect output from the relevant stream in
858+
the subprocess; the default ``None`` does no redirection
859+
(i.e. output or errors may go to the console or log file,
860+
but is not captured); if set to :const:`subprocess.DEVNULL`
861+
they are explicitly thrown away. ``capture_output`` is a
862+
synonym for setting both ``stdout`` and ``stderr``
863+
to :const:`~subprocess.PIPE`.
864+
text: open *stdin*, *stdout*, *stderr* in text mode. Default
865+
is binary mode. ``universal_newlines`` is a synonym -
866+
note ``text`` is not understood before Python 3.7.
867+
encoding: specifies an encoding. Changes to text mode.
868+
errors: specified error handling. Changes to text mode.
869+
input: a byte sequence to be passed to *stdin*, unless text
870+
mode is enabled, in which case it must be a string.
871+
shell: if true, the command is executed through the shell.
872+
check: if true and the subprocess exits with a non-zero exit
873+
code, raise a :exc:`subprocess.CalledProcessError` exception.
874+
Otherwise (the default) in case of an :exc:`OSError`, report the
875+
exit code in the :class:`~Subprocess.CompletedProcess` instance.
876+
877+
.. versionadded:: 4.6
878+
"""
879+
# Figure out the execution environment to use
880+
ENV = kwargs.get('env', None)
881+
if ENV is None:
882+
ENV = get_default_ENV(scons_env)
883+
kwargs['env'] = SCons.Util.sanitize_shell_env(ENV)
884+
885+
# backwards-compat with _subproc: accept 'error', map it to
886+
# ``check`` and remove, since it would not be recognized by run()
887+
check = kwargs.get('check')
888+
if check and error:
889+
raise ValueError('error and check arguments may not both be used.')
890+
if check is None:
891+
check = False # always supply some value, to pacify checkers (pylint).
892+
if error is not None:
893+
if error == 'raise':
894+
check = True
895+
del kwargs['error']
896+
kwargs['check'] = check
897+
898+
# Ensure that the ENV values are all strings:
899+
new_env = {}
900+
for key, value in ENV.items():
901+
if is_List(value):
902+
# If the value is a list, then we assume it is a path list,
903+
# because that's a pretty common list-like value to stick
904+
# in an environment variable:
905+
value = SCons.Util.flatten_sequence(value)
906+
new_env[key] = os.pathsep.join(str(v) for v in value)
907+
else:
908+
# If it's not a list type, "convert" it to str. This is
909+
# harmless if it's already a string, but gets us the right
910+
# thing for Dir and File instances and will produce something
911+
# reasonable for just about everything else:
912+
new_env[key] = str(value)
913+
kwargs['env'] = new_env
914+
915+
# Some tools/tests expect no failures for things like missing files
916+
# unless raise/check is set. If set, let subprocess.run go ahead and
917+
# kill things, else catch and construct a dummy CompletedProcess instance.
918+
if check:
919+
cp = subprocess.run(*args, **kwargs)
920+
else:
921+
try:
922+
cp = subprocess.run(*args, **kwargs)
923+
except OSError as exc:
924+
argline = ' '.join(*args)
925+
cp = subprocess.CompletedProcess(argline, 1)
926+
cp.stdout = ""
927+
cp.stderr = ""
928+
929+
return cp
930+
931+
932+
def _subproc(scons_env, cmd, error='ignore', **kw):
933+
"""Wrapper for subprocess.Popen which pulls from construction env.
829934
830935
Use for calls to subprocess which need to interpolate values from
831936
an SCons construction environment into the environment passed to
832937
subprocess. Adds an an error-handling argument. Adds ability
833938
to specify std{in,out,err} with "'devnull'" tag.
939+
940+
.. deprecated:: 4.6
834941
"""
835942
# TODO: just uses subprocess.DEVNULL now, we can drop the "devnull"
836943
# string now - it is a holdover from Py2, which didn't have DEVNULL.

SCons/ActionTests.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __call__(self) -> None:
4141
import sys
4242
import types
4343
import unittest
44-
import subprocess
44+
from subprocess import PIPE
4545

4646
import SCons.Action
4747
import SCons.Environment
@@ -2327,17 +2327,9 @@ def test_code_contents(self) -> None:
23272327
self.assertEqual(c, expected[sys.version_info[:2]])
23282328

23292329
def test_uncaught_exception_bubbles(self):
2330-
"""Test that _subproc bubbles uncaught exceptions"""
2331-
2330+
"""Test that scons_subproc_run bubbles uncaught exceptions"""
23322331
try:
2333-
pobj = SCons.Action._subproc(
2334-
Environment(),
2335-
None,
2336-
stdin='devnull',
2337-
stderr='devnull',
2338-
stdout=subprocess.PIPE,
2339-
)
2340-
pobj.wait()
2332+
cp = SCons.Action.scons_subproc_run(Environment(), None, stdout=PIPE)
23412333
except EnvironmentError:
23422334
pass
23432335
except Exception:

SCons/Environment.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import re
3737
import shlex
3838
from collections import UserDict, deque
39+
from subprocess import PIPE, DEVNULL
3940

4041
import SCons.Action
4142
import SCons.Builder
@@ -770,29 +771,24 @@ def backtick(self, command) -> str:
770771
Raises:
771772
OSError: if the external command returned non-zero exit status.
772773
"""
773-
774-
import subprocess
775-
776774
# common arguments
777775
kw = {
778-
"stdin": "devnull",
779-
"stdout": subprocess.PIPE,
780-
"stderr": subprocess.PIPE,
776+
"stdin": DEVNULL,
777+
"stdout": PIPE,
778+
"stderr": PIPE,
781779
"universal_newlines": True,
782780
}
783781
# if the command is a list, assume it's been quoted
784782
# othewise force a shell
785783
if not is_List(command):
786784
kw["shell"] = True
787785
# run constructed command
788-
p = SCons.Action._subproc(self, command, **kw)
789-
out, err = p.communicate()
790-
status = p.wait()
791-
if err:
792-
sys.stderr.write("" + err)
793-
if status:
794-
raise OSError("'%s' exited %d" % (command, status))
795-
return out
786+
cp = SCons.Action.scons_subproc_run(self, command, **kw)
787+
if cp.stderr:
788+
sys.stderr.write(cp.stderr)
789+
if cp.returncode:
790+
raise OSError(f'{command!r} exited {cp.returncode}')
791+
return cp.stdout
796792

797793

798794
def AddMethod(self, function, name=None) -> None:

SCons/EnvironmentTests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ def test_backtick(self) -> None:
649649
try:
650650
env.backtick(cmd)
651651
except OSError as e:
652-
assert str(e) == "'%s' exited 1" % cmd, str(e)
652+
assert str(e) == f'{cmd!r} exited 1', str(e)
653653
else:
654654
self.fail("did not catch expected OSError")
655655

SCons/Errors.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"""
2828

2929
import shutil
30-
import SCons.Util
30+
31+
from SCons.Util.sctypes import to_String, is_String
3132

3233
# Note that not all Errors are defined here, some are at the point of use
3334

@@ -77,7 +78,7 @@ def __init__(self,
7778

7879
# py3: errstr should be string and not bytes.
7980

80-
self.errstr = SCons.Util.to_String(errstr)
81+
self.errstr = to_String(errstr)
8182
self.status = status
8283
self.exitstatus = exitstatus
8384
self.filename = filename
@@ -189,7 +190,7 @@ def convert_to_BuildError(status, exc_info=None):
189190
status=2,
190191
exitstatus=2,
191192
exc_info=exc_info)
192-
elif SCons.Util.is_String(status):
193+
elif is_String(status):
193194
buildError = BuildError(
194195
errstr=status,
195196
status=2,

SCons/Node/FS.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ def islink(self, path) -> bool:
12041204

12051205
if hasattr(os, 'readlink'):
12061206

1207-
def readlink(self, file):
1207+
def readlink(self, file) -> str:
12081208
return os.readlink(file)
12091209

12101210
else:

SCons/Platform/aix.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
selection method.
2929
"""
3030

31-
import subprocess
31+
from subprocess import PIPE
3232

3333
from . import posix
3434

@@ -47,22 +47,21 @@ def get_xlc(env, xlc=None, packages=[]):
4747
xlc = xlc[0]
4848
for package in packages:
4949
# find the installed filename, which may be a symlink as well
50-
pipe = SCons.Action._subproc(env, ['lslpp', '-fc', package],
51-
stdin = 'devnull',
52-
stderr = 'devnull',
53-
universal_newlines=True,
54-
stdout = subprocess.PIPE)
50+
cp = SCons.Action.scons_subproc_run(
51+
env, ['lslpp', '-fc', package], universal_newlines=True, stdout=PIPE
52+
)
5553
# output of lslpp is something like this:
5654
# #Path:Fileset:File
5755
# /usr/lib/objrepos:vac.C 6.0.0.0:/usr/vac/exe/xlCcpp
5856
# /usr/lib/objrepos:vac.C 6.0.0.0:/usr/vac/bin/xlc_r -> /usr/vac/bin/xlc
59-
for line in pipe.stdout:
57+
for line in cp.stdout.splitlines():
6058
if xlcPath:
61-
continue # read everything to let lslpp terminate
59+
continue # read everything to let lslpp terminate
6260
fileset, filename = line.split(':')[1:3]
6361
filename = filename.split()[0]
64-
if ('/' in xlc and filename == xlc) \
65-
or ('/' not in xlc and filename.endswith('/' + xlc)):
62+
if ('/' in xlc and filename == xlc) or (
63+
'/' not in xlc and filename.endswith('/' + xlc)
64+
):
6665
xlcVersion = fileset.split()[1]
6766
xlcPath, sep, xlc = filename.rpartition('/')
6867
return (xlcPath, xlc, xlcVersion)

0 commit comments

Comments
 (0)