Skip to content

Commit 680b984

Browse files
authored
Merge branch 'master' into test/flagstests
2 parents 414f920 + 93033b3 commit 680b984

File tree

123 files changed

+391
-323
lines changed

Some content is hidden

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

123 files changed

+391
-323
lines changed

CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
2424
From Mats Wichmann:
2525
- Clean up C and C++ FLAGS tests. Tests which use a real compiler
2626
are now more clearly distinguished (-live.py suffix and docstring).
27+
- runtest.py once again finds "external" tests, such as the tests for
28+
tools in scons-contrib. An earlier rework had broken this. Fixes #4699.
2729

2830

2931
RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700
@@ -33,6 +35,9 @@ RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700
3335
- Fixed a hang in `wait_for_process_to_die()` on Windows, affecting
3436
clean-up of the SCons daemon used for Ninja builds.
3537

38+
From Thaddeus Crews:
39+
- Nodes are now treated as PathLike objects.
40+
3641
From Mats Wichmann:
3742
- Fix typos in CCFLAGS test. Didn't affect the test itself, but
3843
didn't correctly apply the DefaultEnvironment speedup.

RELEASE.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
2929
- List modifications to existing features, where the previous behavior
3030
wouldn't actually be considered a bug
3131

32+
- Nodes are now treated as PathLike objects.
33+
3234
FIXES
3335
-----
3436

@@ -57,7 +59,8 @@ DOCUMENTATION
5759
DEVELOPMENT
5860
-----------
5961

60-
- List visible changes in the way SCons is developed
62+
- runtest.py once again finds "external" tests, such as the tests for
63+
tools in scons-contrib. An earlier rework had broken this. Fixes #4699.
6164

6265
Thanks to the following contributors listed below for their contributions to this release.
6366
==========================================================================================

SCons/Builder.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
from SCons.Debug import logInstanceCreation
115115
from SCons.Errors import InternalError, UserError
116116
from SCons.Executor import Executor
117+
from SCons.Node import Node
117118

118119
class _Null:
119120
pass
@@ -487,10 +488,11 @@ def _adjustixes(self, files, pre, suf, ensure_suffix: bool=False):
487488
# fspath() is to catch PathLike paths. We avoid the simpler
488489
# str(f) so as not to "lose" files that are already Nodes:
489490
# TypeError: expected str, bytes or os.PathLike object, not File
490-
with suppress(TypeError):
491-
f = os.fspath(f)
492-
if SCons.Util.is_String(f):
493-
f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
491+
if not isinstance(f, Node):
492+
with suppress(TypeError):
493+
f = os.fspath(f)
494+
if SCons.Util.is_String(f):
495+
f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
494496
result.append(f)
495497
return result
496498

SCons/BuilderTests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ def test_single_source(self) -> None:
702702
"""Test Builder with single_source flag set"""
703703
def func(target, source, env) -> None:
704704
"""create the file"""
705-
with open(str(target[0]), "w"):
705+
with open(target[0], "w"):
706706
pass
707707
if len(source) == 1 and len(target) == 1:
708708
env['CNT'][0] = env['CNT'][0] + 1
@@ -759,7 +759,7 @@ def test_lists(self) -> None:
759759
"""Testing handling lists of targets and source"""
760760
def function2(target, source, env, tlist = [outfile, outfile2], **kw) -> int:
761761
for t in target:
762-
with open(str(t), 'w') as f:
762+
with open(t, 'w') as f:
763763
f.write("function2\n")
764764
for t in tlist:
765765
if t not in list(map(str, target)):
@@ -790,7 +790,7 @@ def function2(target, source, env, tlist = [outfile, outfile2], **kw) -> int:
790790

791791
def function3(target, source, env, tlist = [sub1_out, sub2_out]) -> int:
792792
for t in target:
793-
with open(str(t), 'w') as f:
793+
with open(t, 'w') as f:
794794
f.write("function3\n")
795795
for t in tlist:
796796
if t not in list(map(str, target)):

