Skip to content

Commit bafae30

Browse files
committed
Merge branch 'master' into jbrill-msvc-detect
Manually resolve conflicts: * RELEASE.txt
2 parents 1dacd31 + 7e64842 commit bafae30

File tree

4 files changed

+61
-41
lines changed

4 files changed

+61
-41
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
7272
- Dump() with json format selected now recognizes additional compound types
7373
(UserDict and UserList), which improves the detail of the display.
7474
json output is also sorted, to match the default display.
75+
- Python 3.13 (alpha) changes the behavior of isabs() on Windows. Adjust
76+
SCons usage of in NodeInfo classes to match. Fixes #4502, #4504.
7577

7678

7779
RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700

RELEASE.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
2929
- Dump() with json format selected now recognizes additional compound types
3030
(UserDict and UserList), which improves the detail of the display.
3131
json output is also sorted, to match the default display.
32+
- Python 3.13 changes the behavior of isabs() on Windows. Adjust SCons
33+
usage of this in NodeInfo classes to avoid test problems.
3234
- MSVC: For msvc version specifications without an 'Exp' suffix, an express
3335
installation is used when no other edition is detected for the msvc version.
3436
This was the behavior for Visual Studio 2008 (9.0) through Visual Studio 2015
@@ -39,7 +41,6 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
3941
added to the default vswhere executable search list after the Chocolatey
4042
installation location.
4143

42-
4344
FIXES
4445
-----
4546

SCons/Node/FS.py

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -120,43 +120,41 @@ def save_strings(val) -> None:
120120
global Save_Strings
121121
Save_Strings = val
122122

123-
#
124-
# Avoid unnecessary function calls by recording a Boolean value that
125-
# tells us whether or not os.path.splitdrive() actually does anything
126-
# on this system, and therefore whether we need to bother calling it
127-
# when looking up path names in various methods below.
128-
#
129123

130124
do_splitdrive = None
131-
_my_splitdrive =None
125+
_my_splitdrive = None
132126

133127
def initialize_do_splitdrive() -> None:
134-
global do_splitdrive
135-
global has_unc
136-
drive, path = os.path.splitdrive('X:/foo')
137-
# splitunc is removed from python 3.7 and newer
138-
# so we can also just test if splitdrive works with UNC
139-
has_unc = (hasattr(os.path, 'splitunc')
140-
or os.path.splitdrive(r'\\split\drive\test')[0] == r'\\split\drive')
141-
142-
do_splitdrive = not not drive or has_unc
143-
144-
global _my_splitdrive
145-
if has_unc:
146-
def splitdrive(p):
128+
"""Set up splitdrive usage.
129+
130+
Avoid unnecessary function calls by recording a flag that tells us whether
131+
or not :func:`os.path.splitdrive` actually does anything on this system,
132+
and therefore whether we need to bother calling it when looking up path
133+
names in various methods below.
134+
135+
If :data:`do_splitdrive` is True, :func:`_my_splitdrive` will be a real
136+
function which we can call. As all supported Python versions' ntpath module
137+
now handle UNC paths correctly, we no longer special-case that.
138+
139+
Deferring the setup of ``_my_splitdrive`` also lets unit tests do
140+
their thing and test UNC path handling on a POSIX host.
141+
"""
142+
global do_splitdrive, _my_splitdrive
143+
144+
do_splitdrive = bool(os.path.splitdrive('X:/foo')[0])
145+
146+
if do_splitdrive:
147+
def _my_splitdrive(p):
147148
if p[1:2] == ':':
148149
return p[:2], p[2:]
149150
if p[0:2] == '//':
150151
# Note that we leave a leading slash in the path
151152
# because UNC paths are always absolute.
152153
return '//', p[1:]
153154
return '', p
154-
else:
155-
def splitdrive(p):
156-
if p[1:2] == ':':
157-
return p[:2], p[2:]
158-
return '', p
159-
_my_splitdrive = splitdrive
155+
# TODO: the os routine should work and be better debugged than ours,
156+
# but unit test test_unc_path fails on POSIX platforms. Resolve someday.
157+
# _my_splitdrive = os.path.splitdrive
160158

161159
# Keep some commonly used values in global variables to skip to
162160
# module look-up costs.
@@ -1238,7 +1236,10 @@ def __init__(self, path = None) -> None:
12381236
self.pathTop = os.getcwd()
12391237
else:
12401238
self.pathTop = path
1241-
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
1239+
if do_splitdrive:
1240+
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
1241+
else:
1242+
self.defaultDrive = ""
12421243

12431244
self.Top = self.Dir(self.pathTop)
12441245
self.Top._path = '.'
@@ -1554,11 +1555,15 @@ class DirNodeInfo(SCons.Node.NodeInfoBase):
15541555
def str_to_node(self, s):
15551556
top = self.fs.Top
15561557
root = top.root
1558+
# Python 3.13/Win changed isabs() - after you split C:/foo/bar,
1559+
# the path part is no longer considerd absolute. Save the passed
1560+
# path for the isabs check so we can get the right answer.
1561+
path = s
15571562
if do_splitdrive:
15581563
drive, s = _my_splitdrive(s)
15591564
if drive:
15601565
root = self.fs.get_root(drive)
1561-
if not os.path.isabs(s):
1566+
if not os.path.isabs(path):
15621567
s = top.get_labspath() + '/' + s
15631568
return root._lookup_abs(s, Entry)
15641569