SCons/Environment.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ env.Command(
12481248

12491249
import os
12501250
def rename(env, target, source):
1251-
os.rename('.tmp', str(target[0]))
1251+
os.rename('.tmp', target[0])
12521252

12531253

12541254
env.Command(
@@ -3626,7 +3626,7 @@ def create(target, source, env):
36263626

36273627
Writes 'prefix=$SOURCE' into the file name given as $TARGET.
36283628
"""
3629-
with open(str(target[0]), 'wb') as f:
3629+
with open(target[0], 'wb') as f:
36303630
f.write(b'prefix=' + source[0].get_contents() + b'\n')
36313631

36323632
# Fetch the prefix= argument, if any, from the command line.

SCons/Node/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,9 @@ def __init__(self) -> None:
622622
# what line in what file created the node, for example).
623623
Annotate(self)
624624

625+
def __fspath__(self) -> str:
626+
return str(self)
627+
625628
def disambiguate(self, must_exist: bool = False):
626629
return self
627630

SCons/SConfTests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def test_TryAction(self) -> None:
295295
"""Test SConf.TryAction
296296
"""
297297
def actionOK(target, source, env):
298-
with open(str(target[0]), "w") as f:
298+
with open(target[0], "w") as f:
299299
f.write("RUN OK\n")
300300
return None
301301
def actionFAIL(target, source, env) -> int:

bin/SConsExamples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ def process(source_file, ofp):
547547
elif line[:11] != "STRIP CCCOM":
548548
ofp.write(line)
549549
550-
with open(str(target[0]), "w") as fp:
550+
with open(target[0], "w") as fp:
551551
for src in map(str, source):
552552
process(src, fp)
553553
fp.write('debug = ' + ARGUMENTS.get('debug', '0') + '\\n')

runtest.py

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
from io import StringIO
3030
from pathlib import Path, PurePath, PureWindowsPath
3131
from queue import Queue
32-
from typing import TextIO
3332

3433
cwd = os.getcwd()
3534
debug: str | None = None
@@ -575,38 +574,34 @@ def footer(self, f):
575574
del os.environ['_JAVA_OPTIONS']
576575

577576

578-
# ---[ test discovery ]------------------------------------
579-
# This section figures out which tests to run.
577+
# ---[ Test Discovery ]------------------------------------
578+
# This section determines which tests to run based on three
579+
# mutually exclusive options:
580+
# 1. Reading test paths from a testlist file (--file or --retry option)
581+
# 2. Using test paths given as command line arguments
582+
# 3. Automatically finding all tests (--all option)
580583
#
581-
# The initial testlist is made by reading from the testlistfile,
582-
# if supplied, or by looking at the test arguments, if supplied,
583-
# or by looking for all test files if the "all" argument is supplied.
584-
# One of the three is required.
584+
# Test paths can specify either individual test files, or directories to
585+
# scan for tests. The following test types are recognized:
585586
#
586-
# Each test path, whichever of the three sources it comes from,
587-
# specifies either a test file or a directory to search for
588-
# SCons tests. SCons code layout assumes that any file under the 'SCons'
589-
# subdirectory that ends with 'Tests.py' is a unit test, and any Python
590-
# script (*.py) under the 'test' subdirectory is an end-to-end test.
591-
# We need to track these because they are invoked differently.
592-
# find_unit_tests and find_e2e_tests are used for this searching.
587+
# - Unit tests: Files ending in 'Tests.py' under the 'SCons' directory
588+
# - End-to-end tests: Python scripts (*.py) under the 'test' directory
589+
# - External tests: End-to-end tests in paths containing a 'test'
590+
# component (not expected to be local)
593591
#
594-
# Note that there are some tests under 'SCons' that *begin* with
595-
# 'test_', but they're packaging and installation tests, not
596-
# functional tests, so we don't execute them by default. (They can
597-
# still be executed by hand, though).
592+
# find_unit_tests() and find_e2e_tests() perform the directory scanning.
598593
#
599-
# Test exclusions, if specified, are then applied.
600-
594+
# After the initial test list is built, any test exclusions specified via
595+
# --exclude-list are applied to produce the final test set.
601596

602597
def scanlist(testfile):
603598
""" Process a testlist file """
604599
data = StringIO(testfile.read_text())
605600
tests = [t.strip() for t in data.readlines() if not t.startswith('#')]
606601
# in order to allow scanned lists to work whether they use forward or
607-
# backward slashes, first create the object as a PureWindowsPath which
608-
# accepts either, then use that to make a Path object to use for
609-
# comparisons like "file in scanned_list".
602+
# backward slashes, on non-Windows first create the object as a
603+
# PureWindowsPath which accepts either, then use that to make a Path
604+
# object for use in comparisons like "if file in scanned_list".
610605
if sys.platform == 'win32':
611606
return [Path(t) for t in tests if t]
612607
else:
@@ -635,7 +630,7 @@ def find_e2e_tests(directory):
635630
if 'sconstest.skip' in filenames:
636631
continue
637632

638-
# Slurp in any tests in exclude lists
633+
# Gather up the data from any exclude lists
639634
excludes = []
640635
if ".exclude_tests" in filenames:
641636
excludefile = Path(dirpath, ".exclude_tests").resolve()
@@ -648,8 +643,7 @@ def find_e2e_tests(directory):
648643
return sorted(result)
649644

650645

651-
# initial selection:
652-
# if we have a testlist file read that, else hunt for tests.
646+
# Initial test selection:
653647
unittests = []
654648
endtests = []
655649
if args.testlistfile:
@@ -668,15 +662,16 @@ def find_e2e_tests(directory):
668662
# Clean up path removing leading ./ or .\
669663
name = str(path)
670664
if name.startswith('.') and name[1] in (os.sep, os.altsep):
671-
path = path.with_name(tn[2:])
665+
path = path.with_name(name[2:])
672666

673667
if path.exists():
674668
if path.is_dir():
675669
if path.parts[0] == "SCons" or path.parts[0] == "testing":
676670
unittests.extend(find_unit_tests(path))
677671
elif path.parts[0] == 'test':
678672
endtests.extend(find_e2e_tests(path))
679-
# else: TODO: what if user pointed to a dir outside scons tree?
673+
elif args.external and 'test' in path.parts:
674+
endtests.extend(find_e2e_tests(path))
680675
else:
681676
if path.match("*Tests.py"):
682677
unittests.append(path)
@@ -703,7 +698,7 @@ def find_e2e_tests(directory):
703698
""")
704699
sys.exit(1)
705700

706-
# ---[ test processing ]-----------------------------------
701+
# ---[ Test Processing ]-----------------------------------
707702
tests = [Test(t) for t in tests]
708703

709704
if args.list_only:
@@ -826,10 +821,11 @@ def run_test(t, io_lock=None, run_async=True):
826821

827822

828823
class RunTest(threading.Thread):
829-
""" Test Runner class.
824+
"""Test Runner thread.
830825
831-
One instance will be created for each job thread in multi-job mode
826+
One will be created for each job in multi-job mode
832827
"""
828+
833829
def __init__(self, queue=None, io_lock=None, group=None, target=None, name=None):
834830
super().__init__(group=group, target=target, name=name)
835831
self.queue = queue

test/Actions/append.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@
4545
env=Environment()
4646
4747
def before(env, target, source):
48-
with open(str(target[0]), "wb") as f:
48+
with open(target[0], "wb") as f:
4949
f.write(b"Foo\\n")
5050
with open("before.txt", "wb") as f:
5151
f.write(b"Bar\\n")
5252
5353
def after(env, target, source):
54-
with open(str(target[0]), "rb") as fin, open("after%s", "wb") as fout:
54+
with open(target[0], "rb") as fin, open("after%s", "wb") as fout:
5555
fout.write(fin.read())
5656
5757
env.Prepend(LINKCOM=Action(before))

0 commit comments

Comments
 (0)