@@ -2380,7 +2385,7 @@ def __init__(self, drive, fs) -> None:
23802385
# The // entry is necessary because os.path.normpath()
23812386
# preserves double slashes at the beginning of a path on Posix
23822387
# platforms.
2383-
if not has_unc:
2388+
if not do_splitdrive:
23842389
self._lookupDict['//'] = self
23852390

23862391
def _morph(self) -> None:
@@ -2511,11 +2516,15 @@ class FileNodeInfo(SCons.Node.NodeInfoBase):
25112516
def str_to_node(self, s):
25122517
top = self.fs.Top
25132518
root = top.root
2519+
# Python 3.13/Win changed isabs() - after you split C:/foo/bar,
2520+
# the path part is no longer considerd absolute. Save the passed
2521+
# path for the isabs check so we can get the right answer.
2522+
path = s
25142523
if do_splitdrive:
25152524
drive, s = _my_splitdrive(s)
25162525
if drive:
25172526
root = self.fs.get_root(drive)
2518-
if not os.path.isabs(s):
2527+
if not os.path.isabs(path):
25192528
s = top.get_labspath() + '/' + s
25202529
return root._lookup_abs(s, Entry)
25212530

@@ -3534,7 +3543,7 @@ def is_up_to_date(self) -> bool:
35343543
35353544
In all cases self is the target we're checking to see if it's up to date
35363545
"""
3537-
T = 0
3546+
T = False
35383547
if T: Trace('is_up_to_date(%s):' % self)
35393548
if not self.exists():
35403549
if T: Trace(' not self.exists():')
@@ -3730,7 +3739,10 @@ def filedir_lookup(self, p, fd=None):
37303739
if fd is None:
37313740
fd = self.default_filedir
37323741
dir, name = os.path.split(fd)
3733-
drive, d = _my_splitdrive(dir)
3742+
if do_splitdrive:
3743+
drive, d = _my_splitdrive(dir)
3744+
else:
3745+
drive, d = "", dir
37343746
if not name and d[:1] in ('/', OS_SEP):
37353747
#return p.fs.get_root(drive).dir_on_disk(name)
37363748
return p.fs.get_root(drive)

SCons/Node/__init__.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,7 @@ def scan(self) -> None:
10631063
# Don't bother scanning non-derived files, because we don't
10641064
# care what their dependencies are.
10651065
# Don't scan again, if we already have scanned.
1066+
T = False
10661067
if self.implicit is not None:
10671068
return
10681069
self.implicit = []
@@ -1087,7 +1088,12 @@ def scan(self) -> None:
10871088
# essentially short-circuits an N*M scan of the
10881089
# sources for each individual target, which is a hell
10891090
# of a lot more efficient.
1091+
def print_nodelist(n):
1092+
tgts = [f"{t.path!r}" for t in n]
1093+
return f"[{', '.join(tgts)}]"
1094+
10901095
for tgt in executor.get_all_targets():
1096+
if T: Trace(f"adding implicit {print_nodelist(implicit)} to {tgt!s}\n")
10911097
tgt.add_to_implicit(implicit)
10921098

10931099
if implicit_deps_unchanged or self.is_up_to_date():
@@ -1472,8 +1478,8 @@ def changed(self, node=None, allowcache: bool=False):
14721478
14731479
@see: FS.File.changed(), FS.File.release_target_info()
14741480
"""
1475-
t = 0
1476-
if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
1481+
T = False
1482+
if T: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
14771483
if node is None:
14781484
node = self
14791485

@@ -1491,25 +1497,24 @@ def changed(self, node=None, allowcache: bool=False):
14911497
# entries to equal the new dependency list, for the benefit
14921498
# of the loop below that updates node information.
14931499
then.extend([None] * diff)
1494-
if t: Trace(': old %s new %s' % (len(then), len(children)))
1500+
if T: Trace(': old %s new %s' % (len(then), len(children)))
14951501
result = True
14961502

14971503
for child, prev_ni in zip(children, then):
14981504
if _decider_map[child.changed_since_last_build](child, self, prev_ni, node):
1499-
if t: Trace(': %s changed' % child)
1505+
if T: Trace(f": '{child!s}' changed")
15001506
result = True
15011507

15021508
if self.has_builder():
15031509
contents = self.get_executor().get_contents()
15041510
newsig = hash_signature(contents)
15051511
if bi.bactsig != newsig:
1506-
if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig))
1512+
if T: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig))
15071513
result = True
15081514

15091515
if not result:
1510-
if t: Trace(': up to date')
1511-
1512-
if t: Trace('\n')
1516+
if T: Trace(': up to date')
1517+
if T: Trace('\n')
15131518

15141519
return result
15151520

0 commit comments

Comments
 (0)